Repository: diesel-rs/diesel (language: rust)

Issue:
DELETE command includes duplicate whitespace
<!--
If you want to report a bug, we added some points below which help us track down the problem faster.
-->

## Setup

### Versions

- **Rust:** 1.83.0
- **Diesel:** 2.2.6
- **Database:** SQLite
- **Operating System** Linux

### Feature Flags

- **diesel:** `r2d2`, `returning_clauses_for_sqlite_3_35`, `sqlite`, `uuid`

## Problem Description

SQL queries generated for DELETE commands include an unnecessary duplicate space between `DELETE` and `FROM`. This can be seen from emitting the corresponding strings in `walk_ast()`:

https://github.com/diesel-rs/diesel/blob/b705023d85e6ef76292a427aa70f21c28a5902ff/diesel/src/query_builder/delete_statement/mod.rs#L261-L267

The trailing space can safely be omitted here because `self.from_clause.walk_ast()` always contributes a leading space itself, leading to duplication, in its own `walk_ast()` method:

https://github.com/diesel-rs/diesel/blob/b705023d85e6ef76292a427aa70f21c28a5902ff/diesel/src/query_builder/from_clause.rs#L128-L131

Since `DELETE ` is always followed by ` FROM `—`self.from_clause` is not optional and must always be emitted—, the trailing space is not necessary, similar to how INSERT commands are prepared without trailing space (because they are always followed by ` INTO `):

https://github.com/diesel-rs/diesel/blob/b705023d85e6ef76292a427aa70f21c28a5902ff/diesel/src/query_builder/insert_statement/mod.rs#L488-L491

### What are you trying to accomplish?

Issue clean, beautiful SQL queries.

### What is the expected output?

`DELETE FROM "table"`

### What is the actual output?

`DELETE  FROM "table"`

### Are you seeing any additional errors?

No.

### Steps to reproduce

<!--
Please include as much of your codebase as needed to reproduce the error.  If the relevant files are large, please consider linking to a public repository or a [Gist](https://gist.github.com/). This includes normally the following parts:

* The exact code where your hit the problem
* Relevant parts your schema, so any `table!` macro calls required for
* Any other type definitions involved in the code, which produces your problem
-->

Issue a DELETE query as in the following code:

```rust
let num_deleted = diesel::delete(posts.filter(title.like(pattern)))
    .execute(connection)
    .expect("Error deleting posts");
```

## Checklist

- [x] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues) and the [discussion forum](https://github.com/diesel-rs/diesel/discussions) for similar possible closed issues.
<!--
If you are unsure if your issue is a duplicate of an existing issue please link the issue in question here
--> 
- [x] This issue can be reproduced on Rust's stable channel. (Your issue will be
  closed if this is not the case)
- [x] This issue can be reproduced without requiring a third party crate

<!--
Thank you for your submission!  You're helping make Diesel more robust 🎉

We'll try to respond as quickly as possible.
-->


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b006453871c5..894ccd50956e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,10 @@ in a way that makes the pools suitable for use in parallel tests.
 * Added `Json` and `Jsonb` support for the SQLite backend.
 * Fixed diesel thinking `a.eq_any(b)` was non-nullable even if `a` and `b` were nullable.
 
+### Fixed
+
+* Use a single space instead of two spaces between `DELETE FROM`.
+
 ## [2.2.2] 2024-07-19
 
 ### Fixed
diff --git a/diesel/src/query_builder/delete_statement/mod.rs b/diesel/src/query_builder/delete_statement/mod.rs
index f69456f8a102..394771a6d0cc 100644
--- a/diesel/src/query_builder/delete_statement/mod.rs
+++ b/diesel/src/query_builder/delete_statement/mod.rs
@@ -259,7 +259,7 @@ where
     Ret: QueryFragment<DB>,
 {
     fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
-        out.push_sql("DELETE ");
+        out.push_sql("DELETE");
         self.from_clause.walk_ast(out.reborrow())?;
         self.where_clause.walk_ast(out.reborrow())?;
         self.returning.walk_ast(out.reborrow())?;


Test patch: