, DB>;
249 |
250 | impl AsQuery for table {
251 | /* body omitted */
252 | }
253 |
254 | impl Table for table {
255 | /* body omitted */
256 | }
257 |
258 | impl IntoUpdateTarget for table {
259 | /* body omitted */
260 | }
261 |
262 | pub mod columns {
263 | pub struct star;
264 |
265 | impl Expression for star {
266 | type SqlType = ();
267 | }
268 |
269 | pub struct id;
270 |
271 | impl Expression for id {
272 | type SqlType = Integer;
273 | }
274 |
275 | pub struct name;
276 |
277 | impl Expression for name {
278 | type SqlType = Text;
279 | }
280 |
281 | pub struct hair_color;
282 |
283 | impl Expression for hair_color {
284 | type SqlType = Nullable;
285 | }
286 | }
287 | }
288 | ```
289 |
290 | :::
291 | :::
292 | :::
293 |
--------------------------------------------------------------------------------
/src/guides/composing-applications.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Composing Applications with Diesel"
3 | lang: en-US
4 | css: ../assets/stylesheets/application.css
5 | include-after: |
6 |
7 | ---
8 |
9 | ::: demo
10 | ::: content-wrapper
11 | ::: guide-wrapper
12 |
13 | One of the main benefits of using a query builder over raw SQL
14 | is that you can pull bits of your query out into functions and reuse them.
15 | In this guide,
16 | we'll look at common patterns for extracting your code into re-usable pieces.
17 | We'll also look at best practices for how to structure your code.
18 |
19 | All of our code examples are based on code from crates.io,
20 | a real world application which uses Diesel extensively.
21 | All of our examples will be focused on functions which *return*
22 | queries or pieces of queries.
23 | None of these examples will include a function which takes a database
24 | connection.
25 | We will go into the benefits of this structure at the end of the guide.
26 |
27 | crates.io has a `canon_crate_name` SQL function
28 | which is always used when comparing crate names.
29 | Rather than continuously writing
30 | `canon_crate_name(crates::name).eq("some name")`,
31 | we can instead pull this out into a function.
32 |
33 | ::: code-block
34 |
35 | [src/krate/mod.rs]( https://github.com/rust-lang/crates.io/blob/b4d49ac32c5561a7a4a0948ce5ba9ada7b8924fb/src/krate/mod.rs)
36 |
37 | ```rust
38 | use diesel::dsl::Eq;
39 | use diesel::prelude::sql_function;
40 | use diesel::sql_types::Text;
41 |
42 | sql_function!(fn canon_crate_name(x: Text) -> Text);
43 |
44 | type WithName<'a> =
45 | Eq, canon_crate_name::HelperType<&'a str>>;
46 |
47 | fn with_name(name: &str) -> WithName {
48 | canon_crate_name(crates::name).eq(canon_crate_name(name))
49 | }
50 | ```
51 |
52 | :::
53 |
54 | Now when we want to find a crate by name, we can write
55 | `crates::table.filter(with_name("foo"))` instead.
56 | If we want to accept types other than a string,
57 | we can make the method generic.
58 |
59 |
60 | ::: code-block
61 |
62 | [src/krate/mod.rs](https://github.com/rust-lang/crates.io/blob/b4d49ac32c5561a7a4a0948ce5ba9ada7b8924fb/src/krate/mod.rs):
63 |
64 | ```rust
65 | use diesel::dsl::Eq;
66 | use diesel::prelude::sql_function;
67 | use diesel::sql_types::Text;
68 |
69 | sql_function!(fn canon_crate_name(x: Text) -> Text);
70 |
71 | type WithName = Eq, canon_crate_name::HelperType>;
72 |
73 | fn with_name(name: T) -> WithName
74 | where
75 | T: AsExpression,
76 | {
77 | canon_crate_name(crates::name).eq(canon_crate_name(name))
78 | }
79 | ```
80 |
81 | :::
82 |
83 | It's up to you whether you make your functions generic,
84 | or only take a single type.
85 | We recommend only making these functions generic if it's actually needed,
86 | since it requires additional bounds in your `where` clause.
87 | The bounds you need might not be clear,
88 | unless you are familiar with Diesel's lower levels.
89 |
90 | In these examples,
91 | we are using helper types from [`diesel::dsl`]
92 | to write the return type explicitly.
93 | Nearly every method in Diesel has a helper type like this.
94 | The first type parameter is the method receiver
95 | (the thing before the `.`).
96 | The remaining type parameters are the arguments to the method.
97 | If we want to avoid writing this return type,
98 | or dynamically return a different expression,
99 | we can box the value instead.
100 |
101 | [`diesel::dsl`]: https://docs.diesel.rs/2.0.x/diesel/dsl/index.html
102 |
103 | ::: code-block
104 |
105 | [src/krate/mod.rs](https://github.com/rust-lang/crates.io/blob/b4d49ac32c5561a7a4a0948ce5ba9ada7b8924fb/src/krate/mod.rs)
106 |
107 | ```rust
108 | use diesel::pg::Pg;
109 | use diesel::prelude::sql_function;
110 | use diesel::sql_types::Text;
111 |
112 | sql_function!(fn canon_crate_name(x: Text) -> Text);
113 |
114 | fn with_name<'a, T>(name: T) -> Box + 'a>
115 | where
116 | T: AsExpression,
117 | T::Expression: BoxableExpression,
118 | {
119 | canon_crate_name(crates::name).eq(canon_crate_name(name))
120 | }
121 | ```
122 |
123 | :::
124 |
125 | In order to use [`BoxableExpression`], Diesel needs to know three things:
126 |
127 | - The table you intend to use it on
128 | - The backend you plan to execute it against
129 | - The SQL type it represents
130 |
131 | This is all the information Diesel uses to type check your query.
132 | Normally we can get this information from the type,
133 | but since we've erased the type by boxing,
134 | we have to supply it.
135 |
136 | The table is used to make sure that you don't try to use `users::name`
137 | on a query against `posts::table`.
138 | We need to know the backend you will execute it on,
139 | so we don't accidentally use a PostgreSQL function on SQLite.
140 | The SQL type is needed so we know what functions this can be passed to.
141 |
142 | Boxing an expression also implies that it has no aggregate functions.
143 | You cannot box an aggregate expression in Diesel.
144 | As of Diesel 1.0, a boxed expression can only be used with *exactly* the from
145 | clause given.
146 | You cannot use a boxed expression for `crates::table` with an inner join to
147 | another table.
148 |
149 | [`BoxableExpression`]: https://docs.diesel.rs/2.0.x/diesel/expression/trait.BoxableExpression.html
150 |
151 | In addition to extracting expressions,
152 | you can also pull out entire queries into functions.
153 | Going back to crates.io,
154 | the `Crate` struct doesn't use every column from the `crates` table.
155 | Because we almost always select a subset of these columns,
156 | we have an `all` function which selects the columns we need.
157 |
158 | ::: code-block
159 |
160 | [src/krate/mod.rs](https://github.com/rust-lang/crates.io/blob/b4d49ac32c5561a7a4a0948ce5ba9ada7b8924fb/src/krate/mod.rs)
161 |
162 | ```rust
163 | use diesel::backend::Backend;
164 | use diesel::dsl::{AsSelect, Select};
165 |
166 | #[derive(Selectable, Queryable)]
167 | #[diesel(table_name = crates)]
168 | struct Crate {
169 | id: i32,
170 | name: String,
171 | updated_at: NaiveDateTime,
172 | created_at: NaiveDateTime,
173 | }
174 |
175 | type All = Select>;
176 |
177 | impl Crate {
178 | pub fn all() -> All
179 | where
180 | DB: Backend,
181 | {
182 | crates::table.select(Crate::as_select())
183 | }
184 | }
185 | ```
186 |
187 | :::
188 |
189 | We also frequently found ourselves writing
190 | `Crate::all().filter(with_name(crate_name))`.
191 | We can pull that into a function as well.
192 |
193 | ::: code-block
194 |
195 | [src/krate/mod.rs](https://github.com/rust-lang/crates.io/blob/b4d49ac32c5561a7a4a0948ce5ba9ada7b8924fb/src/krate/mod.rs)
196 |
197 | ```rust
198 | use diesel::dsl::Filter;
199 |
200 | type ByName = Filter, WithName>;
201 |
202 | impl Crate {
203 | fn by_name(name: T) -> ByName {
204 | Self::all().filter(with_name(name))
205 | }
206 | }
207 | ```
208 |
209 | :::
210 |
211 | And just like with expressions, if we don't want to write the return types,
212 | or we want to dynamically construct the query differently, we can box the whole query.
213 |
214 | ::: code-block
215 |
216 | [src/krate/mod.rs](https://github.com/rust-lang/crates.io/blob/b4d49ac32c5561a7a4a0948ce5ba9ada7b8924fb/src/krate/mod.rs)
217 |
218 | ```rust
219 | use diesel::expression::{Expression, AsExpression};
220 | use diesel::pg::Pg;
221 | use diesel::sql_types::Text;
222 | use diesel::dsl::{SqlTypeOf, AsSelect};
223 |
224 | type SqlType = SqlTypeOf>;
225 | type BoxedQuery<'a> = crates::BoxedQuery<'a, Pg, SqlType>;
226 |
227 | impl Crate {
228 | fn all() -> BoxedQuery<'static> {
229 | crates::table.select(Crate::as_select()).into_boxed()
230 | }
231 |
232 | fn by_name<'a, T>(name: T) -> BoxedQuery<'a>
233 | where
234 | T: AsExpression,
235 | T::Expression: BoxableExpression,
236 | {
237 | Self::all().filter(by_name(name))
238 | }
239 | }
240 | ```
241 |
242 | :::
243 |
244 | Once again, we have to give Diesel some information to box the query:
245 |
246 | - The SQL type of the `SELECT` clause
247 | - The `FROM` clause
248 | - The backend you are going to execute it against
249 |
250 | The SQL type is needed so we can determine what structs can be
251 | deserialized from this query.
252 | The `FROM` clause is needed so we can validate the arguments
253 | to future calls to `filter` and other query builder methods.
254 | The backend is needed to ensure you don't accidentally use a
255 | PostgreSQL function on SQLite.
256 |
257 | Note that in all of our examples,
258 | we are writing functions which *return* queries or expressions.
259 | None of these functions execute the query.
260 | In general you should always prefer functions which return queries,
261 | and avoid functions which take a connection as an argument.
262 | This allows you to re-use and compose your queries.
263 |
264 | For example, if we had written our `by_name` function like this:
265 |
266 | ::: code-block
267 |
268 | [src/krate/mod.rs](https://github.com/rust-lang/crates.io/blob/b4d49ac32c5561a7a4a0948ce5ba9ada7b8924fb/src/krate/mod.rs)
269 |
270 | ```rust
271 | impl Crate {
272 | fn by_name(name: &str, conn: &mut PgConnection) -> QueryResult {
273 | Self::all()
274 | .filter(with_name(name))
275 | .first(conn)
276 | }
277 | }
278 | ```
279 |
280 | :::
281 |
282 | Then we would never be able to use this query in another context,
283 | or modify it further. By writing the function as one that returns a query,
284 | rather than executing it, we can do things like use it as a subselect.
285 |
286 | ::: code-block
287 |
288 | [Example]()
289 |
290 | ```rust
291 | let version_id = versions
292 | .select(id)
293 | .filter(crate_id.eq_any(Crate::by_name(crate_name).select(crates::id)))
294 | .filter(num.eq(version))
295 | .first(conn)?;
296 | ```
297 |
298 | :::
299 |
300 | Or use it to do things like get all of its downloads:
301 | Example
302 |
303 | ::: code-block
304 |
305 | [Example]()
306 |
307 | ```rust
308 | let recent_downloads = Crate::by_name(crate_name)
309 | .inner_join(crate_downloads::table)
310 | .filter(CrateDownload::is_recent())
311 | .select(sum(crate_downloads::downloads))
312 | .get_result(conn)?;
313 | ```
314 |
315 | :::
316 |
317 | All code in this guide is based on real code from crates.io.
318 | You can find the source [on GitHub][crates-io]
319 |
320 | [crates-io]: https://github.com/rust-lang/crates.io
321 |
322 | :::
323 | :::
324 | :::
325 |
--------------------------------------------------------------------------------
/src/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Diesel is a Safe, Extensible ORM and Query Builder for Rust"
3 | css: assets/stylesheets/application.css
4 | header-includes: |
5 |
12 | include-after: |
13 |
14 |
36 | ---
37 |
38 | >
39 |
40 | ::: main-page
41 |
42 | ::: banner-wrapper
43 |
44 | Diesel is the most productive way to interact with databases in Rust because of its safe and composable abstractions over queries.
45 |
46 | ::: btn-container
47 |
48 | [Getting Started](/guides/getting-started){.btn .btn-primary .btn-download} [View on GitHub](https://github.com/diesel-rs/diesel){.btn .btn-secondary}
49 |
50 | :::
51 |
52 | :::
53 |
54 | ::: feature-list
55 |
56 | ::: content-wrapper
57 |
58 | ### Why did we make Diesel? {.feature-list__heading .section-heading}
59 |
60 |
61 | ::: {.feature-list__feature .type-safe}
62 |
63 | #### Preventing Runtime Errors {.feature__heading}
64 |
65 | We don't want to waste time tracking down runtime errors.
66 | We achieve this by having
67 | Diesel eliminate the possibility of incorrect database interactions
68 | at compile time.
69 |
70 | :::
71 |
72 | ::: {.feature-list__feature .performance}
73 |
74 | #### Built for Performance {.feature__heading}
75 |
76 | Diesel offers a high level query builder and lets you think about your problems in Rust, not SQL.
77 | Our focus on zero-cost abstractions allows
78 | Diesel to run your query and load your data even faster than C.
79 |
80 | :::
81 |
82 | ::: {.feature-list__feature .extensible}
83 |
84 | #### Productive and Extensible {.feature__heading}
85 |
86 | Unlike Active Record and other ORMs, Diesel is designed to be abstracted over.
87 | Diesel enables you to write reusable code
88 | and think in terms of your problem domain and not SQL.
89 |
90 | :::
91 |
92 | :::
93 | :::
94 |
95 |
96 | ::: demo
97 |
98 | ::: content-wrapper
99 |
100 | ### See some examples {.demo__heading .section-heading}
101 |
102 | ::: vertical-tabs-container
103 |
104 | ::: vertical-tabs
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | [Simple Queries](javascript::void(0)){.js-vertical-tab .vertical-tab .is-active onclick="change_tab(event, 'simple_queries')"}
115 | [Complex Queries](javascript::void(0)){.js-vertical-tab .vertical-tab onclick="change_tab(event, 'complex_queries')"}
116 | [Less Boilerplate](javascript::void(0)){.js-vertical-tab .vertical-tab onclick="change_tab(event, 'less_boilerplate')"}
117 | [Inserting Data](javascript::void(0)){.js-vertical-tab .vertical-tab onclick="change_tab(event, 'inserting_data')"}
118 | [Updating Data](javascript::void(0)){.js-vertical-tab .vertical-tab
119 | onclick="change_tab(event, 'updating_data')"}
120 | [Ergonomic Raw SQL](javascript::void(0)){.js-vertical-tab .vertical-tab onclick="change_tab(event, 'raw_sql')"}
121 |
122 | :::
123 |
124 | ::: vertical-tab-content-container
125 |
126 | ::: {#simple_queries .js-vertical-tab-content .vertical-tab-content style="display: block;"}
127 | Simple queries are a complete breeze. Loading all users from a database:
128 |
129 | ::: code-block
130 |
131 | [Rust code]()
132 |
133 | ```rust
134 | users::table.load(&mut connection)
135 | ```
136 |
137 | :::
138 |
139 | ::: code-block
140 |
141 | [Executed SQL]()
142 |
143 | ```sql
144 | SELECT * FROM users;
145 | ```
146 | :::
147 |
148 | Loading all the posts for a user:
149 |
150 | ::: code-block
151 |
152 | [Rust code]()
153 |
154 | ``` rust
155 | Post::belonging_to(user).load(&mut connection)
156 | ```
157 |
158 | :::
159 |
160 | ::: code-block
161 |
162 | [Executed SQL]()
163 |
164 | ```sql
165 | SELECT * FROM posts WHERE user_id = 1;
166 | ```
167 |
168 | :::
169 |
170 | :::
171 |
172 | ::: {#complex_queries .js-vertical-tab-content .vertical-tab-content}
173 |
174 | Diesel's powerful query builder helps you construct queries as simple or complex as
175 | you need, at zero cost.
176 |
177 | ::: code-block
178 |
179 | [Rust code]()
180 |
181 | ```rust
182 | let versions = Version::belonging_to(krate)
183 | .select(id)
184 | .order(num.desc())
185 | .limit(5);
186 | let downloads = version_downloads
187 | .filter(date.gt(now - 90.days()))
188 | .filter(version_id.eq(any(versions)))
189 | .order(date)
190 | .load::(&mut conn)?;
191 | ```
192 |
193 | :::
194 |
195 | ::: code-block
196 |
197 | [Executed SQL]()
198 | ```sql
199 | SELECT version_downloads.*
200 | WHERE date > (NOW() - '90 days')
201 | AND version_id = ANY(
202 | SELECT id FROM versions
203 | WHERE crate_id = 1
204 | ORDER BY num DESC
205 | LIMIT 5
206 | )
207 | ORDER BY date
208 | ```
209 | :::
210 |
211 | :::
212 |
213 | ::: {#less_boilerplate .js-vertical-tab-content .vertical-tab-content}
214 | Diesel codegen generates boilerplate for you. It lets you focus on your business logic, not mapping to and from SQL rows.
215 |
216 | That means you can write this:
217 |
218 | ::: code-block
219 |
220 | [With Diesel]()
221 |
222 | ```rust
223 | #[derive(Queryable)]
224 | pub struct Download {
225 | id: i32,
226 | version_id: i32,
227 | downloads: i32,
228 | counted: i32,
229 | date: SystemTime,
230 | }
231 | ```
232 |
233 | :::
234 |
235 | Instead of this:
236 |
237 | ::: code-block
238 |
239 | [Without Diesel]()
240 |
241 | ```rust
242 | pub struct Download {
243 | id: i32,
244 | version_id: i32,
245 | downloads: i32,
246 | counted: i32,
247 | date: SystemTime,
248 | }
249 |
250 | impl Download {
251 | fn from_row(row: &Row) -> Download {
252 | Download {
253 | id: row.get("id"),
254 | version_id: row.get("version_id"),
255 | downloads: row.get("downloads"),
256 | counted: row.get("counted"),
257 | date: row.get("date"),
258 | }
259 | }
260 | }
261 | ```
262 |
263 | :::
264 |
265 | :::
266 |
267 | ::: {#inserting_data .js-vertical-tab-content .vertical-tab-content}
268 |
269 | It's not just about reading data. Diesel makes it easy to use structs for new records.
270 |
271 | ::: code-block
272 |
273 | [Rust code]()
274 |
275 | ```rust
276 | #[derive(Insertable)]
277 | #[table_name="users"]
278 | struct NewUser<'a> {
279 | name: &'a str,
280 | hair_color: Option<&'a str>,
281 | }
282 |
283 | let new_users = vec![
284 | NewUser { name: "Sean", hair_color: Some("Black") },
285 | NewUser { name: "Gordon", hair_color: None },
286 | ];
287 |
288 | insert_into(users)
289 | .values(&new_users)
290 | .execute(&mut connection);
291 | ```
292 | :::
293 |
294 | ::: code-block
295 |
296 | [Executed SQL]()
297 |
298 | ```sql
299 | INSERT INTO users (name, hair_color) VALUES
300 | ('Sean', 'Black'),
301 | ('Gordon', DEFAULT)
302 | ```
303 | :::
304 |
305 | If you need data from the rows you inserted, just change `execute` to `get_result` or `get_results`. Diesel will take care of the rest.
306 |
307 | ::: code-block
308 |
309 | [Rust code]()
310 |
311 | ```rust
312 | let new_users = vec![
313 | NewUser { name: "Sean", hair_color: Some("Black") },
314 | NewUser { name: "Gordon", hair_color: None },
315 | ];
316 |
317 | let inserted_users = insert_into(users)
318 | .values(&new_users)
319 | .get_results::(&mut connection);
320 | ```
321 | :::
322 |
323 | ::: code-block
324 |
325 | [Executed SQL]()
326 |
327 | ```sql
328 | INSERT INTO users (name, hair_color) VALUES
329 | ('Sean', 'Black'),
330 | ('Gordon', DEFAULT)
331 | RETURNING *
332 | ```
333 |
334 | :::
335 |
336 | :::
337 |
338 | ::: {#updating_data .js-vertical-tab-content .vertical-tab-content}
339 |
340 | Diesel's codegen can generate several ways to update a row, letting you encapsulate your logic in the way that makes sense for your app.
341 |
342 |
343 | ::: code-block
344 |
345 | [Modifying a struct]()
346 |
347 | ```rust
348 | post.published = true;
349 | post.save_changes(&mut connection);
350 | ```
351 |
352 | :::
353 |
354 | ::: code-block
355 |
356 | [One-off batch changes]()
357 |
358 | ```rust
359 | update(users.filter(email.like("%@spammer.com")))
360 | .set(banned.eq(true))
361 | .execute(&mut connection)
362 | ```
363 | :::
364 |
365 | ::: code-block
366 |
367 | [Using a struct for encapsulation]()
368 |
369 | ```rust
370 | update(Settings::belonging_to(current_user))
371 | .set(&settings_form)
372 | .execute(&mut connection)
373 | ```
374 |
375 | :::
376 |
377 | :::
378 |
379 | :::{#raw_sql .js-vertical-tab-content .vertical-tab-content}
380 |
381 | There will always be certain queries that are just easier to write as raw SQL, or can't be expressed with the query builder. Even in these cases, Diesel provides an easy to use API for writing raw SQL.
382 |
383 | ::: code-block
384 |
385 | [Running raw SQL]()
386 |
387 | ```rust
388 | #[derive(QueryableByName)]
389 | #[table_name = "users"]
390 | struct User {
391 | id: i32,
392 | name: String,
393 | organization_id: i32,
394 | }
395 |
396 | // Using `include_str!` allows us to keep the SQL in a
397 | // separate file, where our editor can give us SQL specific
398 | // syntax highlighting.
399 | sql_query(include_str!("complex_users_by_organization.sql"))
400 | .bind::(organization_id)
401 | .bind::(offset)
402 | .bind::(limit)
403 | .load::(&mut conn)?;
404 | ```
405 |
406 | :::
407 |
408 | :::
409 |
410 | :::
411 |
412 | :::
413 | :::
414 | :::
415 | :::
416 |
--------------------------------------------------------------------------------
/src/news/2_0_0_release.md:
--------------------------------------------------------------------------------
1 | ---
2 | lang: "en-US"
3 | title: "Diesel 2.0.0"
4 | css: ../assets/stylesheets/application.css
5 | include-after: |
6 |
7 | ---
8 |
9 | ::: demo
10 | ::: content-wrapper
11 | ::: guide-wrapper
12 |
13 | Diesel 2.0.0 contains the contributions of more than 130 people. More than 1700 commits were submitted
14 | over a span of 3 years.
15 |
16 | As part of this release we introduced numerous new features and rewrote large parts of the internal structure.
17 | Check out our [changelog for a complete list of changes](/changelog.html). As this is a new major Diesel release it contains a number of breaking changes. Checkout our [migration guide](/guides/migration_guide.html) for details about how to handle those breaking changes.
18 |
19 | This release contains the following parts:
20 |
21 | * diesel 2.0.0
22 | * diesel_derives 2.0.0
23 | * diesel_migrations 2.0.0
24 | * diesel_cli 2.0.0
25 | * diesel_dynamic_schema 0.2.0
26 |
27 | Support the development of Diesel by [sponsoring our work on GitHub](https://github.com/diesel-rs/diesel)
28 |
29 | ## Features
30 |
31 | As a highlight Diesel 2.0.0 adds support for the following features:
32 |
33 | * Fully type checked `GROUP BY` support
34 | * Support for table aliasing
35 | * Support for defining select clauses via a corresponding type
36 | * Support for `UNION`/`INTERSECT` queries
37 |
38 | In addition to the highlighted features Diesel 2.0.0 fixes several issues in our type level SQL representation such that it now correctly handles the following cases:
39 |
40 | * Mixed nested `LEFT JOINS` and `INNER JOINS`
41 | * Chaining mixed nullable expressions via `AND`, `OR` and similar operators
42 |
43 | ### Support for `GROUP BY` clauses
44 |
45 | Diesel 2.0 adds support for `GROUP BY` clauses for select queries.
46 |
47 | This means queries like the following one will just work.
48 |
49 | ::: code-block
50 |
51 | [Example]()
52 |
53 | ```rust
54 | users::table.inner_join(posts::table)
55 | .group_by(users::id)
56 | .select((users::name, count(posts::id)))
57 | ```
58 |
59 | :::
60 |
61 | As this is the case for all other Diesel built-in query dsl, this construct is fully checked at compile time. This means Diesel
62 | will ensure that the `GROUP BY` clause is valid for the current query and it will also ensure that expressions appearing inside
63 | of your `SELECT` clause will match the aggregation rules provided by the current `GROUP BY` clause. Checkout the documentation of [`QueryDsl::group_by`](https://docs.diesel.rs/2.0.x/diesel/prelude/trait.QueryDsl.html#method.group_by) for examples.
64 |
65 | ### Support for table aliasing
66 |
67 | Diesel 2.0 adds support for table aliasing. This enables users to write queries, where a table appears more than once in the corresponding
68 | `FROM` clause. For this Diesel provides a [`diesel::alias!`] macro that allows to define new alias for existing tables.
69 |
70 | The following query demonstrates the support for this feature:
71 |
72 | ::: code-block
73 |
74 | [Example]()
75 |
76 | ```rust
77 | // Define new table alias for the existing `users` table
78 | let users1 = diesel::alias!(schema::users as user1);
79 |
80 | // Use the corresponding alias inside any existing query
81 | users::table
82 | .inner_join(users1.on(users::id).eq(users1.field(users::id))))
83 | .select((users::id, users::name, users1.field(users::name)))
84 | .order_by(users1.field(users::id))
85 | ```
86 |
87 | :::
88 |
89 | Again all of this is checked at compile time. So similar to a normal table, columns from aliases are only allowed to appear if
90 | the corresponding query actually uses the alias.
91 |
92 | [`diesel::alias!`]: https://docs.diesel.rs/2.0.x/diesel/macro.alias.html
93 |
94 | ### Implied selection via the new `Selectable` trait
95 |
96 | Diesel 2.0 features a new [`Selectable`] trait and derive that lets users declare that a type expects a certain kind of select clause.
97 | The major use case for this feature is to ensure that columns from a specific query are always requested in the right order
98 | for a corresponding type implementing `Queryable`. This also works for complex queries involving joins or other kinds of nesting.
99 |
100 |
101 | ::: code-block
102 |
103 | [Example]()
104 |
105 | ```rust
106 | #[derive(Queryable, Selectable)]
107 | struct User {
108 | id: i32,
109 | name: String,
110 | }
111 |
112 | let first_user = users.select(User::as_select()).first(connection)?;
113 | ```
114 |
115 | :::
116 |
117 | Diesel enforces at type system level that once you provided such a select clause via `User::as_select()` you are only allowed
118 | to construct this type from the returned result of the corresponding query. This means there is no need to specify the `User` type
119 | twice in the query above.
120 |
121 | [`Selectable`]: https://docs.diesel.rs/2.0.x/diesel/expression/trait.Selectable.html
122 |
123 | ### Support for `UNION`/`INTERSECT`/`EXCEPT` queries
124 |
125 | Diesel 2.0 extents the query builder to support query combinations via `UNION`/`INTERSECT`/`EXCEPT`. This allows you
126 | to easily chain multiple queries together as long as they return fields of the same type. Queries like the following
127 | one are now supported:
128 |
129 | ::: code-block
130 |
131 | [Example]()
132 |
133 | ```rust
134 | users.select(user_name.nullable())
135 | .union(animals.select(animal_name).filter(animal_name.is_not_null()))
136 | ```
137 |
138 | :::
139 |
140 | As always this is checked at compile time to reject invalid queries, like for example that ones containing select
141 | clauses with different fields. Checkout the documentation of [`CombineDsl`](https://docs.diesel.rs/2.0.x/diesel/prelude/trait.CombineDsl.html) for details.
142 |
143 | ## Call for Participation
144 |
145 | The release of Diesel 2.0 does not only include the features listed above, but also marks the
146 | point where the following things can be provided by third party crates:
147 |
148 | * Custom `QueryDsl` extensions to support previously unsupported SQL features. Checkout
149 | [`diesel_full_text_search`](https://github.com/diesel-rs/diesel_full_text_search) for an example
150 | * Alternative query dsl implementations reusing the existing `Connection` infrastructure
151 | * Custom [`Connection`](https://docs.diesel.rs/2.0.x/diesel/connection/trait.Connection.html#provide-a-new-connection-implementation-for-an-existing-backend) implementations for existing backends
152 | * Custom [`Connection`](https://docs.diesel.rs/2.0.x/diesel/connection/trait.Connection.html#implement-support-for-an-unsupported-database-system)
153 | and [`Backend`](https://docs.diesel.rs/2.0.x/diesel/backend/trait.Backend.html#implementing-a-custom-backend) implementations for previously unsupported backends. Checkout [diesel-oci](https://github.com/GiGainfosystems/diesel-oci) for an example.
154 |
155 | We encourage our community to try out those features. Especially we would like to see experimentation around:
156 |
157 | * Previously unsupported database backends
158 | * Pure rust implementations of existing backend implementations. Checkout [this](https://github.com/sfackler/rust-postgres/issues/890) and [this](https://github.com/blackbeam/rust-mysql-simple/discussions/320) discussion for starting points.
159 | * Alternative query dsl implementations. Checkout [this discussion](https://github.com/SeaQL/sea-query/discussions/168) as starting point.
160 |
161 | Please get in touch with us for pointers, help and details.
162 |
163 |
164 | ## Input for Future Roadmap
165 |
166 | With the release of Diesel 2.0 the planing for our next releases start. Hopefully they will not take as long as Diesel 2.0. We are looking for input on which features are wanted by our community. Please open a discussion thread with your idea in our [discussion forum](https://github.com/diesel-rs/diesel/discussions/categories/ideas).
167 |
168 | Weiznich will work on improving error messages for trait heavy crates based on a Rust Foundation Project Grant. This work will hopefully improve error messages for Diesel as well. If you are aware of bad error messages please submit a
169 | minimal example [here](https://github.com/weiznich/rust-foundation-community-grant).
170 |
171 | ## Update considerations
172 |
173 | Diesel 2.0 introduces substantial changes to Diesel's inner workings.
174 | In some cases this impacts code written using Diesel 1.4.x.
175 | This document outlines notable changes and presents potential update strategies.
176 | We recommend to start the upgrade by removing the usage of all items that
177 | are marked as deprecated in Diesel 1.4.x.
178 |
179 | Any code base migrating from Diesel 1.4.x to Diesel 2.0 is expected to be affected at least by
180 | the following changes:
181 |
182 | * [Diesel now requires a mutable reference to the connection](/guides/migration_guide.html#2-0-0-mutable-connection)
183 | * [Changed derive attributes](/guides/migration_guide.html#2-0-0-derive-attributes)
184 |
185 | Users of `diesel_migration` are additionally affected by the following change:
186 |
187 | * [`diesel_migration` rewrite](/guides/migration_guide.html#2-0-0-upgrade-migrations)
188 |
189 | Users of `BoxableExpression` might be affected by the following change:
190 |
191 | * [Changed nullability of operators](/guides/migration_guide.html#2-0-0-nullability-ops)
192 |
193 | Users of tables containing a column of the type `Array` are affected by the following change:
194 |
195 | * [Changed nullability of array elemetns](/guides/migration_guide.html#2-0-0-nullability-of-array-elements)
196 |
197 | Users that implement support for their SQL types or type mappings are affected
198 | by the following changes:
199 |
200 | * [Changed required traits for custom SQL types](/guides/migration_guide.html#2-0-0-custom-type-implementation)
201 | * [Changed `ToSql` implementations](/guides/migration_guide.html#2-0-0-to-sql)
202 | * [Changed `FromSql` implementations](/guides/migration_guide.html#2-0-0-from-sql)
203 |
204 | `no_arg_sql_function!` macro is now pending deprecation.
205 | Users of the macro are advised to consider `sql_function!` macro.
206 |
207 | * [Deprecated usage of `no_arg_sql_function!` macro](/guides/migration_guide.html#2-0-0-no_arg_sql_function)
208 |
209 | Users of `eq_any` on the PostgreSQL backend might hit type rejection error in rare cases.
210 |
211 | * [Changed accepted argument to `eq_any()` for the PostgreSQL backend](/guides/migration_guide.html#2-0-0-changed_eq_any)
212 |
213 | Users that update generic Diesel code will also be affected by the following changes:
214 |
215 | * [Removing `NonAggregate` in favor of `ValidGrouping`](/guides/migration_guide.html#2-0-0-upgrade-non-aggregate)
216 | * [Changed generic bounds](/guides/migration_guide.html#2-0-0-generic-changes)
217 |
218 | Additionally this release contains many changes for users that implemented a custom backend/connection.
219 | We do not provide explicit migration steps but we encourage users to reach out with questions pertaining to these changes.
220 |
221 | ## Thanks
222 |
223 | As part of this release we would like to welcome @Ten0 as part of the
224 | diesel core team.
225 |
226 | Thank you to everyone who helped make this release happen through sponsoring, bug reports, and discussion on GitHub and Gitter. While we don't have a way to collect stats on that form of contribution, it's greatly appreciated.
227 |
228 | In addition to the Diesel core team, 141 people contributed code to this release. A huge thank you to:
229 |
230 |
231 | * Alessandro Menezes
232 | * Alexander 'z33ky' Hirsch
233 | * Alexei Pastuchov
234 | * Alice Ryhl
235 | * Amila Welihinda
236 | * Andre Braga Reis
237 | * Andreas Runfalk
238 | * Andrew Safigan
239 | * Andrew Speed
240 | * Andy Russell
241 | * Artem Vorotnikov
242 | * Arve Seljebu
243 | * Billy Chan
244 | * Blas Rodriguez Irizar
245 | * Bryan Henry
246 | * Callym
247 | * Caroline Glassberg-Powell
248 | * Cassie Jones
249 | * Chenxi Yuan
250 | * Chris Eckhardt
251 | * Chris Hanks
252 | * Chris Maddox
253 | * Chris West (Faux)
254 | * Clouds Flowing
255 | * Corentin Henry
256 | * Daniel Buse
257 | * Danilo Bargen
258 | * David Teller
259 | * David Tulig
260 | * DebugSteven
261 | * Diggory Blake
262 | * Dmitriy Pleshevskiy
263 | * Dusty Mabe
264 | * DrVilepis
265 | * EclipsedSolari
266 | * Emile Fugulin
267 | * Emm
268 | * Emmanuel Surleau
269 | * Erlend Langseth
270 | * Felix Watts
271 | * Filip Gospodinov
272 | * Garrett Thornburg
273 | * Giorgio Gambino
274 | * Grégory Obanos
275 | * Hal Gentz
276 | * Han Xu
277 | * Heliozoa
278 | * Henk van der Laan
279 | * Henry Boisdequin
280 | * Hirokazu Hata
281 | * Iban Eguia (Razican)
282 | * Igor Raits
283 | * Ivan Tham
284 | * JR Heard
285 | * Jean SIMARD
286 | * Jeremy Stucki
287 | * Jiří Sejkora
288 | * jigaoqiang
289 | * Joel Parker Henderson
290 | * John Brandt
291 | * Jonas Platte
292 | * Jonas Schievink
293 | * Joshua Koudys
294 | * Juhasz Sandor
295 | * Justice4Joffrey
296 | * Katharina Fey
297 | * Kevin King
298 | * Kevin Kirchner
299 | * Khionu Sybiern
300 | * Kitsu
301 | * Koisell
302 | * Kononnable
303 | * Leonardo Yvens
304 | * Lukas Markeffsky
305 | * Maccesch
306 | * Marc-Stefan Cassola
307 | * Martell Malone
308 | * Martijn Groeneveldt
309 | * Martin Nordholts
310 | * Matthew Kuo
311 | * Matthieu Guillemot
312 | * Mcat12
313 | * Meven
314 | * Mike Cronce
315 | * Mr Ceperka
316 | * Nafiul Islam
317 | * Nathan Papapietro
318 | * Nicholas Yang
319 | * Oliver Cooper
320 | * Otto Castle
321 | * Pankaj Jangid
322 | * Paolo Barbolini
323 | * Paul Le Corre
324 | * Paul Martensen
325 | * Pavan Kumar Sunkara
326 | * Paweł Przeniczny
327 | * Philip Trauner
328 | * Raphael Arias
329 | * Roman
330 | * Ryan Leckey
331 | * Sarthak Singh
332 | * Scott Driggers
333 | * Sean Klein
334 | * Simon Ertl
335 | * Spencer Taylor
336 | * Steven Chu
337 | * Storm Timmermans
338 | * Sébastien Santoro
339 | * Takayuki Maeda
340 | * Thomas Constantine Moore
341 | * Thomas Eizinger
342 | * Thomas Etter
343 | * Tom MacWright
344 | * Tuetuopay
345 | * Urhengulas
346 | * Vanio Begic
347 | * WebeWizard
348 | * William Myers
349 | * Yin Jifeng
350 | * Yuki Okushi
351 | * Zane Duffield
352 | * blackghost1987
353 | * czotomo
354 | * dchenk
355 | * ejc Drobnič
356 | * gorbit99
357 | * hasezoey
358 | * hi-rustin
359 | * kevinpoitra
360 | * kpcyrd
361 | * matthew-dowdell
362 | * ode79
363 | * ropottnik
364 | * telios
365 | * theredfish
366 | * zoewithabang
367 | * Zhenhui Xie
368 | * Émile Fugulin
369 | * κeen
370 | * 二手掉包工程师
371 | * 棒棒彬_Binboy
372 |
373 |
374 | :::
375 | :::
376 | :::
377 |
--------------------------------------------------------------------------------
/src/guides/all-about-updates.md:
--------------------------------------------------------------------------------
1 | ---
2 | lang: en-US
3 | title: "All About Updates"
4 | css: ../assets/stylesheets/application.css
5 | include-after: |
6 |
7 | ---
8 |
9 | ::: demo
10 | ::: content-wrapper
11 | ::: guide-wrapper
12 |
13 | Most applications fall into a category called "CRUD" apps. CRUD stands for
14 | "Create, Read, Update, Delete". Diesel provides support for all four pieces,
15 | but in this guide we're going to look at all the different ways to go about updating records.
16 |
17 | An update statement is constructed by calling `diesel::update(target).set(changes)`.
18 | The resulting statement is then run by calling either `execute`, `get_result`, or `get_results`.
19 |
20 | If you look at the documentation for [`update`](https://docs.diesel.rs/2.0.x/diesel/fn.update.html),
21 | you'll notice that the type of the argument is any type `T` which implements `IntoUpdateTarget`.
22 | You don't need to worry about what this trait does, but it is important to know
23 | which types implement it. There are three kinds which implement this trait. The first is tables.
24 |
25 | If we have a table that looks like this:
26 |
27 | ::: code-block
28 |
29 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L9-L18)
30 |
31 | ```rust
32 | table! {
33 | posts {
34 | id -> BigInt,
35 | title -> Text,
36 | body -> Text,
37 | draft -> Bool,
38 | publish_at -> Timestamp,
39 | visit_count -> Integer,
40 | }
41 | }
42 | ```
43 |
44 | :::
45 |
46 | We could write a query that publishes all posts by doing:
47 |
48 | ::: code-block
49 |
50 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L30-L34)
51 |
52 | ```rust
53 | use crate::posts::dsl::*;
54 |
55 | diesel::update(posts).set(draft.eq(false)).execute(conn)
56 | ```
57 |
58 | :::
59 |
60 | We can use the [`debug_query`] function to inspect the generated SQL.
61 | The output you see may slightly differ from this guide, depending on which backend you're using.
62 | If we run `println!("{}", debug_query::(&our_query));`, we'll see the following:
63 |
64 | [`debug_query`]: https://docs.diesel.rs/2.0.x/diesel/fn.debug_query.html
65 |
66 | ::: code-block
67 |
68 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L36-L44)
69 |
70 | ```sql
71 | UPDATE "posts" SET "draft" = $1 -- binds: [false]
72 | ```
73 |
74 | :::
75 |
76 | This is pretty much one-to-one with the Rust code (the `?` denotes a bound parameter in SQL,
77 | which will be substituted with `false` here). It's quite rare to want to update an entire table,
78 | though. So let's look at how we can scope that down. The second kind that you can pass to
79 | `update` is any query which has only had `.filter` called on it. We could scope our update to
80 | only touch posts where `publish_at` is in the past like so:
81 |
82 | ::: code-block
83 |
84 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L46-L54)
85 |
86 | ```rust
87 | use crate::posts::dsl::*;
88 | use diesel::dsl::now;
89 |
90 | diesel::update(posts)
91 | .filter(publish_at.lt(now))
92 | .set(draft.eq(false))
93 | .execute(conn)
94 | ```
95 |
96 | :::
97 |
98 | That would generate the following SQL:
99 |
100 | ::: code-block
101 |
102 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L56-L70)
103 |
104 | ```sql
105 | UPDATE "posts" SET "draft" = $1 WHERE ("posts"."publish_at" < CURRENT_TIMESTAMP) -- binds: [false]
106 | ```
107 |
108 | :::
109 |
110 | The most common update queries are just scoped to a single record. So the final kind that
111 | you can pass to `update` is anything which implements [the `Identifiable` trait].
112 | `Identifiable` gets implemented by putting `#[derive(Identifiable)]` on a struct.
113 | It represents any struct which is one-to-one with a row on a database table.
114 | Importantly, and unlike `Queryable`, the `Identifiable` trait requires that the schema
115 | generated by the `table!` macro be in scope, or the compilation will fail with E0433,
116 | noting `Use of undeclared type or module (your_tablename)`.
117 |
118 | [the `Identifiable` trait]: https://docs.diesel.rs/2.0.x/diesel/associations/trait.Identifiable.html
119 |
120 | If we wanted a struct that mapped to our posts table, it'd look something like this:
121 |
122 | ::: code-block
123 |
124 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L20-L28)
125 |
126 | ```rust
127 | #[derive(Queryable, Identifiable, AsChangeset)]
128 | pub struct Post {
129 | pub id: i64,
130 | pub title: String,
131 | pub body: String,
132 | pub draft: bool,
133 | pub publish_at: SystemTime,
134 | pub visit_count: i32,
135 | }
136 | ```
137 |
138 | :::
139 |
140 | The struct has one field per database column, but what's important for `Identifiable` is
141 | that it has the `id` field, which is the primary key of our table. Since our struct name is
142 | just the table name without an `s`, we don't have to provide the table name explicitly.
143 | If our struct were named something different, or if pluralizing it was more complex than
144 | putting an `s` on the end, we would have to specify the table name by adding `#[table_name="posts"]`.
145 | We're using `SystemTime` here since it's in the standard library, but in a real application
146 | we'd probably want to use a more full-featured type like one from `chrono`,
147 | which you can do by enabling the `chrono` feature on Diesel.
148 |
149 | If we wanted to publish just this post, we could do it like this:
150 |
151 |
152 | ::: code-block
153 |
154 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L72-L76)
155 |
156 | ```rust
157 | diesel::update(post)
158 | .set(posts::draft.eq(false))
159 | .execute(conn)
160 | ```
161 |
162 | :::
163 |
164 | It's important to note that we always pass a reference to the post, not the post itself.
165 | When we write `update(post)`, that's equivalent to writing `update(posts.find(post.id))`,
166 | or `update(posts.filter(id.eq(post.id)))`. We can see this in the generated SQL:
167 |
168 |
169 | ::: code-block
170 |
171 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L78-L93)
172 |
173 | ```sql
174 | UPDATE "posts" SET "draft" = $1 WHERE ("posts"."id" = $2) -- binds: [false, 1]
175 | ```
176 |
177 | :::
178 |
179 | Now that we've seen all the ways to specify what we want to update,
180 | let's look at the different ways to provide the data to update it with.
181 | We've already seen the first way, which is to pass `column.eq(value)` directly.
182 | So far we've just been passing Rust values here, but we can actually use any Diesel expression.
183 | For example, we could increment a column:
184 |
185 | ::: code-block
186 |
187 | [src/lib.rs](https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L95-L101)
188 |
189 | ```rust
190 | use crate::posts::dsl::*;
191 |
192 | diesel::update(posts)
193 | .set(visit_count.eq(visit_count + 1))
194 | .execute(conn)
195 | ```
196 |
197 | :::
198 |
199 | That would generate this SQL:
200 |
201 | ::: code-block
202 |
203 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L103-L113)
204 |
205 | ```sql
206 | UPDATE "posts" SET "visit_count" = ("posts"."visit_count" + $1) -- binds: [1]
207 | ```
208 |
209 | :::
210 |
211 | Assigning values directly is great for small, simple changes.
212 | If we wanted to update multiple columns this way, we can pass a tuple.
213 |
214 |
215 | ::: code-block
216 |
217 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L115-L124)
218 |
219 | ```rust
220 | use crate::posts::dsl::*;
221 |
222 | diesel::update(posts)
223 | .set((
224 | title.eq("[REDACTED]"),
225 | body.eq("This post has been classified"),
226 | ))
227 | .execute(conn)
228 | ```
229 |
230 | :::
231 |
232 | This will generate exactly the SQL you'd expect:
233 |
234 | ::: code-block
235 |
236 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L126-L139)
237 |
238 | ```sql
239 | UPDATE "posts" SET "title" = $1, "body" = $2 -- binds: ["[REDACTED]", "This post has been classified"]
240 | ```
241 |
242 | :::
243 |
244 | ## AsChangeset
245 |
246 | While it's nice to have the ability to update columns directly like this,
247 | it can quickly get cumbersome when dealing with forms that have more than a handful of fields.
248 | If we look at the signature of [`.set`], you'll notice that the constraint is for a trait called
249 | [`AsChangeset`]. This is another trait that `diesel` can derive for us. We can add
250 | `#[derive(AsChangeset)]` to our `Post` struct, which will let us pass a `&Post` to `set`.
251 |
252 | [`.set`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/struct.UpdateStatement.html#method.set
253 | [`AsChangeset`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/trait.AsChangeset.html
254 |
255 | ::: code-block
256 |
257 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L141-L143)
258 |
259 | ```rust
260 | diesel::update(posts::table).set(post).execute(conn)
261 | ```
262 |
263 | :::
264 |
265 | The SQL will set every field present on the `Post` struct except for the primary key.
266 |
267 | ::: code-block
268 |
269 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L145-L176)
270 |
271 | ```sql
272 | UPDATE "posts" SET "title" = $1, "body" = $2, "draft" = $3, "publish_at" = $4, "visit_count" = $5 -- binds: ["", "", false, now, 0]
273 | ```
274 |
275 | :::
276 |
277 | Changing the primary key of an existing row is almost never something that you want to do,
278 | so `#[derive(AsChangeset)]` assumes that you want to ignore it. The only way to change
279 | the primary key is to explicitly do it with `.set(id.eq(new_id))`. However,
280 | note that `#[derive(AsChangeset)]` doesn't have the information from your table definition.
281 | If the primary key is something other than `id`, you'll need to put
282 | `#[primary_key(your_primary_key)]` on the struct as well.
283 |
284 | If the struct has any optional fields on it, these will also have special behavior.
285 | By default, `#[derive(AsChangeset)]` will assume that `None` means that you don't wish
286 | to assign that field. For example, if we had the following code:
287 |
288 | ::: code-block
289 |
290 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L178-L192)
291 |
292 | ```rust
293 | #[derive(AsChangeset)]
294 | #[diesel(table_name = posts)]
295 | struct PostForm<'a> {
296 | title: Option<&'a str>,
297 | body: Option<&'a str>,
298 | }
299 |
300 | diesel::update(posts::table)
301 | .set(&PostForm {
302 | title: None,
303 | body: Some("My new post"),
304 | })
305 | .execute(conn)
306 | ```
307 |
308 | :::
309 |
310 | That would generate the following SQL:
311 |
312 | ::: code-block
313 |
314 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs#L194-L213)
315 |
316 | ```sql
317 | UPDATE "posts" SET "body" = $1 -- binds: ["My new post"]
318 | ```
319 |
320 | :::
321 |
322 | If you wanted to assign `NULL` instead, you can either specify `#[changeset_options(treat_none_as_null="true")]` on the struct, or you can have the field be of type `Option