From 4658e7c3a3049cc05a6389021ccae67ef3dbc3bc Mon Sep 17 00:00:00 2001 From: ES Date: Fri, 20 Sep 2024 18:08:36 +0530 Subject: [PATCH 1/6] Initial code for cleaning and validating generated code --- sqlc-gen/Cargo.toml | 2 + sqlc-gen/Makefile | 2 +- sqlc-gen/examples/authors/postgresql/gen.rs | 90 ++--- sqlc-gen/examples/authors/sqlc.yaml | 2 +- sqlc-gen/examples/batch/postgresql/gen.rs | 40 +- sqlc-gen/examples/batch/sqlc.json | 2 +- sqlc-gen/examples/booktest/gen/gen.rs | 302 +++++---------- sqlc-gen/examples/booktest/sqlc.yaml | 2 +- sqlc-gen/examples/jets/postgresql/gen.rs | 64 ++-- sqlc-gen/examples/jets/sqlc.json | 2 +- sqlc-gen/examples/ondeck/postgresql/gen.rs | 294 +++++---------- sqlc-gen/examples/ondeck/sqlc.json | 2 +- sqlc-gen/src/codegen/mod.rs | 388 +++++++++++++------- sqlc-gen/src/codegen/type_const.rs | 14 +- sqlc-gen/src/codegen/type_enum.rs | 42 ++- sqlc-gen/src/codegen/type_method.rs | 112 ------ sqlc-gen/src/codegen/type_query.rs | 189 ++++++++++ sqlc-gen/src/codegen/type_struct.rs | 118 ++---- 18 files changed, 798 insertions(+), 869 deletions(-) delete mode 100644 sqlc-gen/src/codegen/type_method.rs create mode 100644 sqlc-gen/src/codegen/type_query.rs diff --git a/sqlc-gen/Cargo.toml b/sqlc-gen/Cargo.toml index 9a39773..7b9d172 100644 --- a/sqlc-gen/Cargo.toml +++ b/sqlc-gen/Cargo.toml @@ -21,6 +21,8 @@ postgres-types = "0.2.7" strum = "0.26.3" strum_macros = "0.26.4" itertools = "0.13.0" +pluralizer = "0.4.0" +check_keyword = "0.3.1" [build-dependencies] prost-build = "0.9.0" diff --git a/sqlc-gen/Makefile b/sqlc-gen/Makefile index c810c9a..e16ba10 100644 --- a/sqlc-gen/Makefile +++ b/sqlc-gen/Makefile @@ -15,7 +15,7 @@ generate-for-example: file_with_ext=$$(ls sqlc.*) && \ file_ext=$${file_with_ext##*.} && \ ls sqlc.* | yq -iP ".plugins[0].wasm.sha256=\"$$sha_256\", .plugins[0].wasm.url=\"file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm\"" $$file_with_ext -o $$file_ext && \ - sqlc generate && \ + RUST_LOG=debug sqlc generate && \ cd - generate: diff --git a/sqlc-gen/examples/authors/postgresql/gen.rs b/sqlc-gen/examples/authors/postgresql/gen.rs index 99930ff..007725a 100644 --- a/sqlc-gen/examples/authors/postgresql/gen.rs +++ b/sqlc-gen/examples/authors/postgresql/gen.rs @@ -5,26 +5,10 @@ const GET_AUTHOR: &str = r#" SELECT id, name, bio FROM authors WHERE id = $1 LIMIT 1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorParams { - pub id: i64, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorRow { - pub id: i64, - pub name: String, - pub bio: Option, -} const LIST_AUTHORS: &str = r#" SELECT id, name, bio FROM authors ORDER BY name "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct ListAuthorsRow { - pub id: i64, - pub name: String, - pub bio: Option, -} const CREATE_AUTHOR: &str = r#" INSERT INTO authors ( name, bio @@ -33,59 +17,39 @@ INSERT INTO authors ( ) RETURNING id, name, bio "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorParams { - pub name: String, - pub bio: Option, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorRow { - pub id: i64, - pub name: String, - pub bio: Option, -} const DELETE_AUTHOR: &str = r#" DELETE FROM authors WHERE id = $1 "#; #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct DeleteAuthorParams { +pub(crate) struct Author { pub id: i64, + pub name: String, + pub bio: Option, } -pub struct Queries { - client: postgres::Client, -} -impl Queries { - pub fn new(client: postgres::Client) -> Self { - Self { client } - } - pub(crate) fn get_author( - &mut self, - params: GetAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[¶ms.id])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn list_authors(&mut self) -> anyhow::Result> { - let rows = self.client.query(LIST_AUTHORS, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) - } - pub(crate) fn create_author( - &mut self, - params: CreateAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[¶ms.name, ¶ms.bio])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn delete_author( - &mut self, - params: DeleteAuthorParams, - ) -> anyhow::Result<()> { - self.client.execute(DELETE_AUTHOR, &[¶ms.id])?; - Ok(()) +pub(crate) fn create_author( + &mut self, + arg: CreateAuthorParams, +) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[&"arg".name, &"arg".bio])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn delete_author(&mut self, id: i64) -> anyhow::Result<()> { + self.client.execute(DELETE_AUTHOR, &[&"id"])?; + Ok(()) +} +pub(crate) fn get_author(&mut self, id: i64) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&"id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn list_authors( + &mut self, + arg: ListAuthorsParams, +) -> anyhow::Result> { + let rows = self.client.query(LIST_AUTHORS, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } + Ok(result) } diff --git a/sqlc-gen/examples/authors/sqlc.yaml b/sqlc-gen/examples/authors/sqlc.yaml index 5d0d8bc..6d6248a 100644 --- a/sqlc-gen/examples/authors/sqlc.yaml +++ b/sqlc-gen/examples/authors/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 91320118cfc27080bdd42a70be43198d3430fd774aa2ea3920ce1f8b55d444c4 + sha256: 148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4 # # - name: js # process: diff --git a/sqlc-gen/examples/batch/postgresql/gen.rs b/sqlc-gen/examples/batch/postgresql/gen.rs index d40180a..e229a79 100644 --- a/sqlc-gen/examples/batch/postgresql/gen.rs +++ b/sqlc-gen/examples/batch/postgresql/gen.rs @@ -1,38 +1,34 @@ /// @generated by the sqlc-gen-rust on sqlc-generate using sqlc.yaml /// DO NOT EDIT. use postgres::{Error, Row}; -#[derive(Debug, Display)] -pub enum BookType { - Fiction, - Nonfiction, -} const GET_AUTHOR: &str = r#" select author_id, name, biography from authors where author_id = $1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorParams { - pub author_id: i32, +#[derive(Debug, Display)] +pub enum BookType { + Fiction, + Nonfiction, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorRow { +pub(crate) struct Author { pub author_id: i32, pub name: String, pub biography: Option, } -pub struct Queries { - client: postgres::Client, +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct Book { + pub book_id: i32, + pub author_id: i32, + pub isbn: String, + pub book_type: String, + pub title: String, + pub year: i32, + pub available: String, + pub tags: Vec, } -impl Queries { - pub fn new(client: postgres::Client) -> Self { - Self { client } - } - pub(crate) fn get_author( - &mut self, - params: GetAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[¶ms.author_id])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } +pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } diff --git a/sqlc-gen/examples/batch/sqlc.json b/sqlc-gen/examples/batch/sqlc.json index ce29e78..56cdd82 100644 --- a/sqlc-gen/examples/batch/sqlc.json +++ b/sqlc-gen/examples/batch/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "91320118cfc27080bdd42a70be43198d3430fd774aa2ea3920ce1f8b55d444c4" + "sha256": "148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4" } } ], diff --git a/sqlc-gen/examples/booktest/gen/gen.rs b/sqlc-gen/examples/booktest/gen/gen.rs index ac615ea..e4777bf 100644 --- a/sqlc-gen/examples/booktest/gen/gen.rs +++ b/sqlc-gen/examples/booktest/gen/gen.rs @@ -1,105 +1,35 @@ /// @generated by the sqlc-gen-rust on sqlc-generate using sqlc.yaml /// DO NOT EDIT. use postgres::{Error, Row}; -#[derive(Debug, Display)] -pub enum BookType { - Fiction, - Nonfiction, -} const GET_AUTHOR: &str = r#" select author_id, name from authors where author_id = $1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorParams { - pub author_id: i32, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorRow { - pub author_id: i32, - pub name: String, -} const GET_BOOK: &str = r#" select book_id, author_id, isbn, book_type, title, year, available, tags from books where book_id = $1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetBookParams { - pub book_id: i32, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetBookRow { - pub book_id: i32, - pub author_id: i32, - pub isbn: String, - pub book_type: String, - pub title: String, - pub year: i32, - pub available: String, - pub tags: Vec, -} const DELETE_BOOK: &str = r#" delete from books where book_id = $1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct DeleteBookParams { - pub book_id: i32, -} const BOOKS_BY_TITLE_YEAR: &str = r#" select book_id, author_id, isbn, book_type, title, year, available, tags from books where title = $1 and year = $2 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct BooksByTitleYearParams { - pub title: String, - pub year: i32, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct BooksByTitleYearRow { - pub book_id: i32, - pub author_id: i32, - pub isbn: String, - pub book_type: String, - pub title: String, - pub year: i32, - pub available: String, - pub tags: Vec, -} const BOOKS_BY_TAGS: &str = r#" select book_id, title, name, isbn, tags from books left join authors on books.author_id = authors.author_id where tags && $1::varchar[] "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct BooksByTagsParams { - pub _1: Vec, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct BooksByTagsRow { - pub book_id: i32, - pub title: String, - pub name: Option, - pub isbn: String, - pub tags: Vec, -} const CREATE_AUTHOR: &str = r#" INSERT INTO authors (name) VALUES ($1) RETURNING author_id, name "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorParams { - pub name: String, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorRow { - pub author_id: i32, - pub name: String, -} const CREATE_BOOK: &str = r#" INSERT INTO books ( author_id, @@ -120,165 +50,115 @@ INSERT INTO books ( ) RETURNING book_id, author_id, isbn, book_type, title, year, available, tags "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateBookParams { - pub author_id: i32, - pub isbn: String, - pub book_type: String, - pub title: String, - pub year: i32, - pub available: String, - pub tags: Vec, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateBookRow { - pub book_id: i32, - pub author_id: i32, - pub isbn: String, - pub book_type: String, - pub title: String, - pub year: i32, - pub available: String, - pub tags: Vec, -} const UPDATE_BOOK: &str = r#" UPDATE books SET title = $1, tags = $2 WHERE book_id = $3 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateBookParams { - pub title: String, - pub tags: Vec, - pub book_id: i32, -} const UPDATE_BOOK_ISBN: &str = r#" UPDATE books SET title = $1, tags = $2, isbn = $4 WHERE book_id = $3 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateBookIsbnParams { - pub title: String, - pub tags: Vec, - pub book_id: i32, - pub isbn: String, -} const SAY_HELLO: &str = r#" select say_hello from say_hello($1) "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct SayHelloParams { - pub s: String, +#[derive(Debug, Display)] +pub enum BookType { + Fiction, + Nonfiction, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct SayHelloRow { - pub say_hello: Option, +pub(crate) struct Author { + pub author_id: i32, + pub name: String, } -pub struct Queries { - client: postgres::Client, +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct Book { + pub book_id: i32, + pub author_id: i32, + pub isbn: String, + pub book_type: String, + pub title: String, + pub year: i32, + pub available: String, + pub tags: Vec, } -impl Queries { - pub fn new(client: postgres::Client) -> Self { - Self { client } - } - pub(crate) fn get_author( - &mut self, - params: GetAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[¶ms.author_id])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn get_book( - &mut self, - params: GetBookParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_BOOK, &[¶ms.book_id])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn delete_book( - &mut self, - params: DeleteBookParams, - ) -> anyhow::Result<()> { - self.client.execute(DELETE_BOOK, &[¶ms.book_id])?; - Ok(()) - } - pub(crate) fn books_by_title_year( - &mut self, - params: BooksByTitleYearParams, - ) -> anyhow::Result> { - let rows = self - .client - .query(BOOKS_BY_TITLE_YEAR, &[¶ms.title, ¶ms.year])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) - } - pub(crate) fn books_by_tags( - &mut self, - params: BooksByTagsParams, - ) -> anyhow::Result> { - let rows = self.client.query(BOOKS_BY_TAGS, &[¶ms._1])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) - } - pub(crate) fn create_author( - &mut self, - params: CreateAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[¶ms.name])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn create_book( - &mut self, - params: CreateBookParams, - ) -> anyhow::Result { - let row = self - .client - .query_one( - CREATE_BOOK, - &[ - ¶ms.author_id, - ¶ms.isbn, - ¶ms.book_type, - ¶ms.title, - ¶ms.year, - ¶ms.available, - ¶ms.tags, - ], - )?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn update_book( - &mut self, - params: UpdateBookParams, - ) -> anyhow::Result<()> { - self.client - .execute(UPDATE_BOOK, &[¶ms.title, ¶ms.tags, ¶ms.book_id])?; - Ok(()) - } - pub(crate) fn update_book_isbn( - &mut self, - params: UpdateBookIsbnParams, - ) -> anyhow::Result<()> { - self.client - .execute( - UPDATE_BOOK_ISBN, - &[¶ms.title, ¶ms.tags, ¶ms.book_id, ¶ms.isbn], - )?; - Ok(()) +pub(crate) fn books_by_tags( + &mut self, + dollar_1: String, +) -> anyhow::Result> { + let rows = self.client.query(BOOKS_BY_TAGS, &[&"dollar_1"])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } - pub(crate) fn say_hello( - &mut self, - params: SayHelloParams, - ) -> anyhow::Result { - let row = self.client.query_one(SAY_HELLO, &[¶ms.s])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + Ok(result) +} +pub(crate) fn books_by_title_year( + &mut self, + arg: BooksByTitleYearParams, +) -> anyhow::Result> { + let rows = self.client.query(BOOKS_BY_TITLE_YEAR, &[&"arg".title, &"arg".year])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } + Ok(result) +} +pub(crate) fn create_author(&mut self, name: String) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[&"name"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn create_book( + &mut self, + arg: CreateBookParams, +) -> anyhow::Result { + let row = self + .client + .query_one( + CREATE_BOOK, + &[ + &"arg".author_id, + &"arg".isbn, + &"arg".book_type, + &"arg".title, + &"arg".year, + &"arg".available, + &"arg".tags, + ], + )?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn delete_book(&mut self, book_id: i32) -> anyhow::Result<()> { + self.client.execute(DELETE_BOOK, &[&"book_id"])?; + Ok(()) +} +pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn get_book(&mut self, book_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_BOOK, &[&"book_id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn say_hello(&mut self, s: String) -> anyhow::Result { + let row = self.client.query_one(SAY_HELLO, &[&"s"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn update_book(&mut self, arg: UpdateBookParams) -> anyhow::Result<()> { + self.client.execute(UPDATE_BOOK, &[&"arg".title, &"arg".tags, &"arg".book_id])?; + Ok(()) +} +pub(crate) fn update_book_isbn( + &mut self, + arg: UpdateBookIsbnParams, +) -> anyhow::Result<()> { + self.client + .execute( + UPDATE_BOOK_ISBN, + &[&"arg".title, &"arg".tags, &"arg".book_id, &"arg".isbn], + )?; + Ok(()) } diff --git a/sqlc-gen/examples/booktest/sqlc.yaml b/sqlc-gen/examples/booktest/sqlc.yaml index 7318c33..c625ba3 100644 --- a/sqlc-gen/examples/booktest/sqlc.yaml +++ b/sqlc-gen/examples/booktest/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 91320118cfc27080bdd42a70be43198d3430fd774aa2ea3920ce1f8b55d444c4 + sha256: 148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4 # # - name: js # process: diff --git a/sqlc-gen/examples/jets/postgresql/gen.rs b/sqlc-gen/examples/jets/postgresql/gen.rs index d1792d8..d58c203 100644 --- a/sqlc-gen/examples/jets/postgresql/gen.rs +++ b/sqlc-gen/examples/jets/postgresql/gen.rs @@ -2,45 +2,47 @@ /// DO NOT EDIT. use postgres::{Error, Row}; const COUNT_PILOTS: &str = r#"SELECT COUNT(*) FROM pilots"#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CountPilotsRow { - pub count: i64, -} const LIST_PILOTS: &str = r#"SELECT id, name FROM pilots LIMIT 5"#; +const DELETE_PILOT: &str = r#"DELETE FROM pilots WHERE id = $1"#; #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct ListPilotsRow { +pub(crate) struct Jet { pub id: i32, + pub pilot_id: i32, + pub age: i32, pub name: String, + pub color: String, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct Language { + pub id: i32, + pub language: String, } -const DELETE_PILOT: &str = r#"DELETE FROM pilots WHERE id = $1"#; #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct DeletePilotParams { +pub(crate) struct Pilot { pub id: i32, + pub name: String, } -pub struct Queries { - client: postgres::Client, +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct PilotLanguage { + pub pilot_id: i32, + pub language_id: i32, } -impl Queries { - pub fn new(client: postgres::Client) -> Self { - Self { client } - } - pub(crate) fn count_pilots(&mut self) -> anyhow::Result { - let row = self.client.query_one(COUNT_PILOTS, &[])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn list_pilots(&mut self) -> anyhow::Result> { - let rows = self.client.query(LIST_PILOTS, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) - } - pub(crate) fn delete_pilot( - &mut self, - params: DeletePilotParams, - ) -> anyhow::Result<()> { - self.client.execute(DELETE_PILOT, &[¶ms.id])?; - Ok(()) +pub(crate) fn count_pilots(&mut self, arg: CountPilotsParams) -> anyhow::Result { + let row = self.client.query_one(COUNT_PILOTS, &[])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn delete_pilot(&mut self, id: i32) -> anyhow::Result<()> { + self.client.execute(DELETE_PILOT, &[&"id"])?; + Ok(()) +} +pub(crate) fn list_pilots( + &mut self, + arg: ListPilotsParams, +) -> anyhow::Result> { + let rows = self.client.query(LIST_PILOTS, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } + Ok(result) } diff --git a/sqlc-gen/examples/jets/sqlc.json b/sqlc-gen/examples/jets/sqlc.json index 61ca933..26e2556 100644 --- a/sqlc-gen/examples/jets/sqlc.json +++ b/sqlc-gen/examples/jets/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "91320118cfc27080bdd42a70be43198d3430fd774aa2ea3920ce1f8b55d444c4" + "sha256": "148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4" } } ], diff --git a/sqlc-gen/examples/ondeck/postgresql/gen.rs b/sqlc-gen/examples/ondeck/postgresql/gen.rs index 2444881..a6f3e65 100644 --- a/sqlc-gen/examples/ondeck/postgresql/gen.rs +++ b/sqlc-gen/examples/ondeck/postgresql/gen.rs @@ -1,35 +1,16 @@ /// @generated by the sqlc-gen-rust on sqlc-generate using sqlc.yaml /// DO NOT EDIT. use postgres::{Error, Row}; -#[derive(Debug, Display)] -pub enum Status { - Open, - Closed, -} const LIST_CITIES: &str = r#" select slug, name from city order by name "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct ListCitiesRow { - pub slug: String, - pub name: String, -} const GET_CITY: &str = r#" select slug, name from city where slug = $1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetCityParams { - pub slug: String, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetCityRow { - pub slug: String, - pub name: String, -} const CREATE_CITY: &str = r#" INSERT INTO city ( name, @@ -39,80 +20,26 @@ INSERT INTO city ( $2 ) RETURNING slug, name "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateCityParams { - pub name: String, - pub slug: String, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateCityRow { - pub slug: String, - pub name: String, -} const UPDATE_CITY_NAME: &str = r#" UPDATE city SET name = $2 WHERE slug = $1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateCityNameParams { - pub slug: String, - pub name: String, -} const LIST_VENUES: &str = r#" SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at FROM venue WHERE city = $1 ORDER BY name "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct ListVenuesParams { - pub city: String, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct ListVenuesRow { - pub id: i32, - pub status: String, - pub statuses: Option>, - pub slug: String, - pub name: String, - pub city: String, - pub spotify_playlist: String, - pub songkick_id: Option, - pub tags: Option>, - pub created_at: String, -} const DELETE_VENUE: &str = r#" DELETE FROM venue WHERE slug = $1 AND slug = $1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct DeleteVenueParams { - pub slug: String, -} const GET_VENUE: &str = r#" SELECT id, status, statuses, slug, name, city, spotify_playlist, songkick_id, tags, created_at FROM venue WHERE slug = $1 AND city = $2 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetVenueParams { - pub slug: String, - pub city: String, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetVenueRow { - pub id: i32, - pub status: String, - pub statuses: Option>, - pub slug: String, - pub name: String, - pub city: String, - pub spotify_playlist: String, - pub songkick_id: Option, - pub tags: Option>, - pub created_at: String, -} const CREATE_VENUE: &str = r#" INSERT INTO venue ( slug, @@ -134,35 +61,12 @@ INSERT INTO venue ( $7 ) RETURNING id "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateVenueParams { - pub slug: String, - pub name: String, - pub city: String, - pub spotify_playlist: String, - pub status: String, - pub statuses: Option>, - pub tags: Option>, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateVenueRow { - pub id: i32, -} const UPDATE_VENUE_NAME: &str = r#" UPDATE venue SET name = $2 WHERE slug = $1 RETURNING id "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateVenueNameParams { - pub slug: String, - pub name: String, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateVenueNameRow { - pub id: i32, -} const VENUE_COUNT_BY_CITY: &str = r#" SELECT city, @@ -171,109 +75,109 @@ FROM venue GROUP BY 1 ORDER BY 1 "#; +#[derive(Debug, Display)] +pub enum Status { + Open, + Closed, +} #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct VenueCountByCityRow { - pub city: String, - pub count: i64, +pub(crate) struct City { + pub slug: String, + pub name: String, } -pub struct Queries { - client: postgres::Client, +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct Venue { + pub id: i32, + pub status: String, + pub statuses: Option>, + pub slug: String, + pub name: String, + pub city: String, + pub spotify_playlist: String, + pub songkick_id: Option, + pub tags: Option>, + pub created_at: String, } -impl Queries { - pub fn new(client: postgres::Client) -> Self { - Self { client } - } - pub(crate) fn list_cities(&mut self) -> anyhow::Result> { - let rows = self.client.query(LIST_CITIES, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) - } - pub(crate) fn get_city( - &mut self, - params: GetCityParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_CITY, &[¶ms.slug])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn create_city( - &mut self, - params: CreateCityParams, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_CITY, &[¶ms.name, ¶ms.slug])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn update_city_name( - &mut self, - params: UpdateCityNameParams, - ) -> anyhow::Result<()> { - self.client.execute(UPDATE_CITY_NAME, &[¶ms.slug, ¶ms.name])?; - Ok(()) - } - pub(crate) fn list_venues( - &mut self, - params: ListVenuesParams, - ) -> anyhow::Result> { - let rows = self.client.query(LIST_VENUES, &[¶ms.city])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) - } - pub(crate) fn delete_venue( - &mut self, - params: DeleteVenueParams, - ) -> anyhow::Result<()> { - self.client.execute(DELETE_VENUE, &[¶ms.slug])?; - Ok(()) - } - pub(crate) fn get_venue( - &mut self, - params: GetVenueParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_VENUE, &[¶ms.slug, ¶ms.city])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn create_venue( - &mut self, - params: CreateVenueParams, - ) -> anyhow::Result { - let row = self - .client - .query_one( - CREATE_VENUE, - &[ - ¶ms.slug, - ¶ms.name, - ¶ms.city, - ¶ms.spotify_playlist, - ¶ms.status, - ¶ms.statuses, - ¶ms.tags, - ], - )?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +pub(crate) fn create_city( + &mut self, + arg: CreateCityParams, +) -> anyhow::Result { + let row = self.client.query_one(CREATE_CITY, &[&"arg".name, &"arg".slug])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn create_venue(&mut self, arg: CreateVenueParams) -> anyhow::Result { + let row = self + .client + .query_one( + CREATE_VENUE, + &[ + &"arg".slug, + &"arg".name, + &"arg".city, + &"arg".spotify_playlist, + &"arg".status, + &"arg".statuses, + &"arg".tags, + ], + )?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn delete_venue(&mut self, slug: String) -> anyhow::Result<()> { + self.client.execute(DELETE_VENUE, &[&"slug"])?; + Ok(()) +} +pub(crate) fn get_city(&mut self, slug: String) -> anyhow::Result { + let row = self.client.query_one(GET_CITY, &[&"slug"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn get_venue(&mut self, arg: GetVenueParams) -> anyhow::Result { + let row = self.client.query_one(GET_VENUE, &[&"arg".slug, &"arg".city])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn list_cities( + &mut self, + arg: ListCitiesParams, +) -> anyhow::Result> { + let rows = self.client.query(LIST_CITIES, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } - pub(crate) fn update_venue_name( - &mut self, - params: UpdateVenueNameParams, - ) -> anyhow::Result { - let row = self - .client - .query_one(UPDATE_VENUE_NAME, &[¶ms.slug, ¶ms.name])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + Ok(result) +} +pub(crate) fn list_venues( + &mut self, + city: String, +) -> anyhow::Result> { + let rows = self.client.query(LIST_VENUES, &[&"city"])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } - pub(crate) fn venue_count_by_city( - &mut self, - ) -> anyhow::Result> { - let rows = self.client.query(VENUE_COUNT_BY_CITY, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) + Ok(result) +} +pub(crate) fn update_city_name( + &mut self, + arg: UpdateCityNameParams, +) -> anyhow::Result<()> { + self.client.execute(UPDATE_CITY_NAME, &[&"arg".slug, &"arg".name])?; + Ok(()) +} +pub(crate) fn update_venue_name( + &mut self, + arg: UpdateVenueNameParams, +) -> anyhow::Result { + let row = self.client.query_one(UPDATE_VENUE_NAME, &[&"arg".slug, &"arg".name])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +} +pub(crate) fn venue_count_by_city( + &mut self, + arg: VenueCountByCityParams, +) -> anyhow::Result> { + let rows = self.client.query(VENUE_COUNT_BY_CITY, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } + Ok(result) } diff --git a/sqlc-gen/examples/ondeck/sqlc.json b/sqlc-gen/examples/ondeck/sqlc.json index 15216b0..b0de894 100644 --- a/sqlc-gen/examples/ondeck/sqlc.json +++ b/sqlc-gen/examples/ondeck/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "91320118cfc27080bdd42a70be43198d3430fd774aa2ea3920ce1f8b55d444c4" + "sha256": "148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4" } } ], diff --git a/sqlc-gen/src/codegen/mod.rs b/sqlc-gen/src/codegen/mod.rs index 2c2c27a..678889a 100644 --- a/sqlc-gen/src/codegen/mod.rs +++ b/sqlc-gen/src/codegen/mod.rs @@ -1,16 +1,21 @@ +use check_keyword::CheckKeyword; +use convert_case::Casing; +use core::panic; use itertools::Itertools; use proc_macro2::{Punct, Spacing, TokenStream}; use quote::{format_ident, quote, ToTokens}; use std::fmt; +use std::hash::Hash; +use std::str::FromStr; use syn::Ident; use type_const::TypeConst; use type_enum::TypeEnum; -use type_method::TypeMethod; +use type_query::{QueryCommand, QueryValue, TypeQuery}; use type_struct::{StructField, StructType, TypeStruct}; mod type_const; mod type_enum; -mod type_method; +mod type_query; mod type_struct; pub mod plugin { @@ -34,6 +39,55 @@ pub fn get_newline_tokens() -> TokenStream { get_punct_from_char_tokens(newline_char) } +pub fn column_name(c: &plugin::Column, pos: i32) -> String { + if c.name.is_empty() { + c.name.clone() + } else { + format!("column_{}", pos) + } +} + +pub fn param_name(p: &plugin::Parameter) -> String { + let Some(column) = p.column.clone() else { + panic!("column not found"); + }; + + if !column.name.is_empty() { + column.name.to_case(convert_case::Case::Snake) + } else { + format!("dollar_{}", p.number) + } +} + +pub fn escape(s: &str) -> String { + if s.is_keyword() { + format!("s_{s}") + } else { + s.to_string() + } +} + +pub fn same_table( + col_table: Option, + struct_table: Option, + default_schema: String, +) -> bool { + if let Some(tableId) = col_table { + let mut schema = tableId.schema; + if schema.is_empty() { + schema = default_schema; + } + + if let Some(f) = struct_table { + tableId.catalog == f.catalog && schema == f.schema && tableId.name == f.name + } else { + false + } + } else { + false + } +} + #[derive(Debug, Clone)] pub struct MultiLine<'a>(&'a str); @@ -83,18 +137,9 @@ pub struct MultiLineString<'a>(&'a str); impl<'a> ToTokens for MultiLineString<'a> { fn to_tokens(&self, tokens: &mut TokenStream) { - let double_quote_char = char::from_u32(0x0022).unwrap(); - let hashtag_char = char::from_u32(0x0023).unwrap(); - - tokens.extend(get_punct_from_char_tokens('r')); - tokens.extend(get_punct_from_char_tokens(hashtag_char)); - tokens.extend(get_punct_from_char_tokens(double_quote_char)); - + ['r', '#', '"'].map(|c| tokens.extend(get_punct_from_char_tokens(c))); tokens.extend(MultiLine(self.0).to_token_stream()); - - tokens.extend(get_punct_from_char_tokens(double_quote_char)); - tokens.extend(get_punct_from_char_tokens(hashtag_char)); - tokens.extend(get_punct_from_char_tokens(' ')); + ['"', '#', ' '].map(|c| tokens.extend(get_punct_from_char_tokens(c))); } } @@ -114,7 +159,7 @@ impl ToTokens for DataType { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct PgDataType(pub String); impl ToTokens for PgDataType { @@ -168,71 +213,6 @@ impl fmt::Display for PgDataType { } } -pub trait CodePartial { - fn of_type(self: &Self) -> String; - fn generate_code(self: &Self) -> TokenStream; -} - -struct PartialsBuilder { - query: plugin::Query, -} - -impl PartialsBuilder { - pub fn new(query: plugin::Query) -> PartialsBuilder { - Self { query } - } - - fn create_struct_field_from_column(&self, col: plugin::Column, number: i32) -> StructField { - StructField { - name: col.name, - number, - is_array: col.is_array, - not_null: col.not_null, - data_type: PgDataType(col.r#type.unwrap().name), - } - } - - fn params_struct(&self) -> TypeStruct { - let fields = self.query.params.clone(); - let fields = fields - .into_iter() - .map(|field| self.create_struct_field_from_column(field.column.unwrap(), field.number)) - .collect::>(); - - TypeStruct::new(self.query.name.clone(), StructType::Params, fields) - } - - fn result_struct(&self) -> TypeStruct { - let columns = self.query.columns.clone(); - let fields = columns - .into_iter() - .map(|col| self.create_struct_field_from_column(col, 0)) - .collect::>(); - - TypeStruct::new(self.query.name.clone(), StructType::Row, fields) - } - - pub fn build(&self) -> Vec> { - let query_const = TypeConst::new(self.query.name.clone(), self.query.text.clone()); - let params_struct = self.params_struct(); - let result_struct = self.result_struct(); - let query_method = TypeMethod::new( - self.query.name.clone(), - self.query.cmd.clone(), - query_const.clone(), - params_struct.clone(), - result_struct.clone(), - ); - - vec![ - Box::new(query_const), - Box::new(params_struct), - Box::new(result_struct), - Box::new(query_method), - ] - } -} - #[derive(Default)] pub struct CodeBuilder { req: plugin::CodeGenRequest, @@ -245,78 +225,232 @@ impl CodeBuilder { } impl CodeBuilder { - fn build_enums(&self) -> Vec { + fn build_enums(&self) -> Vec { let catalog = self.req.catalog.clone().unwrap(); - let schemas = catalog.schemas; - let enums = schemas + let enums = catalog + .schemas + .clone() .into_iter() - .map(|schema| { - schema - .enums - .into_iter() - .map(|e| TypeEnum::new(e.name, e.vals)) - .collect::>() + .filter_map(|schema| { + if schema.name == "pg_catalog" || schema.name == "information_schema" { + None + } else { + Some( + schema + .enums + .clone() + .into_iter() + .map(|e| { + let mut enum_name = e.name; + if schema.name != catalog.default_schema { + enum_name = format!("{}_{enum_name}", schema.name); + } + + TypeEnum::new(enum_name, e.vals) + }) + .collect::>(), + ) + } }) .flatten() .collect::>(); enums .into_iter() - .map(|e| e.generate_code()) + .sorted_by(|a, b| Ord::cmp(&a.name(), &b.name())) .collect::>() } - fn build_queries_struct(&self, queries: Vec) -> TokenStream { - let data_type = DataType("postgres::Client".to_string()); - - quote! { - pub struct Queries { - client: #data_type - } + fn build_constants(&self) -> Vec { + let queries = self.req.queries.clone(); + queries + .into_iter() + .map(|q| TypeConst::new(q.name.clone(), q.text.clone())) + .collect::>() + } - impl Queries { - pub fn new(client: postgres::Client) -> Self { - Self { client } + fn build_structs(&self) -> Vec { + let catalog = self.req.catalog.clone().unwrap(); + let mut structs = catalog + .schemas + .clone() + .into_iter() + .filter_map(|schema| { + if schema.name == "pg_catalog" || schema.name == "information_schema" { + None + } else { + let type_struct = schema + .tables + .clone() + .into_iter() + .map(|table| { + let mut table_name = table.rel.clone().unwrap().name; + if schema.name != catalog.default_schema { + table_name = format!("{}_{table_name}", schema.name); + } + + let struct_name = pluralizer::pluralize(table_name.as_str(), 1, false); + let fields = table + .columns + .into_iter() + .map(|col| { + StructField::new( + col.name, + 0, + PgDataType(col.r#type.unwrap().name), + col.is_array, + col.not_null, + ) + }) + .collect::>(); + + TypeStruct::new( + struct_name, + Some(plugin::Identifier { + catalog: "".to_string(), + schema: schema.name.clone(), + name: table.rel.unwrap().name, + }), + StructType::Default, + fields, + ) + }) + .collect::>(); + + Some(type_struct) } + }) + .flatten() + .collect::>(); - #(#queries)* - } - } - } + structs.sort_by(|a, b| Ord::cmp(&a.name(), &b.name())); - fn build_queries(&self) -> Vec { - let build_query = |query: plugin::Query| -> Vec> { - PartialsBuilder::new(query).build() - }; + structs + } - self.req + fn build_queries(&self, structs: Vec) -> Vec { + let catalog = self.req.catalog.clone().unwrap(); + let mut queries = self + .req .queries .clone() .into_iter() - .map(build_query) - .flatten() - .into_group_map_by(|e| e.of_type() == "method") - .into_iter() - .map(|(is_query_method, values)| { - let tokens = values - .into_iter() - .map(|v| v.generate_code()) - .collect::>(); - if is_query_method { - self.build_queries_struct(tokens) + .filter_map(|query| { + if query.name.is_empty() || query.cmd.is_empty() { + None } else { - quote! { - #(#tokens)* + // Query parameter limit, get it from the options + let qpl = 3; + let arg: QueryValue; + let params = query.params.clone(); + if params.len() == 1 && qpl != 0 { + let p = params.first().unwrap(); + let col = p.column.clone().unwrap(); + arg = QueryValue::new( + escape(¶m_name(p)), + Some(col.r#type.unwrap().name.to_string()), + None, + ); + } else { + let fields = params + .into_iter() + .map(|field| { + let col = field.column.unwrap(); + StructField { + name: col.name, + number: field.number, + is_array: col.is_array, + not_null: col.not_null, + data_type: PgDataType(col.r#type.unwrap().name), + } + }) + .collect::>(); + + let type_struct = + TypeStruct::new(query.name.clone(), None, StructType::Params, fields); + arg = QueryValue::new("arg", None, Some(type_struct)); } + + let columns = query.columns.clone(); + let mut ret: Option = None; + if columns.len() == 1 { + let c = columns.first().unwrap(); + // let name = column_name(&c, 0).replace("$", "_"); + ret = Some(QueryValue::new( + "", + Some(c.r#type.clone().unwrap().name.to_string()), + None, + )); + } else if QueryCommand::from_str(&query.cmd) + .expect("invalid query command") + .has_return_value() + { + let found_struct = structs.clone().into_iter().find(|s| { + if s.fields.len() != columns.len() { + false + } else { + s.fields.clone().into_iter().enumerate().all(|(i, field)| { + let c = columns.get(i).unwrap(); + let same_name = field.name + == column_name(c, i as i32) + .to_case(convert_case::Case::Pascal); + let same_type = field.data_type.to_string() + == PgDataType(c.r#type.clone().unwrap().name.to_string()) + .to_string(); + + let same_table = same_table( + c.table.clone(), + s.table.clone(), + catalog.default_schema.clone(), + ); + + same_name && same_type && same_table + }) + } + }); + + let gs = match found_struct { + None => { + let fields = columns + .into_iter() + .enumerate() + .map(|(i, col)| StructField { + name: col.name, + number: i as i32, + is_array: col.is_array, + not_null: col.not_null, + data_type: PgDataType(col.r#type.unwrap().name), + }) + .collect::>(); + + TypeStruct::new(query.name.clone(), None, StructType::Row, fields) + } + Some(gs) => gs, + }; + + ret = Some(QueryValue::new("", None, Some(gs))); + } + + Some(TypeQuery::new( + query.name.clone(), + query.cmd.clone(), + arg, + ret, + )) } }) - // .flatten() - .collect::>() + .collect::>(); + + queries.sort_by(|a, b| Ord::cmp(&a.name(), &b.name())); + queries } pub fn generate_code(&self) -> TokenStream { - let enums_tokens = self.build_enums(); - let queries = self.build_queries(); + let enums = self.build_enums(); + let constants = self.build_constants(); + let structs = self.build_structs(); + let queries = self.build_queries(structs.clone()); + let generated_comment = MultiLine( r#" /// @generated by the sqlc-gen-rust on sqlc-generate using sqlc.yaml @@ -328,7 +462,9 @@ impl CodeBuilder { quote! { #generated_comment use postgres::{Error, Row}; - #(#enums_tokens)* + #(#constants)* + #(#enums)* + #(#structs)* #(#queries)* } } diff --git a/sqlc-gen/src/codegen/type_const.rs b/sqlc-gen/src/codegen/type_const.rs index 51b26f3..dcdcb8b 100644 --- a/sqlc-gen/src/codegen/type_const.rs +++ b/sqlc-gen/src/codegen/type_const.rs @@ -1,4 +1,4 @@ -use super::{get_ident, CodePartial, MultiLineString}; +use super::{get_ident, MultiLineString}; use convert_case::{Case, Casing}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; @@ -20,12 +20,6 @@ impl TypeConst { pub fn name(&self) -> String { self.name.to_case(Case::ScreamingSnake) } -} - -impl CodePartial for TypeConst { - fn of_type(&self) -> String { - "const".to_string() - } fn generate_code(&self) -> TokenStream { let ident_const = get_ident(&self.name()); @@ -37,6 +31,12 @@ impl CodePartial for TypeConst { } } +impl ToTokens for TypeConst { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.generate_code()); + } +} + #[cfg(test)] pub mod tests { use super::*; diff --git a/sqlc-gen/src/codegen/type_enum.rs b/sqlc-gen/src/codegen/type_enum.rs index e51bb59..d1d9ac5 100644 --- a/sqlc-gen/src/codegen/type_enum.rs +++ b/sqlc-gen/src/codegen/type_enum.rs @@ -1,7 +1,19 @@ -use super::{get_ident, CodePartial}; +use std::{char, collections::HashSet}; + +use super::get_ident; use convert_case::{Case, Casing}; use proc_macro2::TokenStream; -use quote::quote; +use quote::{quote, ToTokens}; + +fn enum_replacer(c: char) -> Option { + if ['-', '/', ':', '_'].contains(&c) { + Some('_') + } else if c.is_alphanumeric() { + Some(c) + } else { + None + } +} #[derive(Default, Debug, PartialEq)] pub struct TypeEnum { @@ -17,23 +29,27 @@ impl TypeEnum { } } - fn name(&self) -> String { + pub fn name(&self) -> String { self.name.to_case(Case::Pascal) } -} - -impl CodePartial for TypeEnum { - fn of_type(&self) -> String { - "enum".to_string() - } + /// TODO: add #[postgres(name = "")] to generated enum variant fn generate_code(&self) -> TokenStream { let ident_enum_name = get_ident(&self.name()); + let mut seen = HashSet::new(); let variants = self .values .clone() .into_iter() - .map(|val| get_ident(&val.to_case(Case::Pascal))) + .enumerate() + .map(|(i, val)| { + let mut value = val.chars().filter_map(enum_replacer).collect::(); + if seen.get(&value).is_some() || value.is_empty() { + value = format!("value_{}", i); + } + seen.insert(value.clone()); + get_ident(&value.to_case(Case::Pascal)) + }) .collect::>(); quote! { @@ -45,6 +61,12 @@ impl CodePartial for TypeEnum { } } +impl ToTokens for TypeEnum { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.generate_code().to_token_stream()); + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/sqlc-gen/src/codegen/type_method.rs b/sqlc-gen/src/codegen/type_method.rs deleted file mode 100644 index 87c7eae..0000000 --- a/sqlc-gen/src/codegen/type_method.rs +++ /dev/null @@ -1,112 +0,0 @@ -use super::type_const::TypeConst; -use super::type_struct::TypeStruct; -use super::{get_ident, CodePartial}; -use convert_case::{Case, Casing}; -use proc_macro2::TokenStream; -use quote::quote; -use std::str::FromStr; -use strum_macros::EnumString; - -#[derive(Debug, PartialEq, EnumString)] -enum QueryCommand { - #[strum(serialize = ":one")] - One, - #[strum(serialize = ":many")] - Many, - #[strum(serialize = ":exec")] - Exec, - #[strum(serialize = ":execresult")] - ExecResult, - #[strum(serialize = ":execrows")] - ExecRows, - #[strum(serialize = ":execlastid")] - ExecLastId, -} - -#[derive(Default)] -pub struct TypeMethod { - name: String, - query_command: String, - query_const: TypeConst, - params_struct: TypeStruct, - row_struct: TypeStruct, -} - -impl TypeMethod { - pub fn new>( - name: S, - query_command: S, - query_const: TypeConst, - params_struct: TypeStruct, - row_struct: TypeStruct, - ) -> Self { - Self { - name: name.into(), - query_command: query_command.into(), - query_const, - params_struct, - row_struct, - } - } - - fn name(&self) -> String { - self.name.to_case(Case::Snake) - } - - fn query_command(&self) -> QueryCommand { - QueryCommand::from_str(&self.query_command).unwrap() - } -} - -impl CodePartial for TypeMethod { - fn of_type(&self) -> String { - "method".to_string() - } - - fn generate_code(&self) -> TokenStream { - let ident_name = get_ident(&self.name()); - let ident_row = get_ident(&self.row_struct.name()); - - let ident_const_name = get_ident(&self.query_const.name()); - let fields_list = self.params_struct.generate_fields_list(); - - let command = self.query_command(); - - let params_arg = self.params_struct.get_as_arg(Some("params")); - - let query_method = match command { - QueryCommand::One => { - quote! { - pub(crate) fn #ident_name(&mut self, #params_arg) -> anyhow::Result<#ident_row> { - let row = self.client.query_one(#ident_const_name, &[#fields_list])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - } - } - QueryCommand::Many => { - quote! { - pub(crate) fn #ident_name(&mut self, #params_arg) -> anyhow::Result> { - let rows = self.client.query(#ident_const_name, &[#fields_list])?; - let mut result: Vec<#ident_row> = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - - Ok(result) - } - } - } - QueryCommand::Exec - | QueryCommand::ExecRows - | QueryCommand::ExecResult - | QueryCommand::ExecLastId => quote! { - pub(crate) fn #ident_name(&mut self, #params_arg) -> anyhow::Result<()> { - self.client.execute(#ident_const_name, &[#fields_list])?; - Ok(()) - } - }, - }; - - quote! { #query_method } - } -} diff --git a/sqlc-gen/src/codegen/type_query.rs b/sqlc-gen/src/codegen/type_query.rs new file mode 100644 index 0000000..8bf1f5c --- /dev/null +++ b/sqlc-gen/src/codegen/type_query.rs @@ -0,0 +1,189 @@ +use super::type_struct::TypeStruct; +use super::{get_ident, PgDataType}; +use convert_case::{Case, Casing}; +use core::panic; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use std::str::FromStr; +use strum_macros::EnumString; + +#[derive(Debug, PartialEq, EnumString)] +pub enum QueryCommand { + #[strum(serialize = ":one")] + One, + #[strum(serialize = ":many")] + Many, + #[strum(serialize = ":exec")] + Exec, + #[strum(serialize = ":execresult")] + ExecResult, + #[strum(serialize = ":execrows")] + ExecRows, + #[strum(serialize = ":execlastid")] + ExecLastId, +} + +impl QueryCommand { + pub fn has_return_value(&self) -> bool { + match *self { + Self::One | Self::Many => true, + _ => false, + } + } +} + +#[derive(Default, Clone)] +pub struct QueryValue { + name: String, + typ: Option, + type_struct: Option, +} + +impl QueryValue { + pub fn new>( + name: S, + typ: Option, + type_struct: Option, + ) -> Self { + Self { + name: name.into(), + typ, + type_struct, + } + } + + pub fn get_type(&self) -> String { + if let Some(typ) = &self.typ { + PgDataType(typ.to_string()).to_string() + } else if let Some(type_struct) = self.type_struct.clone() { + type_struct.name() + } else { + panic!("QueryValue neither has `typ` specified nor `type_struct`"); + } + } + + pub fn generate_fields_list(&self) -> TokenStream { + let ident_name = self.name.clone(); + let mut fields_list = quote! {}; + if self.typ.is_some() { + fields_list = quote! { &#ident_name }; + } else if let Some(type_struct) = self.type_struct.clone() { + let fields = type_struct + .fields + .clone() + .into_iter() + .map(|field| { + let ident_field_name = get_ident(&field.name()); + quote! { &#ident_name.#ident_field_name } + }) + .collect::>(); + fields_list = quote! { #(#fields),* } + } else { + // add panic if necessary + // panic!("QueryValue neither has `typ` specified nor `type_struct`"); + } + + fields_list + } +} + +impl ToTokens for QueryValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + let ident_type = get_ident(&self.get_type()); + + if !self.name.is_empty() { + let ident_name = get_ident(&self.name); + tokens.extend(quote! { + #ident_name: #ident_type + }); + } else { + tokens.extend(quote! { + #ident_type + }); + } + } +} + +#[derive(Default)] +pub struct TypeQuery { + name: String, + cmd: String, + arg: QueryValue, + ret: Option, +} + +impl TypeQuery { + pub fn new>(name: S, cmd: S, arg: QueryValue, ret: Option) -> Self { + Self { + name: name.into(), + cmd: cmd.into(), + arg, + ret, + } + } + + fn constant_name(&self) -> String { + self.name.to_case(Case::ScreamingSnake) + } + + pub fn name(&self) -> String { + self.name.to_case(Case::Snake) + } + + fn command(&self) -> QueryCommand { + QueryCommand::from_str(&self.cmd).unwrap() + } + + fn generate_code(&self) -> TokenStream { + let ident_name = get_ident(&self.name()); + let ident_const_name = get_ident(&self.constant_name()); + let fields_list = self.arg.generate_fields_list(); + let command = self.command(); + let arg = &self.arg; + + // let params_arg = self.params_struct.get_as_arg(Some("params")); + let query_method = match command { + QueryCommand::One => { + let ret = self.ret.clone().unwrap_or_default(); + quote! { + pub(crate) fn #ident_name(&mut self, #arg) -> anyhow::Result<#ret> { + let row = self.client.query_one(#ident_const_name, &[#fields_list])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + } + } + QueryCommand::Many => { + let ret = self.ret.clone().unwrap_or_default(); + quote! { + pub(crate) fn #ident_name(&mut self, #arg) -> anyhow::Result> { + let rows = self.client.query(#ident_const_name, &[#fields_list])?; + let mut result: Vec<#ret> = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + + Ok(result) + } + } + } + QueryCommand::Exec + | QueryCommand::ExecRows + | QueryCommand::ExecResult + | QueryCommand::ExecLastId => quote! { + pub(crate) fn #ident_name(&mut self, #arg) -> anyhow::Result<()> { + self.client.execute(#ident_const_name, &[#fields_list])?; + Ok(()) + } + }, + // _ => quote! {}, + }; + + quote! { #query_method } + } +} + +impl ToTokens for TypeQuery { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.generate_code().to_token_stream()); + } +} diff --git a/sqlc-gen/src/codegen/type_struct.rs b/sqlc-gen/src/codegen/type_struct.rs index 560b17f..e27b023 100644 --- a/sqlc-gen/src/codegen/type_struct.rs +++ b/sqlc-gen/src/codegen/type_struct.rs @@ -1,10 +1,12 @@ +use super::get_ident; +use super::plugin; use super::PgDataType; -use super::{get_ident, CodePartial}; use convert_case::{Case, Casing}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; +use std::hash::{DefaultHasher, Hash, Hasher}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub struct StructField { pub name: String, pub is_array: bool, @@ -59,9 +61,21 @@ impl StructField { } } +impl ToTokens for StructField { + fn to_tokens(&self, tokens: &mut TokenStream) { + let field_name_ident = get_ident(&self.name()); + let field_type_ident = self.data_type(); + + tokens.extend(quote! { + pub #field_name_ident: #field_type_ident + }) + } +} + #[derive(Default, Debug, Clone)] pub enum StructType { #[default] + Default, Params, Row, Queries, @@ -74,6 +88,7 @@ pub enum StructType { #[derive(Default, Debug, Clone)] pub struct TypeStruct { name: String, + pub table: Option, struct_type: StructType, pub fields: Vec, } @@ -81,37 +96,21 @@ pub struct TypeStruct { impl TypeStruct { pub fn new>( name: S, + table: Option, struct_type: StructType, fields: Vec, ) -> Self { Self { name: name.into(), + table, struct_type, fields, } } - pub fn exists(&self) -> bool { - self.fields.len() > 0 - } - - pub fn get_as_arg(&self, arg_name: Option<&str>) -> TokenStream { - let mut params_arg = quote! {}; - if let Some(name) = arg_name { - if self.exists() { - let ident_name = get_ident(name); - let ident_params = get_ident(&self.name()); - params_arg = quote! { - #ident_name: #ident_params - } - } - } - - params_arg - } - pub fn name(&self) -> String { let name = match &self.struct_type { + StructType::Default => format!("{}", self.name), StructType::Params => format!("{}Params", self.name), StructType::Row => format!("{}Row", self.name), StructType::Queries => self.name.clone(), @@ -123,50 +122,12 @@ impl TypeStruct { name.to_case(Case::Pascal) } - pub fn generate_fields_list(&self) -> TokenStream { - if self.fields.len() == 0 { - quote! {} - } else { - let fields = self - .fields - .clone() - .into_iter() - .map(|field| { - let ident_field_name = get_ident(&field.name()); - quote! { ¶ms.#ident_field_name } - }) - .collect::>(); - - quote! { #(#fields),* } - } - } - - fn generate_struct_field_code(&self, field: StructField) -> TokenStream { - let field_name_ident = get_ident(&field.name()); - let field_type_ident = field.data_type(); - - quote! { - pub #field_name_ident: #field_type_ident - } - } -} - -impl CodePartial for TypeStruct { - fn of_type(&self) -> String { - "struct".to_string() - } - fn generate_code(&self) -> TokenStream { if self.fields.len() == 0 { quote! {} } else { let ident_struct = get_ident(&self.name()); - let fields = self - .fields - .clone() - .into_iter() - .map(|field| self.generate_struct_field_code(field)) - .collect::>(); + let fields = self.fields.clone().into_iter().collect::>(); quote! { #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] @@ -178,6 +139,12 @@ impl CodePartial for TypeStruct { } } +impl ToTokens for TypeStruct { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.generate_code()); + } +} + #[cfg(test)] mod tests { use super::*; @@ -259,6 +226,11 @@ mod tests { TypeStruct::new( name.unwrap_or("struct_name"), + Some(plugin::Identifier { + catalog: "".to_string(), + schema: "".to_string(), + name: "".to_string(), + }), struct_type.unwrap_or_default(), fields.unwrap_or(default_fields), ) @@ -292,32 +264,6 @@ mod tests { ); } - #[test] - fn test_generate_field_code() { - let type_struct = create_type_struct(None, None, None); - assert_eq!( - type_struct - .generate_struct_field_code(type_struct.fields[0].clone()) - .to_string(), - quote! { - pub(crate) f_1: Option - } - .to_string() - ) - } - - #[test] - fn test_generate_fields_list() { - let type_struct = create_type_struct(None, None, None); - assert_eq!( - type_struct.generate_fields_list().to_string(), - quote! { - ¶ms.f_1, ¶ms.f_2, ¶ms.f, ¶ms.f_3, ¶ms._3 - } - .to_string() - ) - } - #[test] fn test_generate_code() { let type_struct = create_type_struct(None, None, None); From c86ab658b41f6b8fc63a6a3c2740c1152c779584 Mon Sep 17 00:00:00 2001 From: ES Date: Fri, 20 Sep 2024 18:14:34 +0530 Subject: [PATCH 2/6] Added Queries struct and impl to generated code --- sqlc-gen/examples/authors/postgresql/gen.rs | 54 ++++--- sqlc-gen/examples/authors/sqlc.yaml | 2 +- sqlc-gen/examples/batch/postgresql/gen.rs | 14 +- sqlc-gen/examples/batch/sqlc.json | 2 +- sqlc-gen/examples/booktest/gen/gen.rs | 159 +++++++++--------- sqlc-gen/examples/booktest/sqlc.yaml | 2 +- sqlc-gen/examples/jets/postgresql/gen.rs | 43 +++-- sqlc-gen/examples/jets/sqlc.json | 2 +- sqlc-gen/examples/ondeck/postgresql/gen.rs | 170 +++++++++++--------- sqlc-gen/examples/ondeck/sqlc.json | 2 +- sqlc-gen/src/codegen/mod.rs | 15 +- 11 files changed, 265 insertions(+), 200 deletions(-) diff --git a/sqlc-gen/examples/authors/postgresql/gen.rs b/sqlc-gen/examples/authors/postgresql/gen.rs index 007725a..a19496e 100644 --- a/sqlc-gen/examples/authors/postgresql/gen.rs +++ b/sqlc-gen/examples/authors/postgresql/gen.rs @@ -27,29 +27,37 @@ pub(crate) struct Author { pub name: String, pub bio: Option, } -pub(crate) fn create_author( - &mut self, - arg: CreateAuthorParams, -) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[&"arg".name, &"arg".bio])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +pub struct Queries { + client: postgres::Client, } -pub(crate) fn delete_author(&mut self, id: i64) -> anyhow::Result<()> { - self.client.execute(DELETE_AUTHOR, &[&"id"])?; - Ok(()) -} -pub(crate) fn get_author(&mut self, id: i64) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[&"id"])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn list_authors( - &mut self, - arg: ListAuthorsParams, -) -> anyhow::Result> { - let rows = self.client.query(LIST_AUTHORS, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); +impl Queries { + pub fn new(client: postgres::Client) -> Self { + Self { client } + } + pub(crate) fn create_author( + &mut self, + arg: CreateAuthorParams, + ) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[&"arg".name, &"arg".bio])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn delete_author(&mut self, id: i64) -> anyhow::Result<()> { + self.client.execute(DELETE_AUTHOR, &[&"id"])?; + Ok(()) + } + pub(crate) fn get_author(&mut self, id: i64) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&"id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn list_authors( + &mut self, + arg: ListAuthorsParams, + ) -> anyhow::Result> { + let rows = self.client.query(LIST_AUTHORS, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) } - Ok(result) } diff --git a/sqlc-gen/examples/authors/sqlc.yaml b/sqlc-gen/examples/authors/sqlc.yaml index 6d6248a..ccc44c1 100644 --- a/sqlc-gen/examples/authors/sqlc.yaml +++ b/sqlc-gen/examples/authors/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4 + sha256: 2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798 # # - name: js # process: diff --git a/sqlc-gen/examples/batch/postgresql/gen.rs b/sqlc-gen/examples/batch/postgresql/gen.rs index e229a79..64bd890 100644 --- a/sqlc-gen/examples/batch/postgresql/gen.rs +++ b/sqlc-gen/examples/batch/postgresql/gen.rs @@ -28,7 +28,15 @@ pub(crate) struct Book { pub available: String, pub tags: Vec, } -pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +pub struct Queries { + client: postgres::Client, +} +impl Queries { + pub fn new(client: postgres::Client) -> Self { + Self { client } + } + pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } } diff --git a/sqlc-gen/examples/batch/sqlc.json b/sqlc-gen/examples/batch/sqlc.json index 56cdd82..54b912f 100644 --- a/sqlc-gen/examples/batch/sqlc.json +++ b/sqlc-gen/examples/batch/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4" + "sha256": "2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798" } } ], diff --git a/sqlc-gen/examples/booktest/gen/gen.rs b/sqlc-gen/examples/booktest/gen/gen.rs index e4777bf..7506d24 100644 --- a/sqlc-gen/examples/booktest/gen/gen.rs +++ b/sqlc-gen/examples/booktest/gen/gen.rs @@ -85,80 +85,91 @@ pub(crate) struct Book { pub available: String, pub tags: Vec, } -pub(crate) fn books_by_tags( - &mut self, - dollar_1: String, -) -> anyhow::Result> { - let rows = self.client.query(BOOKS_BY_TAGS, &[&"dollar_1"])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) +pub struct Queries { + client: postgres::Client, } -pub(crate) fn books_by_title_year( - &mut self, - arg: BooksByTitleYearParams, -) -> anyhow::Result> { - let rows = self.client.query(BOOKS_BY_TITLE_YEAR, &[&"arg".title, &"arg".year])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); +impl Queries { + pub fn new(client: postgres::Client) -> Self { + Self { client } + } + pub(crate) fn books_by_tags( + &mut self, + dollar_1: String, + ) -> anyhow::Result> { + let rows = self.client.query(BOOKS_BY_TAGS, &[&"dollar_1"])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) + } + pub(crate) fn books_by_title_year( + &mut self, + arg: BooksByTitleYearParams, + ) -> anyhow::Result> { + let rows = self.client.query(BOOKS_BY_TITLE_YEAR, &[&"arg".title, &"arg".year])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) + } + pub(crate) fn create_author( + &mut self, + name: String, + ) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[&"name"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn create_book( + &mut self, + arg: CreateBookParams, + ) -> anyhow::Result { + let row = self + .client + .query_one( + CREATE_BOOK, + &[ + &"arg".author_id, + &"arg".isbn, + &"arg".book_type, + &"arg".title, + &"arg".year, + &"arg".available, + &"arg".tags, + ], + )?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn delete_book(&mut self, book_id: i32) -> anyhow::Result<()> { + self.client.execute(DELETE_BOOK, &[&"book_id"])?; + Ok(()) + } + pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn get_book(&mut self, book_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_BOOK, &[&"book_id"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn say_hello(&mut self, s: String) -> anyhow::Result { + let row = self.client.query_one(SAY_HELLO, &[&"s"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn update_book(&mut self, arg: UpdateBookParams) -> anyhow::Result<()> { + self.client.execute(UPDATE_BOOK, &[&"arg".title, &"arg".tags, &"arg".book_id])?; + Ok(()) + } + pub(crate) fn update_book_isbn( + &mut self, + arg: UpdateBookIsbnParams, + ) -> anyhow::Result<()> { + self.client + .execute( + UPDATE_BOOK_ISBN, + &[&"arg".title, &"arg".tags, &"arg".book_id, &"arg".isbn], + )?; + Ok(()) } - Ok(result) -} -pub(crate) fn create_author(&mut self, name: String) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[&"name"])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn create_book( - &mut self, - arg: CreateBookParams, -) -> anyhow::Result { - let row = self - .client - .query_one( - CREATE_BOOK, - &[ - &"arg".author_id, - &"arg".isbn, - &"arg".book_type, - &"arg".title, - &"arg".year, - &"arg".available, - &"arg".tags, - ], - )?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn delete_book(&mut self, book_id: i32) -> anyhow::Result<()> { - self.client.execute(DELETE_BOOK, &[&"book_id"])?; - Ok(()) -} -pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn get_book(&mut self, book_id: i32) -> anyhow::Result { - let row = self.client.query_one(GET_BOOK, &[&"book_id"])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn say_hello(&mut self, s: String) -> anyhow::Result { - let row = self.client.query_one(SAY_HELLO, &[&"s"])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn update_book(&mut self, arg: UpdateBookParams) -> anyhow::Result<()> { - self.client.execute(UPDATE_BOOK, &[&"arg".title, &"arg".tags, &"arg".book_id])?; - Ok(()) -} -pub(crate) fn update_book_isbn( - &mut self, - arg: UpdateBookIsbnParams, -) -> anyhow::Result<()> { - self.client - .execute( - UPDATE_BOOK_ISBN, - &[&"arg".title, &"arg".tags, &"arg".book_id, &"arg".isbn], - )?; - Ok(()) } diff --git a/sqlc-gen/examples/booktest/sqlc.yaml b/sqlc-gen/examples/booktest/sqlc.yaml index c625ba3..af233f1 100644 --- a/sqlc-gen/examples/booktest/sqlc.yaml +++ b/sqlc-gen/examples/booktest/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4 + sha256: 2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798 # # - name: js # process: diff --git a/sqlc-gen/examples/jets/postgresql/gen.rs b/sqlc-gen/examples/jets/postgresql/gen.rs index d58c203..e759f5b 100644 --- a/sqlc-gen/examples/jets/postgresql/gen.rs +++ b/sqlc-gen/examples/jets/postgresql/gen.rs @@ -27,22 +27,33 @@ pub(crate) struct PilotLanguage { pub pilot_id: i32, pub language_id: i32, } -pub(crate) fn count_pilots(&mut self, arg: CountPilotsParams) -> anyhow::Result { - let row = self.client.query_one(COUNT_PILOTS, &[])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +pub struct Queries { + client: postgres::Client, } -pub(crate) fn delete_pilot(&mut self, id: i32) -> anyhow::Result<()> { - self.client.execute(DELETE_PILOT, &[&"id"])?; - Ok(()) -} -pub(crate) fn list_pilots( - &mut self, - arg: ListPilotsParams, -) -> anyhow::Result> { - let rows = self.client.query(LIST_PILOTS, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); +impl Queries { + pub fn new(client: postgres::Client) -> Self { + Self { client } + } + pub(crate) fn count_pilots( + &mut self, + arg: CountPilotsParams, + ) -> anyhow::Result { + let row = self.client.query_one(COUNT_PILOTS, &[])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn delete_pilot(&mut self, id: i32) -> anyhow::Result<()> { + self.client.execute(DELETE_PILOT, &[&"id"])?; + Ok(()) + } + pub(crate) fn list_pilots( + &mut self, + arg: ListPilotsParams, + ) -> anyhow::Result> { + let rows = self.client.query(LIST_PILOTS, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) } - Ok(result) } diff --git a/sqlc-gen/examples/jets/sqlc.json b/sqlc-gen/examples/jets/sqlc.json index 26e2556..8ff97dd 100644 --- a/sqlc-gen/examples/jets/sqlc.json +++ b/sqlc-gen/examples/jets/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4" + "sha256": "2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798" } } ], diff --git a/sqlc-gen/examples/ondeck/postgresql/gen.rs b/sqlc-gen/examples/ondeck/postgresql/gen.rs index a6f3e65..0189ec9 100644 --- a/sqlc-gen/examples/ondeck/postgresql/gen.rs +++ b/sqlc-gen/examples/ondeck/postgresql/gen.rs @@ -98,86 +98,100 @@ pub(crate) struct Venue { pub tags: Option>, pub created_at: String, } -pub(crate) fn create_city( - &mut self, - arg: CreateCityParams, -) -> anyhow::Result { - let row = self.client.query_one(CREATE_CITY, &[&"arg".name, &"arg".slug])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) +pub struct Queries { + client: postgres::Client, } -pub(crate) fn create_venue(&mut self, arg: CreateVenueParams) -> anyhow::Result { - let row = self - .client - .query_one( - CREATE_VENUE, - &[ - &"arg".slug, - &"arg".name, - &"arg".city, - &"arg".spotify_playlist, - &"arg".status, - &"arg".statuses, - &"arg".tags, - ], - )?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn delete_venue(&mut self, slug: String) -> anyhow::Result<()> { - self.client.execute(DELETE_VENUE, &[&"slug"])?; - Ok(()) -} -pub(crate) fn get_city(&mut self, slug: String) -> anyhow::Result { - let row = self.client.query_one(GET_CITY, &[&"slug"])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn get_venue(&mut self, arg: GetVenueParams) -> anyhow::Result { - let row = self.client.query_one(GET_VENUE, &[&"arg".slug, &"arg".city])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn list_cities( - &mut self, - arg: ListCitiesParams, -) -> anyhow::Result> { - let rows = self.client.query(LIST_CITIES, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); +impl Queries { + pub fn new(client: postgres::Client) -> Self { + Self { client } } - Ok(result) -} -pub(crate) fn list_venues( - &mut self, - city: String, -) -> anyhow::Result> { - let rows = self.client.query(LIST_VENUES, &[&"city"])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + pub(crate) fn create_city( + &mut self, + arg: CreateCityParams, + ) -> anyhow::Result { + let row = self.client.query_one(CREATE_CITY, &[&"arg".name, &"arg".slug])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } - Ok(result) -} -pub(crate) fn update_city_name( - &mut self, - arg: UpdateCityNameParams, -) -> anyhow::Result<()> { - self.client.execute(UPDATE_CITY_NAME, &[&"arg".slug, &"arg".name])?; - Ok(()) -} -pub(crate) fn update_venue_name( - &mut self, - arg: UpdateVenueNameParams, -) -> anyhow::Result { - let row = self.client.query_one(UPDATE_VENUE_NAME, &[&"arg".slug, &"arg".name])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) -} -pub(crate) fn venue_count_by_city( - &mut self, - arg: VenueCountByCityParams, -) -> anyhow::Result> { - let rows = self.client.query(VENUE_COUNT_BY_CITY, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + pub(crate) fn create_venue( + &mut self, + arg: CreateVenueParams, + ) -> anyhow::Result { + let row = self + .client + .query_one( + CREATE_VENUE, + &[ + &"arg".slug, + &"arg".name, + &"arg".city, + &"arg".spotify_playlist, + &"arg".status, + &"arg".statuses, + &"arg".tags, + ], + )?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn delete_venue(&mut self, slug: String) -> anyhow::Result<()> { + self.client.execute(DELETE_VENUE, &[&"slug"])?; + Ok(()) + } + pub(crate) fn get_city(&mut self, slug: String) -> anyhow::Result { + let row = self.client.query_one(GET_CITY, &[&"slug"])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn get_venue( + &mut self, + arg: GetVenueParams, + ) -> anyhow::Result { + let row = self.client.query_one(GET_VENUE, &[&"arg".slug, &"arg".city])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn list_cities( + &mut self, + arg: ListCitiesParams, + ) -> anyhow::Result> { + let rows = self.client.query(LIST_CITIES, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) + } + pub(crate) fn list_venues( + &mut self, + city: String, + ) -> anyhow::Result> { + let rows = self.client.query(LIST_VENUES, &[&"city"])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) + } + pub(crate) fn update_city_name( + &mut self, + arg: UpdateCityNameParams, + ) -> anyhow::Result<()> { + self.client.execute(UPDATE_CITY_NAME, &[&"arg".slug, &"arg".name])?; + Ok(()) + } + pub(crate) fn update_venue_name( + &mut self, + arg: UpdateVenueNameParams, + ) -> anyhow::Result { + let row = self.client.query_one(UPDATE_VENUE_NAME, &[&"arg".slug, &"arg".name])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn venue_count_by_city( + &mut self, + arg: VenueCountByCityParams, + ) -> anyhow::Result> { + let rows = self.client.query(VENUE_COUNT_BY_CITY, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) } - Ok(result) } diff --git a/sqlc-gen/examples/ondeck/sqlc.json b/sqlc-gen/examples/ondeck/sqlc.json index b0de894..fa4c641 100644 --- a/sqlc-gen/examples/ondeck/sqlc.json +++ b/sqlc-gen/examples/ondeck/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "148e2e26844daee8205ae49d02b8028ddf24971dc3ed6a8356e33d5c090c7ba4" + "sha256": "2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798" } } ], diff --git a/sqlc-gen/src/codegen/mod.rs b/sqlc-gen/src/codegen/mod.rs index 678889a..1dd5e47 100644 --- a/sqlc-gen/src/codegen/mod.rs +++ b/sqlc-gen/src/codegen/mod.rs @@ -459,13 +459,26 @@ impl CodeBuilder { ) .to_token_stream(); + let queries_impl = quote! { + pub struct Queries { + client: postgres::Client, + } + impl Queries { + pub fn new(client: postgres::Client) -> Self { + Self { client } + } + + #(#queries)* + } + }; + quote! { #generated_comment use postgres::{Error, Row}; #(#constants)* #(#enums)* #(#structs)* - #(#queries)* + #queries_impl } } } From f17b50a7743d416d42cc1f0f8b9e10f564521d32 Mon Sep 17 00:00:00 2001 From: ES Date: Sat, 21 Sep 2024 09:09:15 +0530 Subject: [PATCH 3/6] in generated code: add (only) query associated structs and remove redundant struts --- sqlc-gen/examples/authors/postgresql/gen.rs | 19 +++-- sqlc-gen/examples/authors/sqlc.yaml | 2 +- sqlc-gen/examples/batch/postgresql/gen.rs | 4 +- sqlc-gen/examples/batch/sqlc.json | 2 +- sqlc-gen/examples/booktest/gen/gen.rs | 86 ++++++++++++++------- sqlc-gen/examples/booktest/sqlc.yaml | 2 +- sqlc-gen/examples/jets/postgresql/gen.rs | 6 +- sqlc-gen/examples/jets/sqlc.json | 2 +- sqlc-gen/examples/ondeck/postgresql/gen.rs | 86 ++++++++++++++------- sqlc-gen/examples/ondeck/sqlc.json | 2 +- sqlc-gen/src/codegen/mod.rs | 49 +++++++----- sqlc-gen/src/codegen/type_query.rs | 2 +- sqlc-gen/src/codegen/type_struct.rs | 10 +-- 13 files changed, 169 insertions(+), 103 deletions(-) diff --git a/sqlc-gen/examples/authors/postgresql/gen.rs b/sqlc-gen/examples/authors/postgresql/gen.rs index a19496e..c551fd4 100644 --- a/sqlc-gen/examples/authors/postgresql/gen.rs +++ b/sqlc-gen/examples/authors/postgresql/gen.rs @@ -27,6 +27,11 @@ pub(crate) struct Author { pub name: String, pub bio: Option, } +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct CreateAuthorParams { + pub name: String, + pub bio: Option, +} pub struct Queries { client: postgres::Client, } @@ -37,24 +42,24 @@ impl Queries { pub(crate) fn create_author( &mut self, arg: CreateAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[&"arg".name, &"arg".bio])?; + ) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[&arg.name, &arg.bio])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn delete_author(&mut self, id: i64) -> anyhow::Result<()> { - self.client.execute(DELETE_AUTHOR, &[&"id"])?; + self.client.execute(DELETE_AUTHOR, &[&id])?; Ok(()) } - pub(crate) fn get_author(&mut self, id: i64) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[&"id"])?; + pub(crate) fn get_author(&mut self, id: i64) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&id])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn list_authors( &mut self, arg: ListAuthorsParams, - ) -> anyhow::Result> { + ) -> anyhow::Result> { let rows = self.client.query(LIST_AUTHORS, &[])?; - let mut result: Vec = vec![]; + let mut result: Vec = vec![]; for row in rows { result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } diff --git a/sqlc-gen/examples/authors/sqlc.yaml b/sqlc-gen/examples/authors/sqlc.yaml index ccc44c1..5337e1d 100644 --- a/sqlc-gen/examples/authors/sqlc.yaml +++ b/sqlc-gen/examples/authors/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798 + sha256: 1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00 # # - name: js # process: diff --git a/sqlc-gen/examples/batch/postgresql/gen.rs b/sqlc-gen/examples/batch/postgresql/gen.rs index 64bd890..32f0a58 100644 --- a/sqlc-gen/examples/batch/postgresql/gen.rs +++ b/sqlc-gen/examples/batch/postgresql/gen.rs @@ -35,8 +35,8 @@ impl Queries { pub fn new(client: postgres::Client) -> Self { Self { client } } - pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; + pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&author_id])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } } diff --git a/sqlc-gen/examples/batch/sqlc.json b/sqlc-gen/examples/batch/sqlc.json index 54b912f..5c9d1a4 100644 --- a/sqlc-gen/examples/batch/sqlc.json +++ b/sqlc-gen/examples/batch/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798" + "sha256": "1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00" } } ], diff --git a/sqlc-gen/examples/booktest/gen/gen.rs b/sqlc-gen/examples/booktest/gen/gen.rs index 7506d24..63d101d 100644 --- a/sqlc-gen/examples/booktest/gen/gen.rs +++ b/sqlc-gen/examples/booktest/gen/gen.rs @@ -85,6 +85,42 @@ pub(crate) struct Book { pub available: String, pub tags: Vec, } +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct BooksByTitleYearParams { + pub title: String, + pub year: i32, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct BooksByTagsRow { + pub book_id: i32, + pub title: String, + pub name: Option, + pub isbn: String, + pub tags: Vec, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct CreateBookParams { + pub author_id: i32, + pub isbn: String, + pub book_type: String, + pub title: String, + pub year: i32, + pub available: String, + pub tags: Vec, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct UpdateBookParams { + pub title: String, + pub tags: Vec, + pub book_id: i32, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct UpdateBookIsbnParams { + pub title: String, + pub tags: Vec, + pub book_id: i32, + pub isbn: String, +} pub struct Queries { client: postgres::Client, } @@ -96,7 +132,7 @@ impl Queries { &mut self, dollar_1: String, ) -> anyhow::Result> { - let rows = self.client.query(BOOKS_BY_TAGS, &[&"dollar_1"])?; + let rows = self.client.query(BOOKS_BY_TAGS, &[&dollar_1])?; let mut result: Vec = vec![]; for row in rows { result.push(sqlc_core::FromPostgresRow::from_row(&row)?); @@ -106,59 +142,53 @@ impl Queries { pub(crate) fn books_by_title_year( &mut self, arg: BooksByTitleYearParams, - ) -> anyhow::Result> { - let rows = self.client.query(BOOKS_BY_TITLE_YEAR, &[&"arg".title, &"arg".year])?; - let mut result: Vec = vec![]; + ) -> anyhow::Result> { + let rows = self.client.query(BOOKS_BY_TITLE_YEAR, &[&arg.title, &arg.year])?; + let mut result: Vec = vec![]; for row in rows { result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } Ok(result) } - pub(crate) fn create_author( - &mut self, - name: String, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[&"name"])?; + pub(crate) fn create_author(&mut self, name: String) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[&name])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } - pub(crate) fn create_book( - &mut self, - arg: CreateBookParams, - ) -> anyhow::Result { + pub(crate) fn create_book(&mut self, arg: CreateBookParams) -> anyhow::Result { let row = self .client .query_one( CREATE_BOOK, &[ - &"arg".author_id, - &"arg".isbn, - &"arg".book_type, - &"arg".title, - &"arg".year, - &"arg".available, - &"arg".tags, + &arg.author_id, + &arg.isbn, + &arg.book_type, + &arg.title, + &arg.year, + &arg.available, + &arg.tags, ], )?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn delete_book(&mut self, book_id: i32) -> anyhow::Result<()> { - self.client.execute(DELETE_BOOK, &[&"book_id"])?; + self.client.execute(DELETE_BOOK, &[&book_id])?; Ok(()) } - pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[&"author_id"])?; + pub(crate) fn get_author(&mut self, author_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&author_id])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } - pub(crate) fn get_book(&mut self, book_id: i32) -> anyhow::Result { - let row = self.client.query_one(GET_BOOK, &[&"book_id"])?; + pub(crate) fn get_book(&mut self, book_id: i32) -> anyhow::Result { + let row = self.client.query_one(GET_BOOK, &[&book_id])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn say_hello(&mut self, s: String) -> anyhow::Result { - let row = self.client.query_one(SAY_HELLO, &[&"s"])?; + let row = self.client.query_one(SAY_HELLO, &[&s])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn update_book(&mut self, arg: UpdateBookParams) -> anyhow::Result<()> { - self.client.execute(UPDATE_BOOK, &[&"arg".title, &"arg".tags, &"arg".book_id])?; + self.client.execute(UPDATE_BOOK, &[&arg.title, &arg.tags, &arg.book_id])?; Ok(()) } pub(crate) fn update_book_isbn( @@ -168,7 +198,7 @@ impl Queries { self.client .execute( UPDATE_BOOK_ISBN, - &[&"arg".title, &"arg".tags, &"arg".book_id, &"arg".isbn], + &[&arg.title, &arg.tags, &arg.book_id, &arg.isbn], )?; Ok(()) } diff --git a/sqlc-gen/examples/booktest/sqlc.yaml b/sqlc-gen/examples/booktest/sqlc.yaml index af233f1..9c2c06b 100644 --- a/sqlc-gen/examples/booktest/sqlc.yaml +++ b/sqlc-gen/examples/booktest/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798 + sha256: 1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00 # # - name: js # process: diff --git a/sqlc-gen/examples/jets/postgresql/gen.rs b/sqlc-gen/examples/jets/postgresql/gen.rs index e759f5b..d77ff27 100644 --- a/sqlc-gen/examples/jets/postgresql/gen.rs +++ b/sqlc-gen/examples/jets/postgresql/gen.rs @@ -42,15 +42,15 @@ impl Queries { Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn delete_pilot(&mut self, id: i32) -> anyhow::Result<()> { - self.client.execute(DELETE_PILOT, &[&"id"])?; + self.client.execute(DELETE_PILOT, &[&id])?; Ok(()) } pub(crate) fn list_pilots( &mut self, arg: ListPilotsParams, - ) -> anyhow::Result> { + ) -> anyhow::Result> { let rows = self.client.query(LIST_PILOTS, &[])?; - let mut result: Vec = vec![]; + let mut result: Vec = vec![]; for row in rows { result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } diff --git a/sqlc-gen/examples/jets/sqlc.json b/sqlc-gen/examples/jets/sqlc.json index 8ff97dd..19512c3 100644 --- a/sqlc-gen/examples/jets/sqlc.json +++ b/sqlc-gen/examples/jets/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798" + "sha256": "1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00" } } ], diff --git a/sqlc-gen/examples/ondeck/postgresql/gen.rs b/sqlc-gen/examples/ondeck/postgresql/gen.rs index 0189ec9..65762e1 100644 --- a/sqlc-gen/examples/ondeck/postgresql/gen.rs +++ b/sqlc-gen/examples/ondeck/postgresql/gen.rs @@ -98,6 +98,41 @@ pub(crate) struct Venue { pub tags: Option>, pub created_at: String, } +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct CreateCityParams { + pub name: String, + pub slug: String, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct UpdateCityNameParams { + pub slug: String, + pub name: String, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct GetVenueParams { + pub slug: String, + pub city: String, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct CreateVenueParams { + pub slug: String, + pub name: String, + pub city: String, + pub spotify_playlist: String, + pub status: String, + pub statuses: Option>, + pub tags: Option>, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct UpdateVenueNameParams { + pub slug: String, + pub name: String, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct VenueCountByCityRow { + pub city: String, + pub count: i64, +} pub struct Queries { client: postgres::Client, } @@ -105,11 +140,8 @@ impl Queries { pub fn new(client: postgres::Client) -> Self { Self { client } } - pub(crate) fn create_city( - &mut self, - arg: CreateCityParams, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_CITY, &[&"arg".name, &"arg".slug])?; + pub(crate) fn create_city(&mut self, arg: CreateCityParams) -> anyhow::Result { + let row = self.client.query_one(CREATE_CITY, &[&arg.name, &arg.slug])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn create_venue( @@ -121,49 +153,43 @@ impl Queries { .query_one( CREATE_VENUE, &[ - &"arg".slug, - &"arg".name, - &"arg".city, - &"arg".spotify_playlist, - &"arg".status, - &"arg".statuses, - &"arg".tags, + &arg.slug, + &arg.name, + &arg.city, + &arg.spotify_playlist, + &arg.status, + &arg.statuses, + &arg.tags, ], )?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn delete_venue(&mut self, slug: String) -> anyhow::Result<()> { - self.client.execute(DELETE_VENUE, &[&"slug"])?; + self.client.execute(DELETE_VENUE, &[&slug])?; Ok(()) } - pub(crate) fn get_city(&mut self, slug: String) -> anyhow::Result { - let row = self.client.query_one(GET_CITY, &[&"slug"])?; + pub(crate) fn get_city(&mut self, slug: String) -> anyhow::Result { + let row = self.client.query_one(GET_CITY, &[&slug])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } - pub(crate) fn get_venue( - &mut self, - arg: GetVenueParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_VENUE, &[&"arg".slug, &"arg".city])?; + pub(crate) fn get_venue(&mut self, arg: GetVenueParams) -> anyhow::Result { + let row = self.client.query_one(GET_VENUE, &[&arg.slug, &arg.city])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn list_cities( &mut self, arg: ListCitiesParams, - ) -> anyhow::Result> { + ) -> anyhow::Result> { let rows = self.client.query(LIST_CITIES, &[])?; - let mut result: Vec = vec![]; + let mut result: Vec = vec![]; for row in rows { result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } Ok(result) } - pub(crate) fn list_venues( - &mut self, - city: String, - ) -> anyhow::Result> { - let rows = self.client.query(LIST_VENUES, &[&"city"])?; - let mut result: Vec = vec![]; + pub(crate) fn list_venues(&mut self, city: String) -> anyhow::Result> { + let rows = self.client.query(LIST_VENUES, &[&city])?; + let mut result: Vec = vec![]; for row in rows { result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } @@ -173,14 +199,14 @@ impl Queries { &mut self, arg: UpdateCityNameParams, ) -> anyhow::Result<()> { - self.client.execute(UPDATE_CITY_NAME, &[&"arg".slug, &"arg".name])?; + self.client.execute(UPDATE_CITY_NAME, &[&arg.slug, &arg.name])?; Ok(()) } pub(crate) fn update_venue_name( &mut self, arg: UpdateVenueNameParams, ) -> anyhow::Result { - let row = self.client.query_one(UPDATE_VENUE_NAME, &[&"arg".slug, &"arg".name])?; + let row = self.client.query_one(UPDATE_VENUE_NAME, &[&arg.slug, &arg.name])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } pub(crate) fn venue_count_by_city( diff --git a/sqlc-gen/examples/ondeck/sqlc.json b/sqlc-gen/examples/ondeck/sqlc.json index fa4c641..56ddc7b 100644 --- a/sqlc-gen/examples/ondeck/sqlc.json +++ b/sqlc-gen/examples/ondeck/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "2e9ec5756144dd994ae7a771b50d0282019b68414236b0f2c17d606f01f42798" + "sha256": "1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00" } } ], diff --git a/sqlc-gen/src/codegen/mod.rs b/sqlc-gen/src/codegen/mod.rs index 1dd5e47..102b53c 100644 --- a/sqlc-gen/src/codegen/mod.rs +++ b/sqlc-gen/src/codegen/mod.rs @@ -39,12 +39,13 @@ pub fn get_newline_tokens() -> TokenStream { get_punct_from_char_tokens(newline_char) } -pub fn column_name(c: &plugin::Column, pos: i32) -> String { - if c.name.is_empty() { - c.name.clone() - } else { - format!("column_{}", pos) - } +pub fn column_name(name: String, pos: i32) -> String { + let col_name = match name.is_empty() { + false => name.clone(), + true => format!("_{}", pos), + }; + + col_name.to_case(convert_case::Case::Snake) } pub fn param_name(p: &plugin::Parameter) -> String { @@ -72,14 +73,14 @@ pub fn same_table( struct_table: Option, default_schema: String, ) -> bool { - if let Some(tableId) = col_table { - let mut schema = tableId.schema; + if let Some(table_id) = col_table { + let mut schema = table_id.schema; if schema.is_empty() { schema = default_schema; } if let Some(f) = struct_table { - tableId.catalog == f.catalog && schema == f.schema && tableId.name == f.name + table_id.catalog == f.catalog && schema == f.schema && table_id.name == f.name } else { false } @@ -328,8 +329,9 @@ impl CodeBuilder { structs } - fn build_queries(&self, structs: Vec) -> Vec { + fn build_queries(&self, structs: Vec) -> (Vec, Vec) { let catalog = self.req.catalog.clone().unwrap(); + let mut associated_structs = vec![]; let mut queries = self .req .queries @@ -368,14 +370,14 @@ impl CodeBuilder { let type_struct = TypeStruct::new(query.name.clone(), None, StructType::Params, fields); - arg = QueryValue::new("arg", None, Some(type_struct)); + arg = QueryValue::new("arg", None, Some(type_struct.clone())); + associated_structs.push(type_struct); } let columns = query.columns.clone(); let mut ret: Option = None; if columns.len() == 1 { let c = columns.first().unwrap(); - // let name = column_name(&c, 0).replace("$", "_"); ret = Some(QueryValue::new( "", Some(c.r#type.clone().unwrap().name.to_string()), @@ -391,12 +393,11 @@ impl CodeBuilder { } else { s.fields.clone().into_iter().enumerate().all(|(i, field)| { let c = columns.get(i).unwrap(); - let same_name = field.name - == column_name(c, i as i32) - .to_case(convert_case::Case::Pascal); + let same_name = + field.name() == column_name(c.name.to_string(), i as i32); + let same_type = field.data_type.to_string() - == PgDataType(c.r#type.clone().unwrap().name.to_string()) - .to_string(); + == PgDataType(c.r#type.clone().unwrap().name).to_string(); let same_table = same_table( c.table.clone(), @@ -423,7 +424,14 @@ impl CodeBuilder { }) .collect::>(); - TypeStruct::new(query.name.clone(), None, StructType::Row, fields) + let type_struct = TypeStruct::new( + query.name.clone(), + None, + StructType::Row, + fields, + ); + associated_structs.push(type_struct.clone()); + type_struct } Some(gs) => gs, }; @@ -442,14 +450,14 @@ impl CodeBuilder { .collect::>(); queries.sort_by(|a, b| Ord::cmp(&a.name(), &b.name())); - queries + (queries, associated_structs) } pub fn generate_code(&self) -> TokenStream { let enums = self.build_enums(); let constants = self.build_constants(); let structs = self.build_structs(); - let queries = self.build_queries(structs.clone()); + let (queries, associated_structs) = self.build_queries(structs.clone()); let generated_comment = MultiLine( r#" @@ -478,6 +486,7 @@ impl CodeBuilder { #(#constants)* #(#enums)* #(#structs)* + #(#associated_structs)* #queries_impl } } diff --git a/sqlc-gen/src/codegen/type_query.rs b/sqlc-gen/src/codegen/type_query.rs index 8bf1f5c..d469ddf 100644 --- a/sqlc-gen/src/codegen/type_query.rs +++ b/sqlc-gen/src/codegen/type_query.rs @@ -63,7 +63,7 @@ impl QueryValue { } pub fn generate_fields_list(&self) -> TokenStream { - let ident_name = self.name.clone(); + let ident_name = get_ident(&self.name.clone()); let mut fields_list = quote! {}; if self.typ.is_some() { fields_list = quote! { &#ident_name }; diff --git a/sqlc-gen/src/codegen/type_struct.rs b/sqlc-gen/src/codegen/type_struct.rs index e27b023..ef5a98d 100644 --- a/sqlc-gen/src/codegen/type_struct.rs +++ b/sqlc-gen/src/codegen/type_struct.rs @@ -1,12 +1,12 @@ +use super::column_name; use super::get_ident; use super::plugin; use super::PgDataType; use convert_case::{Case, Casing}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; -use std::hash::{DefaultHasher, Hash, Hasher}; -#[derive(Debug, Clone, Hash)] +#[derive(Debug, Clone)] pub struct StructField { pub name: String, pub is_array: bool, @@ -33,11 +33,7 @@ impl StructField { } pub fn name(&self) -> String { - if !self.name.is_empty() { - self.name.to_case(Case::Snake) - } else { - format!("_{}", self.number) - } + column_name(self.name.clone(), self.number) } pub fn data_type(&self) -> TokenStream { From 1acac3ed0e95c825f158e197b0a1d56f182d7873 Mon Sep 17 00:00:00 2001 From: ES Date: Sat, 21 Sep 2024 10:35:58 +0530 Subject: [PATCH 4/6] handle enum variants with non-alphanumeric chars --- sqlc-gen/examples/authors/sqlc.yaml | 2 +- sqlc-gen/examples/batch/postgresql/gen.rs | 4 +- sqlc-gen/examples/batch/sqlc.json | 2 +- sqlc-gen/examples/booktest/gen/gen.rs | 20 +++++----- sqlc-gen/examples/booktest/sqlc.yaml | 2 +- sqlc-gen/examples/jets/sqlc.json | 2 +- sqlc-gen/examples/ondeck/postgresql/gen.rs | 40 ++++++++++--------- .../ondeck/postgresql/schema/0002_venue.sql | 4 +- sqlc-gen/examples/ondeck/sqlc.json | 2 +- sqlc-gen/src/codegen/mod.rs | 13 +++++- sqlc-gen/src/codegen/type_enum.rs | 24 ++++++----- 11 files changed, 68 insertions(+), 47 deletions(-) diff --git a/sqlc-gen/examples/authors/sqlc.yaml b/sqlc-gen/examples/authors/sqlc.yaml index 5337e1d..d41d8a7 100644 --- a/sqlc-gen/examples/authors/sqlc.yaml +++ b/sqlc-gen/examples/authors/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00 + sha256: 0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f # # - name: js # process: diff --git a/sqlc-gen/examples/batch/postgresql/gen.rs b/sqlc-gen/examples/batch/postgresql/gen.rs index 32f0a58..8bd3e8d 100644 --- a/sqlc-gen/examples/batch/postgresql/gen.rs +++ b/sqlc-gen/examples/batch/postgresql/gen.rs @@ -6,9 +6,11 @@ select author_id, name, biography from authors where author_id = $1 "#; -#[derive(Debug, Display)] +#[derive(Debug, Display, postgres_types::ToSql, postgres_type::FromSql)] pub enum BookType { + #[postgres(name = "FICTION")] Fiction, + #[postgres(name = "NONFICTION")] Nonfiction, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] diff --git a/sqlc-gen/examples/batch/sqlc.json b/sqlc-gen/examples/batch/sqlc.json index 5c9d1a4..e734499 100644 --- a/sqlc-gen/examples/batch/sqlc.json +++ b/sqlc-gen/examples/batch/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00" + "sha256": "0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f" } } ], diff --git a/sqlc-gen/examples/booktest/gen/gen.rs b/sqlc-gen/examples/booktest/gen/gen.rs index 63d101d..b171254 100644 --- a/sqlc-gen/examples/booktest/gen/gen.rs +++ b/sqlc-gen/examples/booktest/gen/gen.rs @@ -64,9 +64,11 @@ const SAY_HELLO: &str = r#" select say_hello from say_hello($1) "#; -#[derive(Debug, Display)] +#[derive(Debug, Display, postgres_types::ToSql, postgres_type::FromSql)] pub enum BookType { + #[postgres(name = "FICTION")] Fiction, + #[postgres(name = "NONFICTION")] Nonfiction, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] @@ -86,11 +88,6 @@ pub(crate) struct Book { pub tags: Vec, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct BooksByTitleYearParams { - pub title: String, - pub year: i32, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] pub(crate) struct BooksByTagsRow { pub book_id: i32, pub title: String, @@ -99,6 +96,11 @@ pub(crate) struct BooksByTagsRow { pub tags: Vec, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct BooksByTitleYearParams { + pub title: String, + pub year: i32, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] pub(crate) struct CreateBookParams { pub author_id: i32, pub isbn: String, @@ -109,17 +111,17 @@ pub(crate) struct CreateBookParams { pub tags: Vec, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateBookParams { +pub(crate) struct UpdateBookIsbnParams { pub title: String, pub tags: Vec, pub book_id: i32, + pub isbn: String, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateBookIsbnParams { +pub(crate) struct UpdateBookParams { pub title: String, pub tags: Vec, pub book_id: i32, - pub isbn: String, } pub struct Queries { client: postgres::Client, diff --git a/sqlc-gen/examples/booktest/sqlc.yaml b/sqlc-gen/examples/booktest/sqlc.yaml index 9c2c06b..71dd1aa 100644 --- a/sqlc-gen/examples/booktest/sqlc.yaml +++ b/sqlc-gen/examples/booktest/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00 + sha256: 0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f # # - name: js # process: diff --git a/sqlc-gen/examples/jets/sqlc.json b/sqlc-gen/examples/jets/sqlc.json index 19512c3..c09db02 100644 --- a/sqlc-gen/examples/jets/sqlc.json +++ b/sqlc-gen/examples/jets/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00" + "sha256": "0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f" } } ], diff --git a/sqlc-gen/examples/ondeck/postgresql/gen.rs b/sqlc-gen/examples/ondeck/postgresql/gen.rs index 65762e1..f4ef91c 100644 --- a/sqlc-gen/examples/ondeck/postgresql/gen.rs +++ b/sqlc-gen/examples/ondeck/postgresql/gen.rs @@ -75,9 +75,11 @@ FROM venue GROUP BY 1 ORDER BY 1 "#; -#[derive(Debug, Display)] +#[derive(Debug, Display, postgres_types::ToSql, postgres_type::FromSql)] pub enum Status { + #[postgres(name = "op!en")] Open, + #[postgres(name = "clo@sed")] Closed, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] @@ -86,22 +88,24 @@ pub(crate) struct City { pub name: String, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct Venue { - pub id: i32, - pub status: String, - pub statuses: Option>, +pub(crate) struct CreateCityParams { + pub name: String, + pub slug: String, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct CreateVenueParams { pub slug: String, pub name: String, pub city: String, pub spotify_playlist: String, - pub songkick_id: Option, + pub status: String, + pub statuses: Option>, pub tags: Option>, - pub created_at: String, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateCityParams { - pub name: String, +pub(crate) struct GetVenueParams { pub slug: String, + pub city: String, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] pub(crate) struct UpdateCityNameParams { @@ -109,24 +113,22 @@ pub(crate) struct UpdateCityNameParams { pub name: String, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetVenueParams { +pub(crate) struct UpdateVenueNameParams { pub slug: String, - pub city: String, + pub name: String, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateVenueParams { +pub(crate) struct Venue { + pub id: i32, + pub status: String, + pub statuses: Option>, pub slug: String, pub name: String, pub city: String, pub spotify_playlist: String, - pub status: String, - pub statuses: Option>, + pub songkick_id: Option, pub tags: Option>, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct UpdateVenueNameParams { - pub slug: String, - pub name: String, + pub created_at: String, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] pub(crate) struct VenueCountByCityRow { diff --git a/sqlc-gen/examples/ondeck/postgresql/schema/0002_venue.sql b/sqlc-gen/examples/ondeck/postgresql/schema/0002_venue.sql index fbde8ad..a04f692 100644 --- a/sqlc-gen/examples/ondeck/postgresql/schema/0002_venue.sql +++ b/sqlc-gen/examples/ondeck/postgresql/schema/0002_venue.sql @@ -1,5 +1,5 @@ --- CREATE TYPE status AS ENUM ('op!en', 'clo@sed'); -CREATE TYPE status AS ENUM ('open', 'closed'); +CREATE TYPE status AS ENUM ('op!en', 'clo@sed'); +-- CREATE TYPE status AS ENUM ('open', 'closed'); COMMENT ON TYPE status IS 'Venues can be either open or closed'; CREATE TABLE venues ( diff --git a/sqlc-gen/examples/ondeck/sqlc.json b/sqlc-gen/examples/ondeck/sqlc.json index 56ddc7b..67939bc 100644 --- a/sqlc-gen/examples/ondeck/sqlc.json +++ b/sqlc-gen/examples/ondeck/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "1088e9ccad85c6d2e287e5b845edbfa865f2511118092878128476e3cb9d9f00" + "sha256": "0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f" } } ], diff --git a/sqlc-gen/src/codegen/mod.rs b/sqlc-gen/src/codegen/mod.rs index 102b53c..4c8c85c 100644 --- a/sqlc-gen/src/codegen/mod.rs +++ b/sqlc-gen/src/codegen/mod.rs @@ -450,15 +450,25 @@ impl CodeBuilder { .collect::>(); queries.sort_by(|a, b| Ord::cmp(&a.name(), &b.name())); + associated_structs.sort_by(|a, b| Ord::cmp(&a.name(), &b.name())); + (queries, associated_structs) } pub fn generate_code(&self) -> TokenStream { let enums = self.build_enums(); let constants = self.build_constants(); - let structs = self.build_structs(); + let mut structs = self.build_structs(); + let (queries, associated_structs) = self.build_queries(structs.clone()); + structs.extend(associated_structs); + structs.sort_by(|a, b| Ord::cmp(&a.name(), &b.name())); + + // TODO: below + // let (enums, structs) = self.filter_unused_structs(enums, structs, queries); + // validate_structs_and_enums + let generated_comment = MultiLine( r#" /// @generated by the sqlc-gen-rust on sqlc-generate using sqlc.yaml @@ -486,7 +496,6 @@ impl CodeBuilder { #(#constants)* #(#enums)* #(#structs)* - #(#associated_structs)* #queries_impl } } diff --git a/sqlc-gen/src/codegen/type_enum.rs b/sqlc-gen/src/codegen/type_enum.rs index d1d9ac5..6d3ea43 100644 --- a/sqlc-gen/src/codegen/type_enum.rs +++ b/sqlc-gen/src/codegen/type_enum.rs @@ -15,6 +15,19 @@ fn enum_replacer(c: char) -> Option { } } +fn generate_enum_variant(i: usize, val: String, seen: &mut HashSet) -> TokenStream { + let mut value = val.chars().filter_map(enum_replacer).collect::(); + if seen.get(&value).is_some() || value.is_empty() { + value = format!("value_{}", i); + } + seen.insert(value.clone()); + let ident_variant = get_ident(&value.to_case(Case::Pascal)); + quote! { + #[postgres(name=#val)] + #ident_variant + } +} + #[derive(Default, Debug, PartialEq)] pub struct TypeEnum { name: String, @@ -42,18 +55,11 @@ impl TypeEnum { .clone() .into_iter() .enumerate() - .map(|(i, val)| { - let mut value = val.chars().filter_map(enum_replacer).collect::(); - if seen.get(&value).is_some() || value.is_empty() { - value = format!("value_{}", i); - } - seen.insert(value.clone()); - get_ident(&value.to_case(Case::Pascal)) - }) + .map(|(i, val)| generate_enum_variant(i, val, &mut seen)) .collect::>(); quote! { - #[derive(Debug, Display)] + #[derive(Debug, Display, postgres_types::ToSql, postgres_type::FromSql)] pub enum #ident_enum_name { #(#variants),* } From 77e3a8a64ab84ab09a61a5c40b8b2b6e95152df4 Mon Sep 17 00:00:00 2001 From: ES Date: Sat, 21 Sep 2024 15:28:22 +0530 Subject: [PATCH 5/6] use enums in args and return values, generated code cleanup --- Cargo.lock | 46 +++++ examples/authors/sqlc.yaml | 3 +- examples/authors/src/db/gen.bk.rs | 91 ++++++++++ examples/authors/src/db/gen.rs | 68 +++----- examples/authors/src/main.rs | 28 +-- sqlc-core/Cargo.toml | 1 + sqlc-gen/examples/authors/postgresql/gen.rs | 5 +- sqlc-gen/examples/authors/sqlc.yaml | 2 +- sqlc-gen/examples/batch/postgresql/gen.rs | 2 +- sqlc-gen/examples/batch/sqlc.json | 2 +- sqlc-gen/examples/booktest/gen/gen.rs | 4 +- sqlc-gen/examples/booktest/sqlc.yaml | 2 +- sqlc-gen/examples/jets/postgresql/gen.rs | 10 +- sqlc-gen/examples/jets/sqlc.json | 2 +- sqlc-gen/examples/ondeck/postgresql/gen.rs | 14 +- sqlc-gen/examples/ondeck/sqlc.json | 2 +- sqlc-gen/src/codegen/mod.rs | 183 ++++++++++++-------- sqlc-gen/src/codegen/type_enum.rs | 9 +- sqlc-gen/src/codegen/type_query.rs | 33 ++-- sqlc-gen/src/codegen/type_struct.rs | 42 ++--- 20 files changed, 339 insertions(+), 210 deletions(-) create mode 100644 examples/authors/src/db/gen.bk.rs diff --git a/Cargo.lock b/Cargo.lock index cd120ff..b13e8ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "check_keyword" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3678a4fc14c47dce76127d66f9b2637ff497af665d5618f1f756f4ad1b4a5b1" +dependencies = [ + "phf", +] + [[package]] name = "cipher" version = "0.4.4" @@ -1290,7 +1299,31 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b" +dependencies = [ + "phf_generator", "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.77", ] [[package]] @@ -1340,6 +1373,16 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "pluralizer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e4616e94b67b8b61846ea69d4bf041a62147d569d16f437689229e2677d38c" +dependencies = [ + "lazy_static", + "regex", +] + [[package]] name = "portable-atomic" version = "1.7.0" @@ -2025,6 +2068,7 @@ name = "sqlc-core" version = "0.1.0" dependencies = [ "postgres", + "postgres-types", ] [[package]] @@ -2042,8 +2086,10 @@ name = "sqlc-gen" version = "0.1.0" dependencies = [ "bytes", + "check_keyword", "convert_case", "itertools 0.13.0", + "pluralizer", "postgres-types", "prettyplease", "proc-macro2", diff --git a/examples/authors/sqlc.yaml b/examples/authors/sqlc.yaml index fdde40e..93a1a8d 100644 --- a/examples/authors/sqlc.yaml +++ b/examples/authors/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 91320118cfc27080bdd42a70be43198d3430fd774aa2ea3920ce1f8b55d444c4 + sha256: 7c02055c3eba7bcb913da0e9090541314d14da8c350b34891c4a2e5826c23307 # - name: js # process: @@ -19,6 +19,7 @@ sql: - out: src/db plugin: rust-gen options: + type: async lang: en-US # - out: gen # plugin: js diff --git a/examples/authors/src/db/gen.bk.rs b/examples/authors/src/db/gen.bk.rs new file mode 100644 index 0000000..99930ff --- /dev/null +++ b/examples/authors/src/db/gen.bk.rs @@ -0,0 +1,91 @@ +/// @generated by the sqlc-gen-rust on sqlc-generate using sqlc.yaml +/// DO NOT EDIT. +use postgres::{Error, Row}; +const GET_AUTHOR: &str = r#" +SELECT id, name, bio FROM authors +WHERE id = $1 LIMIT 1 +"#; +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct GetAuthorParams { + pub id: i64, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct GetAuthorRow { + pub id: i64, + pub name: String, + pub bio: Option, +} +const LIST_AUTHORS: &str = r#" +SELECT id, name, bio FROM authors +ORDER BY name +"#; +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct ListAuthorsRow { + pub id: i64, + pub name: String, + pub bio: Option, +} +const CREATE_AUTHOR: &str = r#" +INSERT INTO authors ( + name, bio +) VALUES ( + $1, $2 +) +RETURNING id, name, bio +"#; +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct CreateAuthorParams { + pub name: String, + pub bio: Option, +} +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct CreateAuthorRow { + pub id: i64, + pub name: String, + pub bio: Option, +} +const DELETE_AUTHOR: &str = r#" +DELETE FROM authors +WHERE id = $1 +"#; +#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] +pub(crate) struct DeleteAuthorParams { + pub id: i64, +} +pub struct Queries { + client: postgres::Client, +} +impl Queries { + pub fn new(client: postgres::Client) -> Self { + Self { client } + } + pub(crate) fn get_author( + &mut self, + params: GetAuthorParams, + ) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[¶ms.id])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn list_authors(&mut self) -> anyhow::Result> { + let rows = self.client.query(LIST_AUTHORS, &[])?; + let mut result: Vec = vec![]; + for row in rows { + result.push(sqlc_core::FromPostgresRow::from_row(&row)?); + } + Ok(result) + } + pub(crate) fn create_author( + &mut self, + params: CreateAuthorParams, + ) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[¶ms.name, ¶ms.bio])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn delete_author( + &mut self, + params: DeleteAuthorParams, + ) -> anyhow::Result<()> { + self.client.execute(DELETE_AUTHOR, &[¶ms.id])?; + Ok(()) + } +} diff --git a/examples/authors/src/db/gen.rs b/examples/authors/src/db/gen.rs index 99930ff..6adbd7e 100644 --- a/examples/authors/src/db/gen.rs +++ b/examples/authors/src/db/gen.rs @@ -5,26 +5,10 @@ const GET_AUTHOR: &str = r#" SELECT id, name, bio FROM authors WHERE id = $1 LIMIT 1 "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorParams { - pub id: i64, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorRow { - pub id: i64, - pub name: String, - pub bio: Option, -} const LIST_AUTHORS: &str = r#" SELECT id, name, bio FROM authors ORDER BY name "#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct ListAuthorsRow { - pub id: i64, - pub name: String, - pub bio: Option, -} const CREATE_AUTHOR: &str = r#" INSERT INTO authors ( name, bio @@ -33,25 +17,21 @@ INSERT INTO authors ( ) RETURNING id, name, bio "#; +const DELETE_AUTHOR: &str = r#" +DELETE FROM authors +WHERE id = $1 +"#; #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorParams { +pub(crate) struct Author { + pub id: i64, pub name: String, pub bio: Option, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorRow { - pub id: i64, +pub(crate) struct CreateAuthorParams { pub name: String, pub bio: Option, } -const DELETE_AUTHOR: &str = r#" -DELETE FROM authors -WHERE id = $1 -"#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct DeleteAuthorParams { - pub id: i64, -} pub struct Queries { client: postgres::Client, } @@ -59,33 +39,27 @@ impl Queries { pub fn new(client: postgres::Client) -> Self { Self { client } } - pub(crate) fn get_author( + pub(crate) fn create_author( &mut self, - params: GetAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[¶ms.id])?; + arg: CreateAuthorParams, + ) -> anyhow::Result { + let row = self.client.query_one(CREATE_AUTHOR, &[&arg.name, &arg.bio])?; + Ok(sqlc_core::FromPostgresRow::from_row(&row)?) + } + pub(crate) fn delete_author(&mut self, id: i64) -> anyhow::Result<()> { + self.client.execute(DELETE_AUTHOR, &[&id])?; + Ok(()) + } + pub(crate) fn get_author(&mut self, id: i64) -> anyhow::Result { + let row = self.client.query_one(GET_AUTHOR, &[&id])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } - pub(crate) fn list_authors(&mut self) -> anyhow::Result> { + pub(crate) fn list_authors(&mut self) -> anyhow::Result> { let rows = self.client.query(LIST_AUTHORS, &[])?; - let mut result: Vec = vec![]; + let mut result: Vec = vec![]; for row in rows { result.push(sqlc_core::FromPostgresRow::from_row(&row)?); } Ok(result) } - pub(crate) fn create_author( - &mut self, - params: CreateAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[¶ms.name, ¶ms.bio])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn delete_author( - &mut self, - params: DeleteAuthorParams, - ) -> anyhow::Result<()> { - self.client.execute(DELETE_AUTHOR, &[¶ms.id])?; - Ok(()) - } } diff --git a/examples/authors/src/main.rs b/examples/authors/src/main.rs index 4906c3a..a0c6ef4 100644 --- a/examples/authors/src/main.rs +++ b/examples/authors/src/main.rs @@ -39,12 +39,10 @@ fn main() -> Result<()> { let authors = queries.list_authors().unwrap(); assert_eq!(authors.len(), 0); - let author_res_err = queries.get_author(db::GetAuthorParams { id: 1 }).is_err(); + let author_res_err = queries.get_author(1).is_err(); assert_eq!(author_res_err, true); - let delete_res = queries - .delete_author(db::DeleteAuthorParams { id: 1 }) - .is_ok(); + let delete_res = queries.delete_author(1).is_ok(); assert_eq!(delete_res, true); let author1_req = db::CreateAuthorParams { @@ -56,11 +54,7 @@ fn main() -> Result<()> { assert_eq!(author1_res.bio, author1_req.bio.clone()); assert!(author1_res.id > 0); - let mut authors_list_prepared = vec![db::ListAuthorsRow { - id: author1_res.id, - name: author1_res.name.clone(), - bio: author1_res.bio.clone(), - }]; + let mut authors_list_prepared = vec![author1_res.clone()]; let authors = queries.list_authors().unwrap(); assert_eq!(authors.len(), 1); assert_eq!(authors, authors_list_prepared); @@ -74,24 +68,16 @@ fn main() -> Result<()> { assert_eq!(author2_res.bio, author2_req.bio); assert!(author2_res.id > 1); - authors_list_prepared.push(db::ListAuthorsRow { - id: author2_res.id, - name: author2_res.name, - bio: author2_res.bio, - }); + authors_list_prepared.push(author2_res.clone()); let authors = queries.list_authors().unwrap(); assert_eq!(authors.len(), 2); assert_eq!(authors, authors_list_prepared); - let author = queries.get_author(db::GetAuthorParams { id: 1 }).unwrap(); - assert_eq!(author.id, author1_res.id); - assert_eq!(author.name, author1_res.name); - assert_eq!(author.bio, author1_res.bio); + let author = queries.get_author(1).unwrap(); + assert_eq!(author, author1_res); - queries - .delete_author(db::DeleteAuthorParams { id: 1 }) - .unwrap(); + queries.delete_author(1).unwrap(); let authors = queries.list_authors().unwrap(); assert_eq!(authors.len(), 1); assert_eq!(authors, authors_list_prepared[1..]); diff --git a/sqlc-core/Cargo.toml b/sqlc-core/Cargo.toml index e47d2ac..60f3b5e 100644 --- a/sqlc-core/Cargo.toml +++ b/sqlc-core/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] postgres = "0.19.9" +postgres-types = "0.2.8" diff --git a/sqlc-gen/examples/authors/postgresql/gen.rs b/sqlc-gen/examples/authors/postgresql/gen.rs index c551fd4..6adbd7e 100644 --- a/sqlc-gen/examples/authors/postgresql/gen.rs +++ b/sqlc-gen/examples/authors/postgresql/gen.rs @@ -54,10 +54,7 @@ impl Queries { let row = self.client.query_one(GET_AUTHOR, &[&id])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } - pub(crate) fn list_authors( - &mut self, - arg: ListAuthorsParams, - ) -> anyhow::Result> { + pub(crate) fn list_authors(&mut self) -> anyhow::Result> { let rows = self.client.query(LIST_AUTHORS, &[])?; let mut result: Vec = vec![]; for row in rows { diff --git a/sqlc-gen/examples/authors/sqlc.yaml b/sqlc-gen/examples/authors/sqlc.yaml index d41d8a7..1318b2b 100644 --- a/sqlc-gen/examples/authors/sqlc.yaml +++ b/sqlc-gen/examples/authors/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f + sha256: 7c02055c3eba7bcb913da0e9090541314d14da8c350b34891c4a2e5826c23307 # # - name: js # process: diff --git a/sqlc-gen/examples/batch/postgresql/gen.rs b/sqlc-gen/examples/batch/postgresql/gen.rs index 8bd3e8d..3dc95d0 100644 --- a/sqlc-gen/examples/batch/postgresql/gen.rs +++ b/sqlc-gen/examples/batch/postgresql/gen.rs @@ -24,7 +24,7 @@ pub(crate) struct Book { pub book_id: i32, pub author_id: i32, pub isbn: String, - pub book_type: String, + pub book_type: BookType, pub title: String, pub year: i32, pub available: String, diff --git a/sqlc-gen/examples/batch/sqlc.json b/sqlc-gen/examples/batch/sqlc.json index e734499..038d09d 100644 --- a/sqlc-gen/examples/batch/sqlc.json +++ b/sqlc-gen/examples/batch/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f" + "sha256": "7c02055c3eba7bcb913da0e9090541314d14da8c350b34891c4a2e5826c23307" } } ], diff --git a/sqlc-gen/examples/booktest/gen/gen.rs b/sqlc-gen/examples/booktest/gen/gen.rs index b171254..990a6a5 100644 --- a/sqlc-gen/examples/booktest/gen/gen.rs +++ b/sqlc-gen/examples/booktest/gen/gen.rs @@ -81,7 +81,7 @@ pub(crate) struct Book { pub book_id: i32, pub author_id: i32, pub isbn: String, - pub book_type: String, + pub book_type: BookType, pub title: String, pub year: i32, pub available: String, @@ -104,7 +104,7 @@ pub(crate) struct BooksByTitleYearParams { pub(crate) struct CreateBookParams { pub author_id: i32, pub isbn: String, - pub book_type: String, + pub book_type: BookType, pub title: String, pub year: i32, pub available: String, diff --git a/sqlc-gen/examples/booktest/sqlc.yaml b/sqlc-gen/examples/booktest/sqlc.yaml index 71dd1aa..c15ed32 100644 --- a/sqlc-gen/examples/booktest/sqlc.yaml +++ b/sqlc-gen/examples/booktest/sqlc.yaml @@ -6,7 +6,7 @@ plugins: - RUST_LOG wasm: url: file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm - sha256: 0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f + sha256: 7c02055c3eba7bcb913da0e9090541314d14da8c350b34891c4a2e5826c23307 # # - name: js # process: diff --git a/sqlc-gen/examples/jets/postgresql/gen.rs b/sqlc-gen/examples/jets/postgresql/gen.rs index d77ff27..6925325 100644 --- a/sqlc-gen/examples/jets/postgresql/gen.rs +++ b/sqlc-gen/examples/jets/postgresql/gen.rs @@ -34,10 +34,7 @@ impl Queries { pub fn new(client: postgres::Client) -> Self { Self { client } } - pub(crate) fn count_pilots( - &mut self, - arg: CountPilotsParams, - ) -> anyhow::Result { + pub(crate) fn count_pilots(&mut self) -> anyhow::Result { let row = self.client.query_one(COUNT_PILOTS, &[])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } @@ -45,10 +42,7 @@ impl Queries { self.client.execute(DELETE_PILOT, &[&id])?; Ok(()) } - pub(crate) fn list_pilots( - &mut self, - arg: ListPilotsParams, - ) -> anyhow::Result> { + pub(crate) fn list_pilots(&mut self) -> anyhow::Result> { let rows = self.client.query(LIST_PILOTS, &[])?; let mut result: Vec = vec![]; for row in rows { diff --git a/sqlc-gen/examples/jets/sqlc.json b/sqlc-gen/examples/jets/sqlc.json index c09db02..26f2df0 100644 --- a/sqlc-gen/examples/jets/sqlc.json +++ b/sqlc-gen/examples/jets/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f" + "sha256": "7c02055c3eba7bcb913da0e9090541314d14da8c350b34891c4a2e5826c23307" } } ], diff --git a/sqlc-gen/examples/ondeck/postgresql/gen.rs b/sqlc-gen/examples/ondeck/postgresql/gen.rs index f4ef91c..8818f05 100644 --- a/sqlc-gen/examples/ondeck/postgresql/gen.rs +++ b/sqlc-gen/examples/ondeck/postgresql/gen.rs @@ -98,8 +98,8 @@ pub(crate) struct CreateVenueParams { pub name: String, pub city: String, pub spotify_playlist: String, - pub status: String, - pub statuses: Option>, + pub status: Status, + pub statuses: Option>, pub tags: Option>, } #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] @@ -120,8 +120,8 @@ pub(crate) struct UpdateVenueNameParams { #[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] pub(crate) struct Venue { pub id: i32, - pub status: String, - pub statuses: Option>, + pub status: Status, + pub statuses: Option>, pub slug: String, pub name: String, pub city: String, @@ -178,10 +178,7 @@ impl Queries { let row = self.client.query_one(GET_VENUE, &[&arg.slug, &arg.city])?; Ok(sqlc_core::FromPostgresRow::from_row(&row)?) } - pub(crate) fn list_cities( - &mut self, - arg: ListCitiesParams, - ) -> anyhow::Result> { + pub(crate) fn list_cities(&mut self) -> anyhow::Result> { let rows = self.client.query(LIST_CITIES, &[])?; let mut result: Vec = vec![]; for row in rows { @@ -213,7 +210,6 @@ impl Queries { } pub(crate) fn venue_count_by_city( &mut self, - arg: VenueCountByCityParams, ) -> anyhow::Result> { let rows = self.client.query(VENUE_COUNT_BY_CITY, &[])?; let mut result: Vec = vec![]; diff --git a/sqlc-gen/examples/ondeck/sqlc.json b/sqlc-gen/examples/ondeck/sqlc.json index 67939bc..4f58f4f 100644 --- a/sqlc-gen/examples/ondeck/sqlc.json +++ b/sqlc-gen/examples/ondeck/sqlc.json @@ -9,7 +9,7 @@ ], "wasm": { "url": "file://./../../../target/wasm32-wasi/release/sqlc-gen.wasm", - "sha256": "0965b02d6f9db9337fee88e39b45e51751a277a3159ba790bf418b8df451ee9f" + "sha256": "7c02055c3eba7bcb913da0e9090541314d14da8c350b34891c4a2e5826c23307" } } ], diff --git a/sqlc-gen/src/codegen/mod.rs b/sqlc-gen/src/codegen/mod.rs index 4c8c85c..01450ce 100644 --- a/sqlc-gen/src/codegen/mod.rs +++ b/sqlc-gen/src/codegen/mod.rs @@ -9,7 +9,7 @@ use std::hash::Hash; use std::str::FromStr; use syn::Ident; use type_const::TypeConst; -use type_enum::TypeEnum; +use type_enum::{enum_name, TypeEnum}; use type_query::{QueryCommand, QueryValue, TypeQuery}; use type_struct::{StructField, StructType, TypeStruct}; @@ -169,48 +169,72 @@ impl ToTokens for PgDataType { } } -impl fmt::Display for PgDataType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let ident_str = match self.0.as_str() { +impl PgDataType { + pub fn from(s: &str, schemas: Vec, default_schema: String) -> PgDataType { + let pg_data_type_string = match s { "smallint" | "int2" | "pg_catalog.int2" | "smallserial" | "serial2" - | "pg_catalog.serial2" => "i16", + | "pg_catalog.serial2" => "i16".to_string(), "integer" | "int" | "int4" | "pg_catalog.int4" | "serial" | "serial4" - | "pg_catalog.serial4" => "i32", + | "pg_catalog.serial4" => "i32".to_string(), "bigint" | "int8" | "pg_catalog.int8" | "bigserial" | "serial8" - | "pg_catalog.serial8" => "i64", + | "pg_catalog.serial8" => "i64".to_string(), - "real" | "float4" | "pg_catalog.float4" => "f32", - "float" | "double precision" | "float8" | "pg_catalog.float8" => "f64", + "real" | "float4" | "pg_catalog.float4" => "f32".to_string(), + "float" | "double precision" | "float8" | "pg_catalog.float8" => "f64".to_string(), // "numeric" | "pg_catalog.numeric" | "money" => "", - "boolean" | "bool" | "pg_catalog.bool" => "bool", + "boolean" | "bool" | "pg_catalog.bool" => "bool".to_string(), - "json" | "jsonb" => "serde_json::Value", + "json" | "jsonb" => "serde_json::Value".to_string(), - "bytea" | "blob" | "pg_catalog.bytea" => "Vec", + "bytea" | "blob" | "pg_catalog.bytea" => "Vec".to_string(), - "date" => "time::Date", + "date" => "time::Date".to_string(), - "pg_catalog.time" | "pg_catalog.timez" => "time::Time", + "pg_catalog.time" | "pg_catalog.timez" => "time::Time".to_string(), - "pg_catalog.timestamp" => "time::PrimitiveDateTime", - "pg_catalog.timestampz" | "timestampz" => "time::PrimitiveDateTime", + "pg_catalog.timestamp" => "time::PrimitiveDateTime".to_string(), + "pg_catalog.timestampz" | "timestampz" => "time::PrimitiveDateTime".to_string(), - // "interval" | "pg_catalog.interval" => "", + "interval" | "pg_catalog.interval" => "i64".to_string(), "text" | "pg_catalog.varchar" | "pg_catalog.bpchar" | "string" | "citext" | "ltree" - | "lquery" | "ltxtquery" => "String", - - "uuid" => "uuid::Uuid", - "inet" => "cidr::InetCidr", - "cidr" => "cidr::InetAddr", - "macaddr" | "macaddr8" => "eui48::MacAddress", + | "lquery" | "ltxtquery" => "String".to_string(), + + "uuid" => "uuid::Uuid".to_string(), + "inet" => "cidr::InetCidr".to_string(), + "cidr" => "cidr::InetAddr".to_string(), + "macaddr" | "macaddr8" => "eui48::MacAddress".to_string(), + + _ => { + let res = schemas.into_iter().find_map(|schema| { + if schema.name == "pg_catalog" || schema.name == "information_schema" { + None + } else if let Some(matching_enum) = + schema.enums.clone().into_iter().find(|e| s == e.name) + { + Some((matching_enum, schema)) + } else { + None + } + }); - _ => "String", + if let Some((matching_enum, schema)) = res { + enum_name(&matching_enum.name, &schema.name, &default_schema) + } else { + "String".to_string() + } + } }; - f.write_str(ident_str) + PgDataType(pg_data_type_string) + } +} + +impl fmt::Display for PgDataType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) } } @@ -242,10 +266,8 @@ impl CodeBuilder { .clone() .into_iter() .map(|e| { - let mut enum_name = e.name; - if schema.name != catalog.default_schema { - enum_name = format!("{}_{enum_name}", schema.name); - } + let enum_name = + enum_name(&e.name, &schema.name, &catalog.default_schema); TypeEnum::new(enum_name, e.vals) }) @@ -295,12 +317,11 @@ impl CodeBuilder { .columns .into_iter() .map(|col| { - StructField::new( - col.name, + StructField::from( + col, 0, - PgDataType(col.r#type.unwrap().name), - col.is_array, - col.not_null, + vec![schema.clone()], + catalog.default_schema.clone(), ) }) .collect::>(); @@ -331,6 +352,8 @@ impl CodeBuilder { fn build_queries(&self, structs: Vec) -> (Vec, Vec) { let catalog = self.req.catalog.clone().unwrap(); + let schemas = catalog.schemas.clone(); + let default_schema = catalog.default_schema.clone(); let mut associated_structs = vec![]; let mut queries = self .req @@ -343,44 +366,50 @@ impl CodeBuilder { } else { // Query parameter limit, get it from the options let qpl = 3; - let arg: QueryValue; + let mut arg: Option = None; let params = query.params.clone(); if params.len() == 1 && qpl != 0 { let p = params.first().unwrap(); let col = p.column.clone().unwrap(); - arg = QueryValue::new( + arg = Some(QueryValue::new( escape(¶m_name(p)), - Some(col.r#type.unwrap().name.to_string()), + Some(PgDataType::from( + col.r#type.unwrap().name.as_str(), + schemas.clone(), + default_schema.clone(), + )), None, - ); - } else { + )); + } else if params.len() > 1 { let fields = params .into_iter() .map(|field| { - let col = field.column.unwrap(); - StructField { - name: col.name, - number: field.number, - is_array: col.is_array, - not_null: col.not_null, - data_type: PgDataType(col.r#type.unwrap().name), - } + StructField::from( + field.column.unwrap(), + field.number, + catalog.schemas.clone(), + catalog.default_schema.clone(), + ) }) .collect::>(); let type_struct = TypeStruct::new(query.name.clone(), None, StructType::Params, fields); - arg = QueryValue::new("arg", None, Some(type_struct.clone())); + arg = Some(QueryValue::new("arg", None, Some(type_struct.clone()))); associated_structs.push(type_struct); } let columns = query.columns.clone(); let mut ret: Option = None; if columns.len() == 1 { - let c = columns.first().unwrap(); + let col = columns.first().unwrap(); ret = Some(QueryValue::new( "", - Some(c.r#type.clone().unwrap().name.to_string()), + Some(PgDataType::from( + col.r#type.clone().unwrap().name.as_str(), + schemas.clone(), + default_schema.clone(), + )), None, )); } else if QueryCommand::from_str(&query.cmd) @@ -391,22 +420,31 @@ impl CodeBuilder { if s.fields.len() != columns.len() { false } else { - s.fields.clone().into_iter().enumerate().all(|(i, field)| { - let c = columns.get(i).unwrap(); - let same_name = - field.name() == column_name(c.name.to_string(), i as i32); - - let same_type = field.data_type.to_string() - == PgDataType(c.r#type.clone().unwrap().name).to_string(); - - let same_table = same_table( - c.table.clone(), - s.table.clone(), - catalog.default_schema.clone(), - ); - - same_name && same_type && same_table - }) + s.fields + .clone() + .into_iter() + .zip(columns.clone().into_iter()) + .enumerate() + .all(|(i, (field, c))| { + let same_name = field.name() + == column_name(c.name.to_string(), i as i32); + + let same_type = field.data_type.to_string() + == PgDataType::from( + c.r#type.clone().unwrap().name.as_str(), + schemas.clone(), + default_schema.clone(), + ) + .to_string(); + + let same_table = same_table( + c.table.clone(), + s.table.clone(), + default_schema.clone(), + ); + + same_name && same_type && same_table + }) } }); @@ -415,12 +453,13 @@ impl CodeBuilder { let fields = columns .into_iter() .enumerate() - .map(|(i, col)| StructField { - name: col.name, - number: i as i32, - is_array: col.is_array, - not_null: col.not_null, - data_type: PgDataType(col.r#type.unwrap().name), + .map(|(i, col)| { + StructField::from( + col, + i as i32, + schemas.clone(), + default_schema.clone(), + ) }) .collect::>(); diff --git a/sqlc-gen/src/codegen/type_enum.rs b/sqlc-gen/src/codegen/type_enum.rs index 6d3ea43..6d88de5 100644 --- a/sqlc-gen/src/codegen/type_enum.rs +++ b/sqlc-gen/src/codegen/type_enum.rs @@ -5,6 +5,14 @@ use convert_case::{Case, Casing}; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; +pub fn enum_name(name: &str, schema_name: &str, default_schema: &str) -> String { + match schema_name == default_schema { + true => name.to_string(), + false => format!("{}_{name}", schema_name), + } + .to_case(Case::Pascal) +} + fn enum_replacer(c: char) -> Option { if ['-', '/', ':', '_'].contains(&c) { Some('_') @@ -46,7 +54,6 @@ impl TypeEnum { self.name.to_case(Case::Pascal) } - /// TODO: add #[postgres(name = "")] to generated enum variant fn generate_code(&self) -> TokenStream { let ident_enum_name = get_ident(&self.name()); let mut seen = HashSet::new(); diff --git a/sqlc-gen/src/codegen/type_query.rs b/sqlc-gen/src/codegen/type_query.rs index d469ddf..c36bb97 100644 --- a/sqlc-gen/src/codegen/type_query.rs +++ b/sqlc-gen/src/codegen/type_query.rs @@ -32,17 +32,17 @@ impl QueryCommand { } } -#[derive(Default, Clone)] +#[derive(Default, Debug, Clone)] pub struct QueryValue { name: String, - typ: Option, + typ: Option, type_struct: Option, } impl QueryValue { pub fn new>( name: S, - typ: Option, + typ: Option, type_struct: Option, ) -> Self { Self { @@ -54,7 +54,7 @@ impl QueryValue { pub fn get_type(&self) -> String { if let Some(typ) = &self.typ { - PgDataType(typ.to_string()).to_string() + typ.to_string() } else if let Some(type_struct) = self.type_struct.clone() { type_struct.name() } else { @@ -63,11 +63,12 @@ impl QueryValue { } pub fn generate_fields_list(&self) -> TokenStream { - let ident_name = get_ident(&self.name.clone()); let mut fields_list = quote! {}; if self.typ.is_some() { + let ident_name = get_ident(&self.name.clone()); fields_list = quote! { &#ident_name }; } else if let Some(type_struct) = self.type_struct.clone() { + let ident_name = get_ident(&self.name.clone()); let fields = type_struct .fields .clone() @@ -89,17 +90,19 @@ impl QueryValue { impl ToTokens for QueryValue { fn to_tokens(&self, tokens: &mut TokenStream) { - let ident_type = get_ident(&self.get_type()); - if !self.name.is_empty() { + let ident_type = get_ident(&self.get_type()); let ident_name = get_ident(&self.name); tokens.extend(quote! { #ident_name: #ident_type }); - } else { + } else if self.typ.is_some() || self.type_struct.is_some() { + let ident_type = get_ident(&self.get_type()); tokens.extend(quote! { #ident_type }); + } else { + tokens.extend(quote! {}); } } } @@ -108,12 +111,17 @@ impl ToTokens for QueryValue { pub struct TypeQuery { name: String, cmd: String, - arg: QueryValue, + arg: Option, ret: Option, } impl TypeQuery { - pub fn new>(name: S, cmd: S, arg: QueryValue, ret: Option) -> Self { + pub fn new>( + name: S, + cmd: S, + arg: Option, + ret: Option, + ) -> Self { Self { name: name.into(), cmd: cmd.into(), @@ -137,11 +145,10 @@ impl TypeQuery { fn generate_code(&self) -> TokenStream { let ident_name = get_ident(&self.name()); let ident_const_name = get_ident(&self.constant_name()); - let fields_list = self.arg.generate_fields_list(); + let fields_list = self.arg.clone().unwrap_or_default().generate_fields_list(); let command = self.command(); - let arg = &self.arg; + let arg = self.arg.clone().unwrap_or_default(); - // let params_arg = self.params_struct.get_as_arg(Some("params")); let query_method = match command { QueryCommand::One => { let ret = self.ret.clone().unwrap_or_default(); diff --git a/sqlc-gen/src/codegen/type_struct.rs b/sqlc-gen/src/codegen/type_struct.rs index ef5a98d..6b4601f 100644 --- a/sqlc-gen/src/codegen/type_struct.rs +++ b/sqlc-gen/src/codegen/type_struct.rs @@ -32,6 +32,21 @@ impl StructField { } } + pub fn from( + col: plugin::Column, + pos: i32, + schemas: Vec, + default_schema: String, + ) -> Self { + Self::new( + col.name, + pos, + PgDataType::from(&col.r#type.unwrap().name, schemas, default_schema), + col.is_array, + col.not_null, + ) + } + pub fn name(&self) -> String { column_name(self.name.clone(), self.number) } @@ -74,11 +89,6 @@ pub enum StructType { Default, Params, Row, - Queries, - Other { - prefix: String, - suffix: String, - }, } #[derive(Default, Debug, Clone)] @@ -109,10 +119,6 @@ impl TypeStruct { StructType::Default => format!("{}", self.name), StructType::Params => format!("{}Params", self.name), StructType::Row => format!("{}Row", self.name), - StructType::Queries => self.name.clone(), - StructType::Other { prefix, suffix } => { - format!("{}{}{}", prefix, self.name.to_case(Case::Pascal), suffix) - } }; name.to_case(Case::Pascal) @@ -155,7 +161,7 @@ mod tests { StructField::new( name.unwrap_or(""), number.unwrap_or(0), - data_type.unwrap_or(PgDataType("pg_catalog.int4".to_string())), + data_type.unwrap_or(PgDataType::from("pg_catalog.int4", vec![], "".to_string())), is_array.unwrap_or_default(), not_null.unwrap_or_default(), ) @@ -242,22 +248,6 @@ mod tests { create_type_struct(None, Some(StructType::Row), None).name(), "StructNameRow".to_string() ); - assert_eq!( - create_type_struct(None, Some(StructType::Queries), None).name(), - "StructName".to_string() - ); - assert_eq!( - create_type_struct( - None, - Some(StructType::Other { - prefix: "A".to_string(), - suffix: "B".to_string() - }), - None - ) - .name(), - "AStructNameB".to_string() - ); } #[test] From 9cb4b6ab2ac978069e8c65d0c94bf706c050325a Mon Sep 17 00:00:00 2001 From: ES Date: Sat, 21 Sep 2024 15:29:27 +0530 Subject: [PATCH 6/6] redundant coed --- examples/authors/src/db/gen.bk.rs | 91 ------------------------------- 1 file changed, 91 deletions(-) delete mode 100644 examples/authors/src/db/gen.bk.rs diff --git a/examples/authors/src/db/gen.bk.rs b/examples/authors/src/db/gen.bk.rs deleted file mode 100644 index 99930ff..0000000 --- a/examples/authors/src/db/gen.bk.rs +++ /dev/null @@ -1,91 +0,0 @@ -/// @generated by the sqlc-gen-rust on sqlc-generate using sqlc.yaml -/// DO NOT EDIT. -use postgres::{Error, Row}; -const GET_AUTHOR: &str = r#" -SELECT id, name, bio FROM authors -WHERE id = $1 LIMIT 1 -"#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorParams { - pub id: i64, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct GetAuthorRow { - pub id: i64, - pub name: String, - pub bio: Option, -} -const LIST_AUTHORS: &str = r#" -SELECT id, name, bio FROM authors -ORDER BY name -"#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct ListAuthorsRow { - pub id: i64, - pub name: String, - pub bio: Option, -} -const CREATE_AUTHOR: &str = r#" -INSERT INTO authors ( - name, bio -) VALUES ( - $1, $2 -) -RETURNING id, name, bio -"#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorParams { - pub name: String, - pub bio: Option, -} -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct CreateAuthorRow { - pub id: i64, - pub name: String, - pub bio: Option, -} -const DELETE_AUTHOR: &str = r#" -DELETE FROM authors -WHERE id = $1 -"#; -#[derive(Clone, Debug, sqlc_derive::FromPostgresRow, PartialEq)] -pub(crate) struct DeleteAuthorParams { - pub id: i64, -} -pub struct Queries { - client: postgres::Client, -} -impl Queries { - pub fn new(client: postgres::Client) -> Self { - Self { client } - } - pub(crate) fn get_author( - &mut self, - params: GetAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(GET_AUTHOR, &[¶ms.id])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn list_authors(&mut self) -> anyhow::Result> { - let rows = self.client.query(LIST_AUTHORS, &[])?; - let mut result: Vec = vec![]; - for row in rows { - result.push(sqlc_core::FromPostgresRow::from_row(&row)?); - } - Ok(result) - } - pub(crate) fn create_author( - &mut self, - params: CreateAuthorParams, - ) -> anyhow::Result { - let row = self.client.query_one(CREATE_AUTHOR, &[¶ms.name, ¶ms.bio])?; - Ok(sqlc_core::FromPostgresRow::from_row(&row)?) - } - pub(crate) fn delete_author( - &mut self, - params: DeleteAuthorParams, - ) -> anyhow::Result<()> { - self.client.execute(DELETE_AUTHOR, &[¶ms.id])?; - Ok(()) - } -}