From 5135425e3ebe480e9dcb5bda4198e091401fd90f Mon Sep 17 00:00:00 2001 From: JasonShin Date: Tue, 18 Nov 2025 21:41:33 +1100 Subject: [PATCH 1/3] delete and update return handling --- src/ts_generator/sql_parser/translate_stmt.rs | 27 ++++++++-- tests/demo/delete/delete_returning.queries.ts | 50 +++++++++++++++++++ .../demo/delete/delete_returning.snapshot.ts | 50 +++++++++++++++++++ tests/demo/delete/delete_returning.ts | 30 +++++++++++ tests/demo/update/update_returning.queries.ts | 50 +++++++++++++++++++ .../demo/update/update_returning.snapshot.ts | 50 +++++++++++++++++++ tests/demo/update/update_returning.ts | 34 +++++++++++++ 7 files changed, 287 insertions(+), 4 deletions(-) create mode 100644 tests/demo/delete/delete_returning.queries.ts create mode 100644 tests/demo/delete/delete_returning.snapshot.ts create mode 100644 tests/demo/delete/delete_returning.ts create mode 100644 tests/demo/update/update_returning.queries.ts create mode 100644 tests/demo/update/update_returning.snapshot.ts create mode 100644 tests/demo/update/update_returning.ts diff --git a/src/ts_generator/sql_parser/translate_stmt.rs b/src/ts_generator/sql_parser/translate_stmt.rs index 331a63f7..7f4a5ab0 100644 --- a/src/ts_generator/sql_parser/translate_stmt.rs +++ b/src/ts_generator/sql_parser/translate_stmt.rs @@ -36,15 +36,15 @@ pub async fn translate_stmt( } }; - let table_name_str = table_name.as_str(); + let table_name = table_name.as_str(); let query_for_logging = sql_statement.to_string(); let query_for_logging_str = &query_for_logging.as_str(); - translate_insert(ts_query, &insert.columns, &source, table_name_str, db_conn).await?; + translate_insert(ts_query, &insert.columns, &source, table_name, db_conn).await?; if insert.returning.is_some() { let returning = insert.returning.clone().unwrap(); - translate_insert_returning(ts_query, &returning, table_name_str, db_conn, query_for_logging_str).await?; + translate_insert_returning(ts_query, &returning, table_name, db_conn, query_for_logging_str).await?; } } Statement::Delete(delete) => match &delete.from { @@ -53,6 +53,14 @@ pub async fn translate_stmt( let table_name = table_name.as_str(); let selection = delete.selection.to_owned().unwrap(); translate_delete(ts_query, &selection, table_name, db_conn).await?; + + // Handle RETURNING clause if present + if delete.returning.is_some() { + let returning = delete.returning.clone().unwrap(); + let query_for_logging = sql_statement.to_string(); + let query_for_logging_str = &query_for_logging.as_str(); + translate_insert_returning(ts_query, &returning, table_name, db_conn, query_for_logging_str).await?; + } } FromTable::WithoutKeyword(_) => Err(TsGeneratorError::FromWithoutKeyword(sql_statement.to_string()))?, }, @@ -61,7 +69,7 @@ pub async fn translate_stmt( assignments, from, selection, - returning: _, + returning, or: _, limit: _, } => { @@ -79,6 +87,17 @@ pub async fn translate_stmt( }; translate_update(ts_query, table, assignments, &from_table, selection, db_conn).await?; + + // Handle RETURNING clause if present + if returning.is_some() { + let returning_items = returning.clone().unwrap(); + // Extract table name from TableWithJoins + let table_name = get_default_table(&vec![table.clone()]); + let table_name_str = table_name.as_str(); + let query_for_logging = sql_statement.to_string(); + let query_for_logging_str = &query_for_logging.as_str(); + translate_insert_returning(ts_query, &returning_items, table_name_str, db_conn, query_for_logging_str).await?; + } } _ => {} } diff --git a/tests/demo/delete/delete_returning.queries.ts b/tests/demo/delete/delete_returning.queries.ts new file mode 100644 index 00000000..a43bc19b --- /dev/null +++ b/tests/demo/delete/delete_returning.queries.ts @@ -0,0 +1,50 @@ +export type DeleteReturningAllParams = [number]; + +export interface IDeleteReturningAllResult { + flavorText: string | null; + id: number; + inventoryId: number | null; + name: string; + rarity: string | null; +} + +export interface IDeleteReturningAllQuery { + params: DeleteReturningAllParams; + result: IDeleteReturningAllResult; +} + +export type DeleteReturningSpecificParams = [number]; + +export interface IDeleteReturningSpecificResult { + id: number; + name: string; +} + +export interface IDeleteReturningSpecificQuery { + params: DeleteReturningSpecificParams; + result: IDeleteReturningSpecificResult; +} + +export type DeleteReturningWithAliasParams = [number]; + +export interface IDeleteReturningWithAliasResult { + deletedId: number; + deletedName: string; +} + +export interface IDeleteReturningWithAliasQuery { + params: DeleteReturningWithAliasParams; + result: IDeleteReturningWithAliasResult; +} + +export type DeleteReturningExpressionParams = [number]; + +export interface IDeleteReturningExpressionResult { + id: number; + upperName: string; +} + +export interface IDeleteReturningExpressionQuery { + params: DeleteReturningExpressionParams; + result: IDeleteReturningExpressionResult; +} diff --git a/tests/demo/delete/delete_returning.snapshot.ts b/tests/demo/delete/delete_returning.snapshot.ts new file mode 100644 index 00000000..a43bc19b --- /dev/null +++ b/tests/demo/delete/delete_returning.snapshot.ts @@ -0,0 +1,50 @@ +export type DeleteReturningAllParams = [number]; + +export interface IDeleteReturningAllResult { + flavorText: string | null; + id: number; + inventoryId: number | null; + name: string; + rarity: string | null; +} + +export interface IDeleteReturningAllQuery { + params: DeleteReturningAllParams; + result: IDeleteReturningAllResult; +} + +export type DeleteReturningSpecificParams = [number]; + +export interface IDeleteReturningSpecificResult { + id: number; + name: string; +} + +export interface IDeleteReturningSpecificQuery { + params: DeleteReturningSpecificParams; + result: IDeleteReturningSpecificResult; +} + +export type DeleteReturningWithAliasParams = [number]; + +export interface IDeleteReturningWithAliasResult { + deletedId: number; + deletedName: string; +} + +export interface IDeleteReturningWithAliasQuery { + params: DeleteReturningWithAliasParams; + result: IDeleteReturningWithAliasResult; +} + +export type DeleteReturningExpressionParams = [number]; + +export interface IDeleteReturningExpressionResult { + id: number; + upperName: string; +} + +export interface IDeleteReturningExpressionQuery { + params: DeleteReturningExpressionParams; + result: IDeleteReturningExpressionResult; +} diff --git a/tests/demo/delete/delete_returning.ts b/tests/demo/delete/delete_returning.ts new file mode 100644 index 00000000..ed7d932c --- /dev/null +++ b/tests/demo/delete/delete_returning.ts @@ -0,0 +1,30 @@ +import { sql } from 'sqlx-ts' + +// Issue #226: DELETE with RETURNING clause should generate types +const deleteReturningAll = sql` +-- @name: delete returning all +DELETE FROM items +WHERE id = $1 +RETURNING * +` + +const deleteReturningSpecific = sql` +-- @name: delete returning specific +DELETE FROM items +WHERE id = $1 +RETURNING id, name +` + +const deleteReturningWithAlias = sql` +-- @name: delete returning with alias +DELETE FROM items +WHERE id = $1 +RETURNING id AS deleted_id, name AS deleted_name +` + +const deleteReturningExpression = sql` +-- @name: delete returning expression +DELETE FROM items +WHERE id = $1 +RETURNING id, UPPER(name) AS upper_name +` diff --git a/tests/demo/update/update_returning.queries.ts b/tests/demo/update/update_returning.queries.ts new file mode 100644 index 00000000..56a965ee --- /dev/null +++ b/tests/demo/update/update_returning.queries.ts @@ -0,0 +1,50 @@ +export type UpdateReturningAllParams = [string, number]; + +export interface IUpdateReturningAllResult { + flavorText: string | null; + id: number; + inventoryId: number | null; + name: string; + rarity: string | null; +} + +export interface IUpdateReturningAllQuery { + params: UpdateReturningAllParams; + result: IUpdateReturningAllResult; +} + +export type UpdateReturningSpecificParams = [string, number]; + +export interface IUpdateReturningSpecificResult { + id: number; + name: string; +} + +export interface IUpdateReturningSpecificQuery { + params: UpdateReturningSpecificParams; + result: IUpdateReturningSpecificResult; +} + +export type UpdateReturningWithAliasParams = [string, number]; + +export interface IUpdateReturningWithAliasResult { + updatedId: number; + updatedName: string; +} + +export interface IUpdateReturningWithAliasQuery { + params: UpdateReturningWithAliasParams; + result: IUpdateReturningWithAliasResult; +} + +export type UpdateReturningExpressionParams = [string, number]; + +export interface IUpdateReturningExpressionResult { + id: number; + lowerName: string; +} + +export interface IUpdateReturningExpressionQuery { + params: UpdateReturningExpressionParams; + result: IUpdateReturningExpressionResult; +} diff --git a/tests/demo/update/update_returning.snapshot.ts b/tests/demo/update/update_returning.snapshot.ts new file mode 100644 index 00000000..56a965ee --- /dev/null +++ b/tests/demo/update/update_returning.snapshot.ts @@ -0,0 +1,50 @@ +export type UpdateReturningAllParams = [string, number]; + +export interface IUpdateReturningAllResult { + flavorText: string | null; + id: number; + inventoryId: number | null; + name: string; + rarity: string | null; +} + +export interface IUpdateReturningAllQuery { + params: UpdateReturningAllParams; + result: IUpdateReturningAllResult; +} + +export type UpdateReturningSpecificParams = [string, number]; + +export interface IUpdateReturningSpecificResult { + id: number; + name: string; +} + +export interface IUpdateReturningSpecificQuery { + params: UpdateReturningSpecificParams; + result: IUpdateReturningSpecificResult; +} + +export type UpdateReturningWithAliasParams = [string, number]; + +export interface IUpdateReturningWithAliasResult { + updatedId: number; + updatedName: string; +} + +export interface IUpdateReturningWithAliasQuery { + params: UpdateReturningWithAliasParams; + result: IUpdateReturningWithAliasResult; +} + +export type UpdateReturningExpressionParams = [string, number]; + +export interface IUpdateReturningExpressionResult { + id: number; + lowerName: string; +} + +export interface IUpdateReturningExpressionQuery { + params: UpdateReturningExpressionParams; + result: IUpdateReturningExpressionResult; +} diff --git a/tests/demo/update/update_returning.ts b/tests/demo/update/update_returning.ts new file mode 100644 index 00000000..392a8cf6 --- /dev/null +++ b/tests/demo/update/update_returning.ts @@ -0,0 +1,34 @@ +import { sql } from 'sqlx-ts' + +// Issue #226: UPDATE with RETURNING clause should generate types +const updateReturningAll = sql` +-- @name: update returning all +UPDATE items +SET name = $1 +WHERE id = $2 +RETURNING * +` + +const updateReturningSpecific = sql` +-- @name: update returning specific +UPDATE items +SET name = $1 +WHERE id = $2 +RETURNING id, name +` + +const updateReturningWithAlias = sql` +-- @name: update returning with alias +UPDATE items +SET name = $1 +WHERE id = $2 +RETURNING id AS updated_id, name AS updated_name +` + +const updateReturningExpression = sql` +-- @name: update returning expression +UPDATE items +SET name = $1 +WHERE id = $2 +RETURNING id, LOWER(name) AS lower_name +` From 309d3f44372e2aee756d4d8357556142024a711a Mon Sep 17 00:00:00 2001 From: JasonShin Date: Tue, 18 Nov 2025 21:42:07 +1100 Subject: [PATCH 2/3] revert --- src/ts_generator/sql_parser/translate_stmt.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ts_generator/sql_parser/translate_stmt.rs b/src/ts_generator/sql_parser/translate_stmt.rs index 7f4a5ab0..e94381a1 100644 --- a/src/ts_generator/sql_parser/translate_stmt.rs +++ b/src/ts_generator/sql_parser/translate_stmt.rs @@ -36,30 +36,30 @@ pub async fn translate_stmt( } }; - let table_name = table_name.as_str(); + let table_name_str = table_name.as_str(); let query_for_logging = sql_statement.to_string(); let query_for_logging_str = &query_for_logging.as_str(); - translate_insert(ts_query, &insert.columns, &source, table_name, db_conn).await?; + translate_insert(ts_query, &insert.columns, &source, table_name_str, db_conn).await?; if insert.returning.is_some() { let returning = insert.returning.clone().unwrap(); - translate_insert_returning(ts_query, &returning, table_name, db_conn, query_for_logging_str).await?; + translate_insert_returning(ts_query, &returning, table_name_str, db_conn, query_for_logging_str).await?; } } Statement::Delete(delete) => match &delete.from { FromTable::WithFromKeyword(from) => { let table_name = get_default_table(from); - let table_name = table_name.as_str(); + let table_name_str = table_name.as_str(); let selection = delete.selection.to_owned().unwrap(); - translate_delete(ts_query, &selection, table_name, db_conn).await?; + translate_delete(ts_query, &selection, table_name_str, db_conn).await?; // Handle RETURNING clause if present if delete.returning.is_some() { let returning = delete.returning.clone().unwrap(); let query_for_logging = sql_statement.to_string(); let query_for_logging_str = &query_for_logging.as_str(); - translate_insert_returning(ts_query, &returning, table_name, db_conn, query_for_logging_str).await?; + translate_insert_returning(ts_query, &returning, table_name_str, db_conn, query_for_logging_str).await?; } } FromTable::WithoutKeyword(_) => Err(TsGeneratorError::FromWithoutKeyword(sql_statement.to_string()))?, From 6b759c8f58bc383f70e9eabd272da28cf9b4157f Mon Sep 17 00:00:00 2001 From: JasonShin Date: Tue, 18 Nov 2025 21:49:28 +1100 Subject: [PATCH 3/3] fmt --- src/ts_generator/sql_parser/translate_stmt.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ts_generator/sql_parser/translate_stmt.rs b/src/ts_generator/sql_parser/translate_stmt.rs index e94381a1..f967500d 100644 --- a/src/ts_generator/sql_parser/translate_stmt.rs +++ b/src/ts_generator/sql_parser/translate_stmt.rs @@ -96,7 +96,14 @@ pub async fn translate_stmt( let table_name_str = table_name.as_str(); let query_for_logging = sql_statement.to_string(); let query_for_logging_str = &query_for_logging.as_str(); - translate_insert_returning(ts_query, &returning_items, table_name_str, db_conn, query_for_logging_str).await?; + translate_insert_returning( + ts_query, + &returning_items, + table_name_str, + db_conn, + query_for_logging_str, + ) + .await?; } } _ => {}