├── CNAME ├── requirements.txt ├── .gitignore ├── src ├── guides │ ├── extending-diesel │ │ └── index.html │ ├── getting-started │ │ └── index.html │ ├── schema-in-depth │ │ └── index.html │ ├── all-about-inserts │ │ └── index.html │ ├── all-about-updates │ │ └── index.html │ ├── composing-applications │ │ └── index.html │ ├── configuring-diesel-cli │ │ └── index.html │ ├── index.md │ ├── configuring-diesel-cli.md │ ├── schema-in-depth.md │ ├── composing-applications.md │ ├── all-about-updates.md │ ├── extending-diesel.md │ ├── all-about-inserts.md │ ├── migration_guide.md │ └── getting-started.md ├── news │ ├── index.md │ └── 2_0_0_release.md ├── docs │ └── index.md ├── SUMMARY.md └── index.md ├── assets ├── images │ ├── diesel_logo_stacked_black.png │ ├── diesel_logo_stacked_black_500.png │ ├── liquid-bg.svg │ ├── extensible.svg │ ├── type-safe.svg │ ├── performance.svg │ └── logo.svg └── javascripts │ └── application.js ├── README.md ├── .github └── workflows │ └── ci.yml ├── LICENSE.md ├── Makefile ├── code-block-filter.py ├── diesel.theme ├── toml.xml ├── Makefile.venv └── template.html /CNAME: -------------------------------------------------------------------------------- 1 | diesel.rs 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandocfilters==1.5.0 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.venv 3 | /out 4 | -------------------------------------------------------------------------------- /src/guides/extending-diesel/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/guides/getting-started/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/guides/schema-in-depth/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/guides/all-about-inserts/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/guides/all-about-updates/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/guides/composing-applications/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/guides/configuring-diesel-cli/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/images/diesel_logo_stacked_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wulf/diesel.rs-website/master/assets/images/diesel_logo_stacked_black.png -------------------------------------------------------------------------------- /assets/images/diesel_logo_stacked_black_500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Wulf/diesel.rs-website/master/assets/images/diesel_logo_stacked_black_500.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # diesel.rs 2 | 3 | This site is built using [pandoc](https://pandoc.org/). 4 | The content is available here: https://diesel.rs 5 | 6 | Build locally 7 | ```sh 8 | make page 9 | ``` 10 | 11 | Master branch is automatically deployed to Github Pages. 12 | -------------------------------------------------------------------------------- /assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require_tree . 2 | 3 | var onReady = function() { 4 | var logo = document.querySelector(".logo") 5 | logo.addEventListener("mouseover", function(e) { 6 | logo.classList.add("animate"); 7 | }); 8 | 9 | logo.querySelector("#drop").addEventListener("animationend", function(e) { 10 | logo.classList.remove("animate"); 11 | }); 12 | }; 13 | 14 | document.addEventListener("DOMContentLoaded", onReady, false); 15 | -------------------------------------------------------------------------------- /src/news/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "News" 3 | lang: en-US 4 | css: ../assets/stylesheets/application.css 5 | include-after: | 6 | 7 | --- 8 | 9 | ::: content-wrapper 10 | ::: guide-wrapper 11 | 12 | #### [Diesel 2.0.0](./2_0_0_release.html) 13 | 14 | Diesel 2.0 introduces support for `GROUP BY` and `UNION`/`INTERSECT` queries. Additionally it features table aliases and a improved field mapping mechanism. 15 | 16 | ::: 17 | ::: 18 | -------------------------------------------------------------------------------- /src/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "API Documentation" 3 | css: ../assets/stylesheets/application.css 4 | include-after: | 5 | 6 | --- 7 | 8 | ::: content-wrapper 9 | ::: guide-wrapper 10 | 11 | [API Documentation (master branch)](https://docs.diesel.rs/master/diesel/index.html){.guides-link} 12 | [API Documentation (2.0.x release)](https://docs.diesel.rs/2.0.x/diesel/index.html){.guides-link} 13 | [API Documentation (1.4.x release)](https://docs.diesel.rs/1.4.x/diesel/index.html){.guides-link} 14 | 15 | ::: 16 | ::: 17 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [About Diesel](./index.md) 4 | 5 | --- 6 | 7 | # Guides 8 | 9 | - [Guides to Diesel](./guides/index.md) 10 | - [Getting started](./guides/getting-started.md) 11 | - [All About Updates](./guides/all-about-updates.md) 12 | - [All About Inserts](./guides/all-about-inserts.md) 13 | - [Composing Applications with Diesel](./guides/composing-applications.md) 14 | - [Schema in Depth](./guides/schema-in-depth.md) 15 | - [Extending Diesel](./guides/extending-diesel.md) 16 | - [Configuring Diesel CLI](./guides/configuring-diesel-cli.md) 17 | 18 | # Docs 19 | 20 | - [API documentation](./docs/index.md) 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [ master ] 7 | 8 | jobs: 9 | ci: 10 | name: CI 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Build the Webpage 15 | run: | 16 | sudo apt-get update 17 | sudo apt-get install -y pandoc python3-pandocfilters make 18 | pandoc --version 19 | make page 20 | - name: Deploy 21 | if: github.repository == 'sgrif/diesel.rs-website' && github.event_name == 'push' 22 | uses: JamesIves/github-pages-deploy-action@4.1.0 23 | with: 24 | branch: gh-pages # The branch the action should deploy to. 25 | folder: out # The folder the action should deploy. 26 | clean: true 27 | clean-exclude: | 28 | CNAME 29 | -------------------------------------------------------------------------------- /assets/images/liquid-bg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | liquid-bg 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2015–2020 Sean Griffin 4 | Copyright © 2014–2015 [thoughtbot, inc.](http://thoughtbot.com) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the “Software”), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | %.html: venv 2 | mkdir -p out/$(dir $@) 3 | . $(VENV)/activate && pandoc -t html5 --template=template.html -F code-block-filter.py src/$*.md -o out/$*.html -s --syntax-definition=toml.xml --highlight-style=diesel.theme 4 | 5 | page: index.html guides docs news changelog 6 | cp -R assets/ out 7 | 8 | guides: guides/all-about-updates.html guides/all-about-inserts.html guides/composing-applications.html guides/configuring-diesel-cli.html guides/extending-diesel.html guides/getting-started.html guides/index.html guides/schema-in-depth.html guides/migration_guide.html 9 | cp -R src/guides/all-about-inserts/ out/guides/all-about-inserts/ 10 | cp -R src/guides/all-about-updates/ out/guides/all-about-updates/ 11 | cp -R src/guides/composing-applications/ out/guides/composing-applications/ 12 | cp -R src/guides/configuring-diesel-cli/ out/guides/configuring-diesel-cli/ 13 | cp -R src/guides/extending-diesel/ out/guides/extending-diesel/ 14 | cp -R src/guides/getting-started/ out/guides/getting-started/ 15 | cp -R src/guides/schema-in-depth/ out/guides/schema-in-depth/ 16 | 17 | news: news/index.html news/2_0_0_release.html 18 | 19 | changelog: changelog.html 20 | 21 | docs: docs/index.html 22 | 23 | clean: 24 | rm out -r 25 | 26 | include Makefile.venv 27 | -------------------------------------------------------------------------------- /code-block-filter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Pandoc filter to convert all level 2+ headings to paragraphs with 5 | emphasized text. 6 | """ 7 | 8 | from pandocfilters import toJSONFilter, RawBlock, Div 9 | 10 | def html(x): 11 | return RawBlock('html', x) 12 | 13 | 14 | def behead(key, value, format, meta): 15 | if key == 'Div': 16 | [[ident, classes, kvs], content] = value 17 | if "code-block" in classes: 18 | [link, code_block] = content 19 | source_file = "" 20 | for s in link['c'][0]['c'][1]: 21 | if s['t'] == 'Str': 22 | source_file += str(s['c']) 23 | elif s['t'] == 'Space': 24 | source_file += ' ' 25 | target = link['c'][0]['c'][2][0] 26 | github_link = html('View on GitHub') 27 | browser_bar = Div([ident, ['browser-bar'], kvs], [html(source_file)]) 28 | if len(target.strip()) == 0: 29 | browser_content = [browser_bar, code_block] 30 | else: 31 | browser_content = [browser_bar, github_link, code_block] 32 | 33 | demo_example_browser = Div([ident, ['demo__example-browser'], kvs], 34 | browser_content) 35 | return Div([ident, ['demo__example'], kvs], [demo_example_browser]) 36 | 37 | if __name__ == "__main__": 38 | toJSONFilter(behead) 39 | -------------------------------------------------------------------------------- /assets/images/extensible.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | extensible 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/guides/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Guides to Diesel" 3 | lang: en-US 4 | css: ../assets/stylesheets/application.css 5 | include-after: | 6 | 7 | --- 8 | 9 | ::: content-wrapper 10 | ::: guide-wrapper 11 | 12 | #### [Getting Started](./getting-started.html) 13 | 14 | Everything you need to know to install and configure Diesel, and create your first application. 15 | 16 | #### [All About Updates](./all-about-updates.html) 17 | 18 | This guide covers everything you need to know about constructing UPDATE queries with Diesel. 19 | 20 | #### [All About Inserts](./all-about-inserts.html) 21 | 22 | Just like the last guide, but this guide covers INSERT queries. 23 | 24 | #### [Composing Applications with Diesel](./composing-applications.html) 25 | 26 | This guide covers how to structure your application, and best practices for reusing code. 27 | 28 | #### [Schema in Depth](./schema-in-depth.html) 29 | 30 | Ever wondered what exactly diesel print-schema and table! are doing? 31 | This guide will walk you through exactly what code gets generated, and how it's done. 32 | 33 | #### [Extending Diesel](./extending-diesel.html) 34 | 35 | Want to use a feature Diesel doesn't support? Have a user defined SQL function? 36 | Wish Diesel had custom query helpers (like pagination)? 37 | This guide will cover everything you need to know about extending Diesel 38 | with new functionality. 39 | 40 | #### [Configuring Diesel CLI](./configuring-diesel-cli.html) 41 | 42 | This guide will cover all available configuration options in `diesel.toml`, and what they do. 43 | 44 | #### [Diesel 2.0 migration guide](./migration_guide.html) 45 | 46 | This guide contains information about how to resolve breaking changes while updating from Diesel 47 | 1.4.x to Diesel 2.0 48 | 49 | ::: 50 | ::: 51 | -------------------------------------------------------------------------------- /assets/images/type-safe.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | document_magnify 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/guides/configuring-diesel-cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Configuring Diesel CLI" 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 | Diesel CLI is an optional tool Diesel provides to manage your 14 | database schema. Its main two roles are to run database 15 | migrations, and to create a Rust file which represents your 16 | database schema. 17 | 18 | The behavior of Diesel CLI can be configured through a toml file. By 19 | default Diesel will look for `diesel.toml` in the same directory as 20 | your `Cargo.toml` file. You can provide a different config file by 21 | setting the `DIESEL_CONFIG_FILE` environment variable, or passing 22 | `--config-file` on the command line. You can get a basic config file 23 | with some defaults provided by running `diesel setup`. 24 | 25 | As of Diesel 2.0, the file contains a single section, 26 | `[print_schema]`. All fields in this file are optional. 27 | 28 | ## The `file` field 29 | 30 | This field specifies the file where you want the Rust representation 31 | of your schema to live. When this field is present, commands which 32 | modify database schema (such as `diesel migration run`) will 33 | automatically run `diesel print-schema`, and output its results to 34 | this file. 35 | 36 | This means that you can modify your database schema without having to 37 | worry about running a separate command to update your Rust code. It is 38 | highly recommended that you use this field, to ensure that the Rust 39 | representation of your database schema is always in sync with what is 40 | actually in your database. Typically this is set to `src/schema.rs`. 41 | 42 | Unlike other fields, this doesn't actually modify the behavior of 43 | `diesel print-schema`. `diesel print-schema` will always output your 44 | schema to stdout, regardless of whether this field is present or not. 45 | 46 | ## The `with_docs` field 47 | 48 | When this field is set to `true`, `diesel print-schema` will act as 49 | though the `--with-docs` flag was passed by default. This places a doc 50 | comment on all tables and columns. 51 | 52 | ## The `filter` field 53 | 54 | This field specifies which tables should be output by `diesel 55 | print-schema`. It corresponds to the `--only-tables` and 56 | `--except-tables` on the command line. Its value should be a map with 57 | one of those two keys. For example: 58 | 59 | ::: code-block 60 | 61 | [diesel.toml]() 62 | 63 | ```toml 64 | [print_schema] 65 | # This will cause only the users and posts tables to be output 66 | filter = { only_tables = ["users", "posts"] } 67 | 68 | # This will cause all tables *except* the comments table to be 69 | # output 70 | filter = { except_tables = ["comments"] } 71 | ``` 72 | 73 | ::: 74 | 75 | ## The `schema` field 76 | 77 | Specifies which schema to use when searching for tables. When set, 78 | `diesel print-schema` will always behave as though `--schema` were 79 | passed. This field only affects PostgreSQL. If no value is provided, 80 | the `public` schema will be searched. 81 | 82 | ## The `generate_missing_sql_type_definitions` field 83 | 84 | This field accepts a boolean value, that configures whether `diesel print-schema` 85 | should generate definitions for missing sql types as part of the `schema.rs` file. 86 | The generated sql type definitions are placed in a separate `sql_types` module inside 87 | of the generated `schema.rs` file. This will generate only definitions for sql types actually 88 | used by any of the `table!` definitions generated by `diesel print-schema`. Those definitions 89 | automatically use the auto-generated sql type definition. 90 | 91 | If this field is not present, or set to `true` 92 | `diesel print-schema` will generate the corresponding definitions. This configuration 93 | only generates custom sql type definition for PostgreSQL based database systems, as 94 | SQLite and MySQL are using a fixed set of sql types. 95 | 96 | ::: code-block 97 | [diesel.toml]() 98 | 99 | ```toml 100 | [print-schema] 101 | # skip generating missing sql type definitions 102 | generate_missing_sql_type_definitions = false 103 | ``` 104 | ::: 105 | 106 | ## The `import_types` field 107 | 108 | This field adds `use` statements to the top of every `table!` 109 | declaration. When set, `diesel print-schema` will behave as if 110 | `--import-types` were passed. When no value is given, only types from 111 | `diesel::sql_types` will be imported. 112 | 113 | ::: code-block 114 | 115 | [diesel.toml]() 116 | 117 | ```toml 118 | [print_schema] 119 | # Add types from `diesel_full_text_search` like `tsvector` 120 | import_types = ["diesel::sql_types::*", "diesel_full_text_search::types::*"] 121 | ``` 122 | 123 | ::: 124 | 125 | ## The `patch_file` field 126 | 127 | Specifies a `.patch` file to be applied to your schema after it is 128 | generated. Corresponds to the `--patch-file` option on the command 129 | line. 130 | 131 | We can't provide an option for every possible customization to this 132 | file that you might want to make. This serves as a general purpose 133 | catch-all for schema customizations. 134 | 135 | The file should be a unified diff, which you can generate with `diff` 136 | or `git diff`. It's highly recommended that you provide more than 3 137 | context lines, especially if you have set `import_types`. 138 | 139 | You can easily generate this file by making the changes you want to 140 | `schema.rs`, and then running `git diff -U6 > src/schema.patch`. 141 | 142 | ::: 143 | ::: 144 | ::: 145 | -------------------------------------------------------------------------------- /diesel.theme: -------------------------------------------------------------------------------- 1 | { 2 | "text-color": "#cccccc", 3 | "background-color": "#303030", 4 | "line-number-color": null, 5 | "line-number-background-color": null, 6 | "text-styles": { 7 | "Other": { 8 | "text-color": "#efef8f", 9 | "background-color": null, 10 | "bold": false, 11 | "italic": false, 12 | "underline": false 13 | }, 14 | "Attribute": { 15 | "text-color": null, 16 | "background-color": null, 17 | "bold": false, 18 | "italic": false, 19 | "underline": false 20 | }, 21 | "SpecialString": { 22 | "text-color": "#cc9393", 23 | "background-color": null, 24 | "bold": false, 25 | "italic": false, 26 | "underline": false 27 | }, 28 | "Annotation": { 29 | "text-color": "#7f9f7f", 30 | "background-color": null, 31 | "bold": true, 32 | "italic": false, 33 | "underline": false 34 | }, 35 | "Function": { 36 | "text-color": "#efef8f", 37 | "background-color": null, 38 | "bold": false, 39 | "italic": false, 40 | "underline": false 41 | }, 42 | "String": { 43 | "text-color": "#b5bd68", 44 | "background-color": null, 45 | "bold": false, 46 | "italic": false, 47 | "underline": false 48 | }, 49 | "ControlFlow": { 50 | "text-color": "#f0dfaf", 51 | "background-color": null, 52 | "bold": false, 53 | "italic": false, 54 | "underline": false 55 | }, 56 | "Operator": { 57 | "text-color": "#f0efd0", 58 | "background-color": null, 59 | "bold": false, 60 | "italic": false, 61 | "underline": false 62 | }, 63 | "Error": { 64 | "text-color": "#c3bf9f", 65 | "background-color": null, 66 | "bold": false, 67 | "italic": false, 68 | "underline": false 69 | }, 70 | "BaseN": { 71 | "text-color": "#dca3a3", 72 | "background-color": null, 73 | "bold": false, 74 | "italic": false, 75 | "underline": false 76 | }, 77 | "Alert": { 78 | "text-color": "#ffcfaf", 79 | "background-color": null, 80 | "bold": false, 81 | "italic": false, 82 | "underline": false 83 | }, 84 | "Variable": { 85 | "text-color": null, 86 | "background-color": null, 87 | "bold": false, 88 | "italic": false, 89 | "underline": false 90 | }, 91 | "BuiltIn": { 92 | "text-color": "#de935f", 93 | "background-color": null, 94 | "bold": false, 95 | "italic": false, 96 | "underline": false 97 | }, 98 | "Extension": { 99 | "text-color": null, 100 | "background-color": null, 101 | "bold": false, 102 | "italic": false, 103 | "underline": false 104 | }, 105 | "Preprocessor": { 106 | "text-color": "#ffcfaf", 107 | "background-color": null, 108 | "bold": true, 109 | "italic": false, 110 | "underline": false 111 | }, 112 | "Information": { 113 | "text-color": "#7f9f7f", 114 | "background-color": null, 115 | "bold": true, 116 | "italic": false, 117 | "underline": false 118 | }, 119 | "VerbatimString": { 120 | "text-color": "#cc9393", 121 | "background-color": null, 122 | "bold": false, 123 | "italic": false, 124 | "underline": false 125 | }, 126 | "Warning": { 127 | "text-color": "#7f9f7f", 128 | "background-color": null, 129 | "bold": true, 130 | "italic": false, 131 | "underline": false 132 | }, 133 | "Documentation": { 134 | "text-color": "#7f9f7f", 135 | "background-color": null, 136 | "bold": false, 137 | "italic": false, 138 | "underline": false 139 | }, 140 | "Import": { 141 | "text-color": null, 142 | "background-color": null, 143 | "bold": false, 144 | "italic": false, 145 | "underline": false 146 | }, 147 | "Char": { 148 | "text-color": "#dca3a3", 149 | "background-color": null, 150 | "bold": false, 151 | "italic": false, 152 | "underline": false 153 | }, 154 | "DataType": { 155 | "text-color": "#de935f", 156 | "background-color": null, 157 | "bold": false, 158 | "italic": false, 159 | "underline": false 160 | }, 161 | "Float": { 162 | "text-color": "#c0bed1", 163 | "background-color": null, 164 | "bold": false, 165 | "italic": false, 166 | "underline": false 167 | }, 168 | "Comment": { 169 | "text-color": "#7f9f7f", 170 | "background-color": null, 171 | "bold": false, 172 | "italic": false, 173 | "underline": false 174 | }, 175 | "CommentVar": { 176 | "text-color": "#7f9f7f", 177 | "background-color": null, 178 | "bold": true, 179 | "italic": false, 180 | "underline": false 181 | }, 182 | "Constant": { 183 | "text-color": "#dca3a3", 184 | "background-color": null, 185 | "bold": true, 186 | "italic": false, 187 | "underline": false 188 | }, 189 | "SpecialChar": { 190 | "text-color": "#dca3a3", 191 | "background-color": null, 192 | "bold": false, 193 | "italic": false, 194 | "underline": false 195 | }, 196 | "DecVal": { 197 | "text-color": "#dcdccc", 198 | "background-color": null, 199 | "bold": false, 200 | "italic": false, 201 | "underline": false 202 | }, 203 | "Keyword": { 204 | "text-color": "#81a2be", 205 | "background-color": null, 206 | "bold": false, 207 | "italic": false, 208 | "underline": false 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /toml.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | ]> 13 | 14 | 15 | 16 | 17 | true 18 | false 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /Makefile.venv: -------------------------------------------------------------------------------- 1 | # 2 | # SEAMLESSLY MANAGE PYTHON VIRTUAL ENVIRONMENT WITH A MAKEFILE 3 | # 4 | # https://github.com/sio/Makefile.venv v2022.07.20 5 | # 6 | # 7 | # Insert `include Makefile.venv` at the bottom of your Makefile to enable these 8 | # rules. 9 | # 10 | # When writing your Makefile use '$(VENV)/python' to refer to the Python 11 | # interpreter within virtual environment and '$(VENV)/executablename' for any 12 | # other executable in venv. 13 | # 14 | # This Makefile provides the following targets: 15 | # venv 16 | # Use this as a dependency for any target that requires virtual 17 | # environment to be created and configured 18 | # python, ipython 19 | # Use these to launch interactive Python shell within virtual environment 20 | # shell, bash, zsh 21 | # Launch interactive command line shell. "shell" target launches the 22 | # default shell Makefile executes its rules in (usually /bin/sh). 23 | # "bash" and "zsh" can be used to refer to the specific desired shell. 24 | # show-venv 25 | # Show versions of Python and pip, and the path to the virtual environment 26 | # clean-venv 27 | # Remove virtual environment 28 | # $(VENV)/executable_name 29 | # Install `executable_name` with pip. Only packages with names matching 30 | # the name of the corresponding executable are supported. 31 | # Use this as a lightweight mechanism for development dependencies 32 | # tracking. E.g. for one-off tools that are not required in every 33 | # developer's environment, therefore are not included into 34 | # requirements.txt or setup.py. 35 | # Note: 36 | # Rules using such target or dependency MUST be defined below 37 | # `include` directive to make use of correct $(VENV) value. 38 | # Example: 39 | # codestyle: $(VENV)/pyflakes 40 | # $(VENV)/pyflakes . 41 | # See `ipython` target below for another example. 42 | # 43 | # This Makefile can be configured via following variables: 44 | # PY 45 | # Command name for system Python interpreter. It is used only initially to 46 | # create the virtual environment 47 | # Default: python3 48 | # REQUIREMENTS_TXT 49 | # Space separated list of paths to requirements.txt files. 50 | # Paths are resolved relative to current working directory. 51 | # Default: requirements.txt 52 | # 53 | # Non-existent files are treated as hard dependencies, 54 | # recipes for creating such files must be provided by the main Makefile. 55 | # Providing empty value (REQUIREMENTS_TXT=) turns off processing of 56 | # requirements.txt even when the file exists. 57 | # SETUP_PY 58 | # Space separated list of paths to setup.py files. 59 | # Corresponding packages will be installed into venv in editable mode 60 | # along with all their dependencies 61 | # Default: setup.py 62 | # 63 | # Non-existent and empty values are treated in the same way as for REQUIREMENTS_TXT. 64 | # WORKDIR 65 | # Parent directory for the virtual environment. 66 | # Default: current working directory. 67 | # VENVDIR 68 | # Python virtual environment directory. 69 | # Default: $(WORKDIR)/.venv 70 | # 71 | # This Makefile was written for GNU Make and may not work with other make 72 | # implementations. 73 | # 74 | # 75 | # Copyright (c) 2019-2020 Vitaly Potyarkin 76 | # 77 | # Licensed under the Apache License, Version 2.0 78 | # 79 | # 80 | 81 | 82 | # 83 | # Configuration variables 84 | # 85 | 86 | WORKDIR?=. 87 | VENVDIR?=$(WORKDIR)/.venv 88 | REQUIREMENTS_TXT?=$(wildcard requirements.txt) # Multiple paths are supported (space separated) 89 | SETUP_PY?=$(wildcard setup.py) # Multiple paths are supported (space separated) 90 | SETUP_CFG?=$(foreach s,$(SETUP_PY),$(wildcard $(patsubst %setup.py,%setup.cfg,$(s)))) 91 | MARKER=.initialized-with-Makefile.venv 92 | 93 | 94 | # 95 | # Python interpreter detection 96 | # 97 | 98 | _PY_AUTODETECT_MSG=Detected Python interpreter: $(PY). Use PY environment variable to override 99 | 100 | ifeq (ok,$(shell test -e /dev/null 2>&1 && echo ok)) 101 | NULL_STDERR=2>/dev/null 102 | else 103 | NULL_STDERR=2>NUL 104 | endif 105 | 106 | ifndef PY 107 | _PY_OPTION:=python3 108 | ifeq (ok,$(shell $(_PY_OPTION) -c "print('ok')" $(NULL_STDERR))) 109 | PY=$(_PY_OPTION) 110 | endif 111 | endif 112 | 113 | ifndef PY 114 | _PY_OPTION:=$(VENVDIR)/bin/python 115 | ifeq (ok,$(shell $(_PY_OPTION) -c "print('ok')" $(NULL_STDERR))) 116 | PY=$(_PY_OPTION) 117 | $(info $(_PY_AUTODETECT_MSG)) 118 | endif 119 | endif 120 | 121 | ifndef PY 122 | _PY_OPTION:=$(subst /,\,$(VENVDIR)/Scripts/python) 123 | ifeq (ok,$(shell $(_PY_OPTION) -c "print('ok')" $(NULL_STDERR))) 124 | PY=$(_PY_OPTION) 125 | $(info $(_PY_AUTODETECT_MSG)) 126 | endif 127 | endif 128 | 129 | ifndef PY 130 | _PY_OPTION:=py -3 131 | ifeq (ok,$(shell $(_PY_OPTION) -c "print('ok')" $(NULL_STDERR))) 132 | PY=$(_PY_OPTION) 133 | $(info $(_PY_AUTODETECT_MSG)) 134 | endif 135 | endif 136 | 137 | ifndef PY 138 | _PY_OPTION:=python 139 | ifeq (ok,$(shell $(_PY_OPTION) -c "print('ok')" $(NULL_STDERR))) 140 | PY=$(_PY_OPTION) 141 | $(info $(_PY_AUTODETECT_MSG)) 142 | endif 143 | endif 144 | 145 | ifndef PY 146 | define _PY_AUTODETECT_ERR 147 | Could not detect Python interpreter automatically. 148 | Please specify path to interpreter via PY environment variable. 149 | endef 150 | $(error $(_PY_AUTODETECT_ERR)) 151 | endif 152 | 153 | 154 | # 155 | # Internal variable resolution 156 | # 157 | 158 | VENV=$(VENVDIR)/bin 159 | EXE= 160 | # Detect windows 161 | ifeq (win32,$(shell $(PY) -c "import __future__, sys; print(sys.platform)")) 162 | VENV=$(VENVDIR)/Scripts 163 | EXE=.exe 164 | endif 165 | 166 | touch=touch $(1) 167 | ifeq (,$(shell command -v touch $(NULL_STDERR))) 168 | # https://ss64.com/nt/touch.html 169 | touch=type nul >> $(subst /,\,$(1)) && copy /y /b $(subst /,\,$(1))+,, $(subst /,\,$(1)) 170 | endif 171 | 172 | RM?=rm -f 173 | ifeq (,$(shell command -v $(firstword $(RM)) $(NULL_STDERR))) 174 | RMDIR:=rd /s /q 175 | else 176 | RMDIR:=$(RM) -r 177 | endif 178 | 179 | 180 | # 181 | # Virtual environment 182 | # 183 | 184 | .PHONY: venv 185 | venv: $(VENV)/$(MARKER) 186 | 187 | .PHONY: clean-venv 188 | clean-venv: 189 | -$(RMDIR) "$(VENVDIR)" 190 | 191 | .PHONY: show-venv 192 | show-venv: venv 193 | @$(VENV)/python -c "import sys; print('Python ' + sys.version.replace('\n',''))" 194 | @$(VENV)/pip --version 195 | @echo venv: $(VENVDIR) 196 | 197 | .PHONY: debug-venv 198 | debug-venv: 199 | @echo "PATH (Shell)=$$PATH" 200 | @$(MAKE) --version 201 | $(info PATH (GNU Make)="$(PATH)") 202 | $(info SHELL="$(SHELL)") 203 | $(info PY="$(PY)") 204 | $(info REQUIREMENTS_TXT="$(REQUIREMENTS_TXT)") 205 | $(info SETUP_PY="$(SETUP_PY)") 206 | $(info SETUP_CFG="$(SETUP_CFG)") 207 | $(info VENVDIR="$(VENVDIR)") 208 | $(info VENVDEPENDS="$(VENVDEPENDS)") 209 | $(info WORKDIR="$(WORKDIR)") 210 | 211 | 212 | # 213 | # Dependencies 214 | # 215 | 216 | ifneq ($(strip $(REQUIREMENTS_TXT)),) 217 | VENVDEPENDS+=$(REQUIREMENTS_TXT) 218 | endif 219 | 220 | ifneq ($(strip $(SETUP_PY)),) 221 | VENVDEPENDS+=$(SETUP_PY) 222 | endif 223 | ifneq ($(strip $(SETUP_CFG)),) 224 | VENVDEPENDS+=$(SETUP_CFG) 225 | endif 226 | 227 | $(VENV): 228 | $(PY) -m venv $(VENVDIR) 229 | $(VENV)/python -m pip install --upgrade pip setuptools wheel 230 | 231 | $(VENV)/$(MARKER): $(VENVDEPENDS) | $(VENV) 232 | ifneq ($(strip $(REQUIREMENTS_TXT)),) 233 | $(VENV)/pip install $(foreach path,$(REQUIREMENTS_TXT),-r $(path)) 234 | endif 235 | ifneq ($(strip $(SETUP_PY)),) 236 | $(VENV)/pip install $(foreach path,$(SETUP_PY),-e $(dir $(path))) 237 | endif 238 | $(call touch,$(VENV)/$(MARKER)) 239 | 240 | 241 | # 242 | # Interactive shells 243 | # 244 | 245 | .PHONY: python 246 | python: venv 247 | exec $(VENV)/python 248 | 249 | .PHONY: ipython 250 | ipython: $(VENV)/ipython 251 | exec $(VENV)/ipython 252 | 253 | .PHONY: shell 254 | shell: venv 255 | . $(VENV)/activate && exec $(notdir $(SHELL)) 256 | 257 | .PHONY: bash zsh 258 | bash zsh: venv 259 | . $(VENV)/activate && exec $@ 260 | 261 | 262 | # 263 | # Commandline tools (wildcard rule, executable name must match package name) 264 | # 265 | 266 | ifneq ($(EXE),) 267 | $(VENV)/%: $(VENV)/%$(EXE) ; 268 | .PHONY: $(VENV)/% 269 | .PRECIOUS: $(VENV)/%$(EXE) 270 | endif 271 | 272 | $(VENV)/%$(EXE): $(VENV)/$(MARKER) 273 | $(VENV)/pip install --upgrade $* 274 | $(call touch,$@) 275 | 276 | -------------------------------------------------------------------------------- /assets/images/performance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | dashboard 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /src/guides/schema-in-depth.md: -------------------------------------------------------------------------------- 1 | --- 2 | lang: en-US 3 | title: "Schema in Depth" 4 | css: ../assets/stylesheets/application.css 5 | include-after: | 6 | 7 | --- 8 | 9 | 10 | ::: demo 11 | ::: content-wrapper 12 | ::: guide-wrapper 13 | 14 | 15 | In this guide we're going to look at what exactly `diesel print-schema` 16 | and [`table!`] do. For `table!`, we will show a simplified version of the actual code 17 | that gets generated, and explain how each piece is relevant to you. 18 | If you've ever been confused about what exactly is getting generated, 19 | or what `use schema::posts::dsl::*` means, this is the right place to be. 20 | Another way to get an overview of which types are available where is to open 21 | the API documentation of your current crate via `cargo docs --open` and navigate 22 | to the relevant module there. 23 | 24 | `diesel print-schema` is a command provided by Diesel CLI. 25 | This command will establish a database connection, query for a list of all the tables 26 | and their columns, and generate `table!` invocations for each one. 27 | `diesel print-schema` will skip any table names which start with `__` (a double underscore). 28 | Diesel can be configured to automatically re-run `diesel print-schema` 29 | whenever you run migrations. See [Configuring Diesel CLI] for details. 30 | 31 | `table!` is where the bulk of the code gets generated. If you wanted to, 32 | you could see the actual exact code that gets generated 33 | by running `cargo +nightly rustc -- -Z unstable-options --pretty=expanded`. 34 | However, the output will be quite noisy, and there's a lot of code 35 | that won't actually be relevant to you. 36 | Instead we're going to go step by step through a *simplified* version of this output, 37 | which only has the code which you would use directly. 38 | 39 | For this example, we'll look at the code generated by this `table!` invocation: 40 | 41 | [Configuring Diesel CLI]: ./configuring-diesel-cli.html 42 | [`table!`]: https://docs.diesel.rs/2.0.x/diesel/macro.table.html 43 | 44 | ```rust 45 | table! { 46 | users { 47 | id -> Integer, 48 | name -> Text, 49 | hair_color -> Nullable, 50 | } 51 | } 52 | ``` 53 | 54 | If you just want to see the full simplified code and look through it yourself, 55 | you will find it at the end of this guide. 56 | 57 | The output of `table!` is always a Rust module with the same name. 58 | The first and most important part of this module will be the definition of the table itself: 59 | 60 | ```rust 61 | pub struct table; 62 | ``` 63 | 64 | This is the struct that represents the user's table for the purpose of constructing SQL queries. 65 | It's usually referenced in code as `users::table` (or sometimes just `users`, more on that in a bit). 66 | Next, we'll see there's a module called `columns`, with one struct per column of the table. 67 | 68 | ```rust 69 | pub struct id; 70 | pub struct name; 71 | pub struct hair_color; 72 | ``` 73 | 74 | Each of these structs uniquely represents each column of the table 75 | for the purpose of constructing SQL queries. 76 | Each of these structs will implement a trait called [`Expression`], 77 | which indicates the SQL type of the column. 78 | 79 | [`Expression`]: https://docs.diesel.rs/2.0.x/diesel/expression/trait.Expression.html 80 | 81 | ```rust 82 | impl Expression for id { 83 | type SqlType = Integer; 84 | } 85 | 86 | impl Expression for name { 87 | type SqlType = Text; 88 | } 89 | 90 | impl Expression for hair_color { 91 | type SqlType = Nullable; 92 | } 93 | ``` 94 | 95 | The `SqlType` type is at the core of how Diesel ensures that your queries are correct. 96 | This type will be used by [`ExpressionMethods`] to determine what things can and cannot 97 | be passed to methods like `eq`. It will also be used by [`Queryable`] to determine 98 | what types can be deserialized when this column appears in the select clause. 99 | 100 | [`ExpressionMethods`]: https://docs.diesel.rs/2.0.x/diesel/expression_methods/trait.ExpressionMethods.html 101 | [`Queryable`]: https://docs.diesel.rs/2.0.x/diesel/deserialize/trait.Queryable.html 102 | 103 | In the columns module you'll also see a special column called `star`. 104 | Its definition looks like this: 105 | 106 | ```rust 107 | pub struct star; 108 | 109 | impl Expression for star { 110 | type SqlType = NotSelectable; 111 | } 112 | ``` 113 | 114 | The `star` struct represents `users.*` in the query builder. 115 | This struct is only intended to be used for generating count queries. 116 | It should never be used directly. Diesel loads your data from a query by index, 117 | not by name. In order to ensure that we're actually getting the data 118 | for the column we think we are, Diesel never uses `*` when we actually want to 119 | get the data back out of it. We will instead generate an explicit select clause 120 | such as `SELECT users.id, users.name, users.hair_color`. 121 | 122 | Everything in the columns module will be re-exported from the parent module. 123 | This is why we can reference columns as `users::id`, and not `users::columns::id`. 124 | 125 | ```rust 126 | pub use self::columns::*; 127 | 128 | pub struct table; 129 | 130 | pub mod columns { 131 | /* ... */ 132 | } 133 | ``` 134 | 135 | Queries can often get quite verbose when everything has to be prefixed with `users::`. 136 | For this reason, Diesel also provides a convenience module called `dsl`. 137 | 138 | ```rust 139 | pub mod dsl { 140 | pub use super::columns::{id, name, hair_color}; 141 | pub use super::table as users; 142 | } 143 | ``` 144 | 145 | This module re-exports everything in `columns` module (except for `star`), 146 | and also re-exports the table but renamed to the actual name of the table. 147 | This means that instead of writing: 148 | 149 | ```rust 150 | users::table 151 | .filter(users::name.eq("Sean")) 152 | .filter(users::hair_color.eq("black")) 153 | ``` 154 | 155 | we can instead write: 156 | 157 | ```rust 158 | users.filter(name.eq("Sean")).filter(hair_color.eq("black")) 159 | ``` 160 | 161 | The `dsl` module should only ever be imported for single functions. 162 | You should never have `use schema::users::dsl::*;` at the top of a module. 163 | Code like `#[derive(Insertable)]` will assume that `users` points to the module, 164 | not the table struct. 165 | 166 | Since `star` is otherwise inaccessible if you have `use schema::users::dsl::*;`, 167 | it is also exposed as an instance method on the table. 168 | 169 | ```rust 170 | impl table { 171 | pub fn star(&self) -> star { 172 | star 173 | } 174 | } 175 | ``` 176 | 177 | Next, there are several traits that get implemented for `table`. 178 | You generally will never interact with these directly, 179 | but they are what enable most of the query builder functions 180 | found in [the `query_dsl` module], 181 | as well as use with `insert`, `update`, and `delete`. 182 | 183 | [the `query_dsl` module]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/index.html 184 | 185 | ```rust 186 | impl AsQuery for table { 187 | /* body omitted */ 188 | } 189 | 190 | impl Table for table { 191 | /* body omitted */ 192 | } 193 | 194 | impl IntoUpdateTarget for table { 195 | /* body omitted */ 196 | } 197 | ``` 198 | 199 | Finally, there are a few small type definitions 200 | and constants defined to make your life easier. 201 | 202 | ```rust 203 | pub const all_columns: (id, name, hair_color) = (id, name, hair_color); 204 | 205 | pub type SqlType = (Integer, Text, Nullable); 206 | 207 | pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>; 208 | ``` 209 | 210 | `all_columns` is just a tuple of all of the column on the table. 211 | It is what is used to generate the `select` statement for a query on this table 212 | when you don't specify one explicitly. If you ever want to reference `users::star` 213 | for things that aren't count queries, you probably want `users::all_columns` instead. 214 | 215 | `SqlType` will be the SQL type of `all_columns`. 216 | It's rare to need to reference this directly, 217 | but it's less verbose than `<::AllColumns as Expression>::SqlType` 218 | when you need it. 219 | 220 | Finally, there is a helper type for referencing boxed queries built from this table. 221 | This means that instead of writing `diesel::dsl::IntoBoxed<'static, users::table, Pg>` 222 | you can instead write `users::BoxedQuery<'static, Pg>`. 223 | You can optionally specify the SQL type as well if the query has a custom select clause. 224 | 225 | And that's everything! Here is the full code that was generated for this table: 226 | 227 | ```rust 228 | pub mod users { 229 | pub use self::columns::*; 230 | 231 | pub mod dsl { 232 | pub use super::columns::{id, name, hair_color}; 233 | pub use super::table as users; 234 | } 235 | 236 | pub const all_columns: (id, name, hair_color) = (id, name, hair_color); 237 | 238 | pub struct table; 239 | 240 | impl table { 241 | pub fn star(&self) -> star { 242 | star 243 | } 244 | } 245 | 246 | pub type SqlType = (Integer, Text, Nullable); 247 | 248 | pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, FromClause, 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>`. Diesel doesn't currently provide a way to explicitly assign a field to its default value, though it may be provided in the future. 323 | 324 | If you are using PostgreSQL or SQLite, all of these options will work with `INSERT ON CONFLICT DO UPDATE` 325 | as well. See the [upsert docs] for more details. 326 | 327 | [upsert docs]: https://docs.diesel.rs/2.0.x/diesel/upsert/index.html 328 | 329 | ## Executing your query 330 | 331 | Once you've constructed your query, we need to actually execute it. 332 | There are several different methods to do this, depending on what type you'd like back. 333 | 334 | The simplest method for running your query is [`execute`]. This method will run your query, 335 | and return the number of rows that were affected. This is the method you should use 336 | if you simply want to ensure that the query executed successfully, 337 | and don't care about getting anything back from the database. 338 | 339 | [`execute`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#tymethod.execute 340 | 341 | For queries where you do want to get data back from the database, 342 | we need to use [`get_result`] or [`get_results`]. If you haven't explicitly called [`returning`], 343 | these methods will return all of the columns on the table. Similar to [`load`] on a select statement, 344 | you will need to specify the type you'd like to deserialize to (either a tuple or a struct with 345 | `#[derive(Queryable)]`). You should use [`get_results`] when you are expecting more than one record back. 346 | If you are only expecting a single record, you can call [`get_result`] instead. 347 | 348 | It should be noted that receiving 0 rows from [`get_result`] is considered an error condition by default. 349 | If you want to get back 0 or 1 row (e.g. have a return type of `QueryResult>`), 350 | then you will need to call `.get_result(...).optional()`. 351 | 352 | [`get_result`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#method.get_result 353 | [`get_results`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#method.get_results 354 | [`returning`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/update_statement/struct.UpdateStatement.html#method.returning 355 | [`load`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#method.load 356 | 357 | Finally, if your struct has both `#[derive(AsChangeset)]` and `#[derive(Identifiable)]`, 358 | you will be able to use the [`save_changes`] method. Unlike the other methods mentioned in this guide, 359 | you do not explicitly build a query when using [`save_changes`]. 360 | Doing `foo.save_changes(&conn)` is equivalent to doing `diesel::update(&foo).set(&foo).get_result(&conn)`. 361 | Like [`get_result`] and [`get_results`], you will need to specify the type you'd like to get back. 362 | 363 | [`save_changes`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.SaveChangesDsl.html#method.save_changes 364 | 365 | All of the code for this guide can be found in executable form in [this Diesel example]. 366 | 367 | [this Diesel example]: https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_updates/src/lib.rs 368 | 369 | ::: 370 | ::: 371 | ::: 372 | -------------------------------------------------------------------------------- /assets/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo 5 | Created with Sketch. 6 | 7 | 8 | 9 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(keywords)$ 14 | 15 | $endif$ 16 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 17 | 18 | $if(quotes)$ 19 | 20 | $endif$ 21 | $if(highlighting-css)$ 22 | 25 | $endif$ 26 | $for(css)$ 27 | 28 | $endfor$ 29 | $if(math)$ 30 | $math$ 31 | $endif$ 32 | 35 | $for(header-includes)$ 36 | $header-includes$ 37 | $endfor$ 38 | 39 | 40 |
41 | 89 |
90 | $for(include-before)$ 91 | $include-before$ 92 | $endfor$ 93 | $if(title)$ 94 | 101 | $endif$ 102 | 103 | $body$ 104 | $for(include-after)$ 105 | $include-after$ 106 | $endfor$ 107 |
108 |
109 |

Found a mistake on this website? Submit an issue or send a pull request 110 | here!

111 |

112 |
113 |

Copyright © 2015-2022 The Diesel Core Team

114 |
115 |
116 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/guides/extending-diesel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Extending 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 | Diesel provides a lot of capabilities out of the box. 14 | However, it doesn't necessarily provide everything your app may want to use. 15 | One of Diesel's greatest strengths 16 | is that it can be extended to add new functionality. 17 | 18 | In this guide we'll look at several ways to hook into Diesel's query builder, 19 | both to add new capabilities, 20 | and to introduce new abstractions. 21 | 22 | This guide is only going to cover extending the query builder. 23 | How to add support for new SQL types will be covered in a future guide. 24 | 25 | ## `sql_function!` 26 | 27 | The easiest and most common way to extend Diesel's query builder 28 | is by declaring a new SQL function. 29 | This can be used for functions defined by your database, 30 | or for built-in functions that Diesel doesn't support out of the box. 31 | 32 | Functions in SQL often have multiple signatures, 33 | making them difficult or impossible to represent in Rust. 34 | Because of this, Diesel only provides support for a small number 35 | of built-in SQL functions. 36 | Consider `COALESCE`. 37 | This function can take any number of arguments, 38 | and its return type changes based on whether any arguments are `NOT NULL`. 39 | While we can't easily represent that in Rust, 40 | we can use [`sql_function!`] to declare it with the exact signature we're using. 41 | 42 | [`sql_function!`]: https://docs.diesel.rs/2.0.x/diesel/expression/functions/macro.sql_function.html 43 | 44 | ::: code-block 45 | 46 | [Example]() 47 | 48 | ```rust 49 | use diesel::sql_types::{Nullable, Text}; 50 | sql_function! { fn coalesce(x: Nullable, y: Text) -> Text; } 51 | 52 | users.select(coalesce(hair_color, "blue")) 53 | ``` 54 | 55 | ::: 56 | 57 | As this example shows, 58 | [`sql_function!`] converts its argument like other parts of the query builder. 59 | This means that the generated function can take both Diesel expressions, 60 | and Rust values to be sent with the query. 61 | 62 | The macro takes one argument: a function definition. 63 | However, the types in the function signature are SQL types, 64 | not concrete Rust types. 65 | This is what allows us to pass both columns and Rust strings. 66 | If we defined this function manually, it would look like this: 67 | 68 | ::: code-block 69 | 70 | [Example]() 71 | 72 | ```rust 73 | pub fn coalesce(x: X, y: Y) -> coalesce::HelperType 74 | where 75 | X: AsExpression>, 76 | Y: AsExpression, 77 | { 78 | ... 79 | } 80 | 81 | pub(crate) mod coalesce { 82 | pub type HelperType = ...; 83 | } 84 | ``` 85 | 86 | ::: 87 | 88 | A helper type is generated with the same name as the function. 89 | This helper type handles Diesel's argument conversion. 90 | This lets us write `coalesce`. 91 | More information can be found in [the API documentation](https://docs.diesel.rs/2.0.x/diesel/expression/functions/macro.sql_function.html) 92 | 93 | ## Using Custom SQL and How to Extend the Query DSL 94 | 95 | Often times it's useful to encapsulate a common SQL pattern. 96 | For example, if you're doing pagination on your queries, 97 | PostgreSQL is capable of loading the total count in a single query. 98 | The query you would want to execute would look like this: 99 | 100 | ::: code-block 101 | 102 | [Example]() 103 | 104 | ```sql 105 | SELECT *, COUNT(*) OVER () FROM (subselect t) as paged_query_with LIMIT $1 OFFSET $2 106 | ``` 107 | 108 | ::: 109 | 110 | However, as of version 2.0, 111 | Diesel doesn't support window functions, or selecting from a subselect. 112 | Even if Diesel's query builder supported those things, 113 | this is a case that is easier to reason about in terms of the SQL we want to 114 | generate. 115 | 116 | Let's look at how we would go about adding a `paginate` method to Diesel's query 117 | builder, to generate that query. 118 | Let's assume for the time being that we have a struct `Paginated` already. 119 | We'll look at the specifics of this struct shortly. 120 | 121 | If you are creating a struct where you want to manually define the SQL, 122 | you will need to implement a trait called [`QueryFragment`]. 123 | The implementation will look like this: 124 | 125 | [`QueryFragment`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/trait.QueryFragment.html 126 | 127 | ::: code-block 128 | 129 | [src/pagination.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/advanced-blog-cli/src/pagination.rs#L60-L73) 130 | 131 | ```rust 132 | impl QueryFragment for Paginated 133 | where 134 | T: QueryFragment, 135 | { 136 | fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { 137 | out.push_sql("SELECT *, COUNT(*) OVER () FROM ("); 138 | self.query.walk_ast(out.reborrow())?; 139 | out.push_sql(") as paged_query_with LIMIT "); 140 | out.push_bind_param::(&self.per_page)?; 141 | out.push_sql(" OFFSET "); 142 | out.push_bind_param::(&self.offset)?; 143 | Ok(()) 144 | } 145 | } 146 | ``` 147 | 148 | ::: 149 | 150 | For details on what each method does, 151 | see the documentation for [`AstPass`]. 152 | One important question to ask whenever you implement `QueryFragment` 153 | is whether you are generating a query that is safe to cache. 154 | The way to answer this question is by asking 155 | "does this struct generate an unlimited number of potential SQL queries"? 156 | Typically that is only the case if the body of `walk_ast` contains a for loop. 157 | If your query is not safe to cache, you *must* call 158 | [`out.unsafe_to_cache_prepared`]. 159 | 160 | Whenever you implement `QueryFragment`, you also need to implement [`QueryId`]. 161 | - We can use [`#[derive(QueryId)]`] for this. 162 | Since this struct represents a full query which can be executed, 163 | we will implement [`RunQueryDsl`] which adds methods like [`execute`] and [`load`]. 164 | Since this query has a return type, 165 | we'll implement [`Query`] which states the return type as well. 166 | 167 | [`QueryId`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/trait.QueryId.html 168 | [`AstPass`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/struct.AstPass.html 169 | [`impl_query_id!`]: https://docs.diesel.rs/2.0.x/diesel/macro.impl_query_id.html 170 | [`RunQueryDsl`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html 171 | [`execute`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#method.execute 172 | [`load`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#method.load 173 | [`Query`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/trait.Query.html 174 | [`#[derive(QueryId)]`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/derive.QueryId.html 175 | [`out.unsafe_to_cache_prepared`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/struct.AstPass.html#method.unsafe_to_cache_prepared 176 | 177 | 178 | ::: code-block 179 | 180 | [src/pagination.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/advanced-blog-cli/src/pagination.rs#L54-L58) 181 | 182 | ```rust 183 | impl Query for Paginated { 184 | type SqlType = (T::SqlType, BigInt); 185 | } 186 | 187 | impl RunQueryDsl for Paginated {} 188 | ``` 189 | 190 | ::: 191 | 192 | Now that we've implemented all of these things, 193 | let's look at how we would go about constructing this. 194 | We'll want to add a `paginate` method to all Diesel queries, 195 | which specifies which page we're on, 196 | as well as a `per_page` method which specifies the number of elements per page. 197 | 198 | In order to add new methods to existing types, we can use a trait. 199 | 200 | ::: code-block 201 | 202 | [src/pagination.rs]( https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/advanced-blog-cli/src/pagination.rs#L7-L39) 203 | 204 | ```rust 205 | pub trait Paginate: Sized { 206 | fn paginate(self, page: i64) -> Paginated; 207 | } 208 | 209 | impl Paginate for T { 210 | fn paginate(self, page: i64) -> Paginated { 211 | Paginated { 212 | query: self, 213 | per_page: DEFAULT_PER_PAGE, 214 | page, 215 | offset: (page - 1) * DEFAULT_PER_PAGE, 216 | } 217 | } 218 | } 219 | 220 | const DEFAULT_PER_PAGE: i64 = 10; 221 | 222 | #[derive(Debug, Clone, Copy, QueryId)] 223 | pub struct Paginated { 224 | query: T, 225 | page: i64, 226 | per_page: i64, 227 | offset: i64, 228 | } 229 | 230 | impl Paginated { 231 | pub fn per_page(self, per_page: i64) -> Self { 232 | Paginated { 233 | per_page, 234 | offset: (self.page - 1) * per_page, 235 | ..self 236 | } 237 | } 238 | } 239 | ``` 240 | 241 | ::: 242 | 243 | Now we can get the third page of a query with 25 elements per page like this: 244 | 245 | ::: code-block 246 | 247 | [Example]() 248 | 249 | ```rust 250 | let results: Vec<(User, i64)> = users::table 251 | .paginate(3) 252 | .per_page(25) 253 | .get_results(&conn) 254 | .expect("error"); 255 | ``` 256 | 257 | ::: 258 | 259 | With this code, 260 | we could load any query into a `Vec<(T, i64)>`, 261 | but we can do better. 262 | When doing pagination, 263 | you usually want the records and the total number of pages. 264 | We can write that method. 265 | 266 | ::: code-block 267 | 268 | [src/pagination.rs]( https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/advanced-blog-cli/src/pagination.rs#L41-L51) 269 | 270 | ```rust 271 | impl Paginated { 272 | pub fn load_and_count_pages<'a, U>(self, conn: &mut PgConnection) -> QueryResult<(Vec, i64)> 273 | where 274 | Self: LoadQuery<'a, PgConnection, (U, i64)>, 275 | { 276 | let per_page = self.per_page; 277 | let results = self.load::<(U, i64)>(conn)?; 278 | let total = results.get(0).map(|x| x.1).unwrap_or(0); 279 | let records = results.into_iter().map(|x| x.0).collect(); 280 | let total_pages = (total as f64 / per_page as f64).ceil() as i64; 281 | Ok((records, total_pages)) 282 | } 283 | } 284 | ``` 285 | 286 | ::: 287 | 288 | This is one of the rare cases where we want to define a function that takes a 289 | connection. 290 | One benefit of defining the function this way 291 | is that if we wanted to support backends other than PostgreSQL, 292 | we could have this function execute two queries. 293 | 294 | You can find the full code for this example in [the "advanced blog" example]. 295 | 296 | [the "advanced blog" example]: https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/advanced-blog-cli 297 | 298 | ## Custom Operators 299 | 300 | If you're adding support for a new type to Diesel, 301 | or working with a type that has incomplete support, 302 | you may wish to add support for the operators associated with that type. 303 | The term operator refers to anything that uses one of these syntaxes: 304 | 305 | - Infix (e.g. `left OP right`) 306 | - Prefix (e.g. `OP expr`) 307 | - Postfix (e.g. `expr OP`) 308 | 309 | Diesel provides helper macros for defining each of these kinds of operators. 310 | In fact, Diesel uses these macros to declare nearly all of the operators 311 | supported by the main crate. 312 | The macros are 313 | [`diesel::infix_operator!`], [`diesel::postfix_operator!`] and 314 | [`diesel::prefix_operator!`]. 315 | 316 | [`diesel::infix_operator!`]: https://docs.diesel.rs/2.0.x/diesel/macro.infix_operator.html 317 | [`diesel::postfix_operator!`]: https://docs.diesel.rs/2.0.x/diesel/macro.postfix_operator.html 318 | [`diesel::prefix_operator!`]: https://docs.diesel.rs/2.0.x/diesel/macro.prefix_operator.html 319 | 320 | All of these macros have the same signature. 321 | They take between two and four arguments. 322 | 323 | The first is the name of the struct you want to represent this operator. 324 | 325 | The second is the actual SQL for this operator. 326 | 327 | The third argument is optional, and is the SQL type of the operator. 328 | If the SQL type is not specified, it will default to `Bool`. 329 | You can also pass the "magic" type `ReturnBasedOnArgs`, 330 | which will cause the SQL type to be the same as the type of its arguments. 331 | Diesel uses this to make the string concatenation operator `||` 332 | return `Nullable` if the arguments are nullable, 333 | or `Text` if they are not null. 334 | 335 | The fourth argument (or third if you didn't specify the SQL type) 336 | is the backend this operator is used for. 337 | If you don't specify a backend, 338 | the operator can be used on all backends. 339 | 340 | Let's look at some example usage from Diesel: 341 | 342 | ::: code-block 343 | 344 | [Example]() 345 | 346 | ```rust 347 | // A simple operator. It returns `Bool` and works on all backends. 348 | diesel::infix_operator!(Eq, " = "); 349 | 350 | // Here we've specified the SQL type. 351 | // Since this operator is only used for ordering, and we don't want it used 352 | // elsewhere, we've made it `()` which is normally useless. 353 | diesel::postfix_operator!(Asc, " ASC", ()); 354 | 355 | // Concat uses the magic `ReturnBasedOnArgs` return type 356 | // so it can work with both `Text` and `Nullable`. 357 | diesel::infix_operator!(Concat, " || ", ReturnBasedOnArgs); 358 | 359 | // This operator is PG specific, so we specify the backend 360 | diesel::infix_operator!(IsDistinctFrom, " IS DISTINCT FROM ", backend: Pg); 361 | 362 | // This operator is PG specific, and we are also specifying the SQL type. 363 | diesel::postfix_operator!(NullsFirst, " NULLS FIRST", (), backend: Pg); 364 | ``` 365 | 366 | ::: 367 | 368 | Diesel provides a proof-of-concept crate showing how to add new SQL types called 369 | `diesel_full_text_search`. 370 | These are the operators as they are defined in that crate. 371 | You'll notice all of the operators specify the backend, 372 | and many of them specify the return type. 373 | 374 | ::: code-block 375 | 376 | [src/lib.rs]( https://github.com/diesel-rs/diesel_full_text_search/blob/27b9946831caa8b08177c1818a50cb7f0563c9c0/src/lib.rs#L57-L62) 377 | 378 | ```rust 379 | diesel::infix_operator!(Matches, " @@ ", backend: Pg); 380 | diesel::infix_operator!(Concat, " || ", TsVector, backend: Pg); 381 | diesel::infix_operator!(And, " && ", TsQuery, backend: Pg); 382 | diesel::infix_operator!(Or, " || ", TsQuery, backend: Pg); 383 | diesel::infix_operator!(Contains, " @> ", backend: Pg); 384 | diesel::infix_operator!(ContainedBy, " <@ ", backend: Pg); 385 | ``` 386 | 387 | ::: 388 | 389 | However, just declaring the operator by itself isn't very useful. 390 | This creates the types required by Diesel's query builder, 391 | but doesn't provide anything to help use the operator in real code. 392 | The structs created by these macros will have a `new` method, 393 | but that's not typically how you work with Diesel's query builder. 394 | 395 | - Infix operators are usually methods on the left hand side. 396 | - Postfix operators are usually methods on the expression. 397 | - Prefix operators are usually bare functions. 398 | 399 | For operators that you create with methods, 400 | you would typically create a trait for this. 401 | For example, here's how the `.eq` method gets defined by Diesel. 402 | 403 | ::: code-block 404 | 405 | [src/expression_methods/global_expression_methods.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/diesel/src/expression_methods/global_expression_methods.rs#L9-L31) 406 | 407 | ```rust 408 | pub trait ExpressionMethods: Expression + Sized { 409 | fn eq(self, other: T) -> dsl::Eq 410 | where 411 | Self::SqlType: SqlType, 412 | T: AsExpression, 413 | { 414 | Grouped(Eq::new(self, other.as_expression())) 415 | } 416 | } 417 | 418 | impl ExpressionMethods for T {} 419 | ``` 420 | 421 | ::: 422 | 423 | It's important to note that these methods are where you should put any type 424 | constraints. 425 | The structs defined by `diesel::*_operator!` don't know or care about what the 426 | types of the arguments should be. 427 | The `=` operator requires that both sides be of the same type, 428 | so we represent that in the type of `ExpressionMethods::eq`. 429 | 430 | You'll also notice that our argument is 431 | [`AsExpression`], 432 | not [`Expression`]. 433 | This allows Rust values to be passed as well as Diesel expressions. 434 | For example, we can do `text_col.eq(other_text_col)`, 435 | or `text_col.eq("Some Rust string")`. 436 | 437 | [`AsExpression`]: https://docs.diesel.rs/2.0.x/diesel/expression/trait.AsExpression.html 438 | [`Expression`]: https://docs.diesel.rs/2.0.x/diesel/prelude/trait.Expression.html 439 | 440 | If the operator is specific to only one SQL type, 441 | we can represent that in our trait. 442 | 443 | ::: code-block 444 | 445 | [src/expression_methods/global_expression_methods.rs](https://github.com/diesel-rs/diesel/blob/master/diesel/src/expression_methods/bool_expression_methods.rs) 446 | 447 | ```rust 448 | pub trait BoolExpressionMethods 449 | where 450 | Self: Expression + Sized, 451 | { 452 | fn and(self, other: T) -> dsl::And 453 | where 454 | Self::SqlType: SqlType, 455 | ST: SqlType + TypedExpressionType, 456 | T: AsExpression, 457 | And: Expression, 458 | { 459 | Grouped(And::new(self, other.as_expression())) 460 | } 461 | } 462 | 463 | impl BoolExpressionMethods for T 464 | where 465 | T: Expression, 466 | T::SqlType: BoolOrNullableBool, 467 | { 468 | } 469 | ``` 470 | 471 | ::: 472 | 473 | Prefix operators are usually defined as bare functions. 474 | The code is very similar, but without the trait. 475 | Here's how `not` is defined in Diesel. 476 | 477 | ::: code-block 478 | 479 | [src/expression/not.rs](https://github.com/diesel-rs/diesel/blob/master/diesel/src/expression/not.rs#L26-L32) 480 | 481 | ```rust 482 | pub fn not(expr: T) -> not 483 | where 484 | T: Expression, 485 | ::SqlType: BoolOrNullableBool, 486 | { 487 | super::operators::Not::new(Grouped(expr)) 488 | } 489 | ``` 490 | 491 | ::: 492 | 493 | In this case we're using `Grouped` 494 | (which is currently undocumented in Diesel and only used internally) 495 | to add parenthesis around our argument. 496 | This ensures that the operator precedence in SQL matches what's expected. 497 | For example, we would expect `not(true.and(false))` to return `true`. 498 | However, `SELECT NOT TRUE AND FALSE` returns `FALSE`. 499 | Diesel does the same thing with `.or`. 500 | 501 | It's also a best practice to expose a "helper type" for your method, 502 | which does the same type conversion as the method itself. 503 | Nobody wants to write `Eq>::Expression>`. 504 | Instead, we provide a type that lets you write `Eq`. 505 | 506 | ::: code-block 507 | 508 | [src/expression/helper_types.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/diesel/src/expression/helper_types.rs#L21) 509 | 510 | ```rust 511 | pub type Eq = Grouped>>; 512 | ``` 513 | 514 | ::: 515 | 516 | For defining these types, 517 | you'll usually want to make use of [`SqlTypeOf`], [`AsExpr`], and [`AsExprOf`]. 518 | 519 | [`SqlTypeOf`]: https://docs.diesel.rs/2.0.x/diesel/helper_types/type.SqlTypeOf.html 520 | [`AsExpr`]: https://docs.diesel.rs/2.0.x/diesel/helper_types/type.AsExpr.html 521 | [`AsExprOf`]: https://docs.diesel.rs/2.0.x/diesel/helper_types/type.AsExprOf.html 522 | 523 | 524 | ::: 525 | ::: 526 | ::: 527 | -------------------------------------------------------------------------------- /src/guides/all-about-inserts.md: -------------------------------------------------------------------------------- 1 | --- 2 | lang: "en-US" 3 | title: "All About Inserts" 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. 14 | CRUD stands for "Create, Read, Update, Delete". 15 | Diesel provides support for all four pieces, 16 | but in this guide we're going to look at 17 | the different ways to go about creating `INSERT` statements. 18 | 19 | The examples for this guide are going to be shown for PostgreSQL, 20 | but you can follow along with any backend. 21 | The full code examples for all backends are linked at the bottom of this guide. 22 | 23 | An insert statement always starts with [`insert_into`]. 24 | The first argument to this function is the table you're inserting into. 25 | 26 | [`insert_into`]: https://docs.diesel.rs/2.0.x/diesel/fn.insert_into.html 27 | 28 | For this guide, our schema will look like this: 29 | 30 | ::: code-block 31 | 32 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L11-L21 33 | ) 34 | 35 | ```rust 36 | diesel::table! { 37 | users { 38 | id -> Integer, 39 | name -> Text, 40 | hair_color -> Nullable, 41 | created_at -> Timestamp, 42 | updated_at -> Timestamp, 43 | } 44 | } 45 | ``` 46 | 47 | ::: 48 | 49 | Since our functions are going to only operate on the `users` table, 50 | we can put `use schema::users::dsl::*;` at the top of our function, 51 | which will let us write `insert_into(users)` instead of 52 | `insert_into(users::table)`. 53 | If you're importing `table::dsl::*`, 54 | make sure it's always inside a function, not the top of your module. 55 | 56 | If all of the columns on a table have a default, 57 | the simplest thing we can do is call [`.default_values`]. 58 | We could write a function that ran that query like this: 59 | 60 | [`.default_values`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/insert_statement/struct.IncompleteInsertStatement.html#method.default_values 61 | 62 | ::: code-block 63 | 64 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L41-L45) 65 | 66 | ```rust 67 | use schema::users::dsl::*; 68 | 69 | insert_into(users).default_values().execute(conn) 70 | ``` 71 | 72 | ::: 73 | 74 | It's worth noting that this code will still compile, 75 | even if you don't have default values on all of your columns. 76 | Diesel will ensure that the value you're assigning has the right type, 77 | but it can't validate whether the column has a default, 78 | any constraints that could fail, 79 | or any triggers that could fire. 80 | 81 | We can use [`debug_query`] to inspect the generated SQL. 82 | The exact SQL that is generated may differ depending on the backend you're using. 83 | If we run `println!("{}", debug_query::(&our_query));`, 84 | we'll see the following: 85 | 86 | [`debug_query`]: https://docs.diesel.rs/2.0.x/diesel/fn.debug_query.html 87 | 88 | ::: code-block 89 | 90 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L47-54) 91 | 92 | 93 | ```sql 94 | INSERT INTO "users" DEFAULT VALUES -- binds: [] 95 | ``` 96 | 97 | ::: 98 | 99 | If we want to actually provide values, we can call [`.values`] instead. 100 | There are a lot of different arguments we can provide here. 101 | The simplest is a single column/value pair using [`.eq`]. 102 | 103 | [`.values`]: https://docs.diesel.rs/2.0.x/diesel/query_builder/insert_statement/struct.IncompleteInsertStatement.html#method.values 104 | [`.eq`]: https://docs.diesel.rs/2.0.x/diesel/expression_methods/trait.ExpressionMethods.html#method.eq 105 | 106 | ::: code-block 107 | 108 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L56-L60 109 | ) 110 | 111 | ```rust 112 | use schema::users::dsl::*; 113 | 114 | insert_into(users).values(name.eq("Sean")).execute(conn) 115 | ``` 116 | 117 | ::: 118 | 119 | This will generate the following SQL: 120 | 121 | ::: code-block 122 | 123 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L62-L70) 124 | 125 | ```sql 126 | INSERT INTO "users" ("name") VALUES ($1) 127 | -- binds ["Sean"] 128 | ``` 129 | 130 | ::: 131 | 132 | If we want to provide values for more than one column, we can pass a tuple. 133 | 134 | ::: code-block 135 | 136 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L72-L78) 137 | 138 | ```rust 139 | insert_into(users) 140 | .values((name.eq("Tess"), hair_color.eq("Brown"))) 141 | .execute(conn) 142 | ``` 143 | 144 | ::: 145 | 146 | This will generate the following SQL: 147 | 148 | ::: code-block 149 | 150 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L80-L88) 151 | 152 | ```sql 153 | INSERT INTO "users" ("name", "hair_color") VALUES ($1, $2) 154 | -- binds: ["Tess", "Brown"] 155 | ``` 156 | 157 | ::: 158 | 159 | ## Insertable 160 | 161 | Working with tuples is the typical way to do an insert 162 | if you just have some values that you want to stick in the database. 163 | But what if your data is coming from another source, 164 | like a web form deserialized by Serde? 165 | It'd be annoying to have to write 166 | `(name.eq(user.name), hair_color.eq(user.hair_color))`. 167 | 168 | Diesel provides the [`Insertable`] trait for this case. 169 | `Insertable` maps your struct to columns in the database. 170 | We can derive this automatically by adding `#[derive(Insertable)]` to our type. 171 | 172 | [`Insertable`]: https://docs.diesel.rs/2.0.x/diesel/prelude/trait.Insertable.html 173 | 174 | ::: code-block 175 | 176 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L23-L30) 177 | 178 | ```rust 179 | use schema::users; 180 | 181 | #[derive(Deserialize, Insertable)] 182 | #[diesel(table_name = users)] 183 | pub struct UserForm<'a> { 184 | name: &'a str, 185 | hair_color: Option<&'a str>, 186 | } 187 | ``` 188 | 189 | ::: 190 | 191 | ::: code-block 192 | 193 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L90-L99) 194 | 195 | ```rust 196 | use schema::users::dsl::*; 197 | 198 | let json = r#"{ "name": "Sean", "hair_color": "Black" }"#; 199 | let user_form = serde_json::from_str::(json)?; 200 | 201 | insert_into(users).values(&user_form).execute(conn)?; 202 | 203 | Ok(()) 204 | ``` 205 | 206 | ::: 207 | 208 | This will generate the same SQL as if we had used a tuple. 209 | 210 | ::: code-block 211 | 212 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L101-L111) 213 | 214 | ```sql 215 | INSERT INTO "users" ("name", "hair_color") VALUES ($1, $2) 216 | -- binds: ["Sean", "Black"] 217 | ``` 218 | 219 | ::: 220 | 221 | If one of the fields is `None`, the default value will be inserted for that field. 222 | 223 | ::: code-block 224 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L113-L122) 225 | 226 | ```rust 227 | use schema::users::dsl::*; 228 | 229 | let json = r#"{ "name": "Ruby", "hair_color": null }"#; 230 | let user_form = serde_json::from_str::(json)?; 231 | 232 | insert_into(users).values(&user_form).execute(conn)?; 233 | 234 | Ok(()) 235 | ``` 236 | 237 | ::: 238 | 239 | That will generate the following SQL: 240 | 241 | ::: code-block 242 | 243 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L124-L134) 244 | 245 | ```sql 246 | INSERT INTO "users" ("name", "hair_color") VALUES ($1, DEFAULT) 247 | -- binds: ["Ruby"] 248 | ``` 249 | 250 | ::: 251 | 252 | ## Batch Insert 253 | 254 | If we want to insert more than one row at a time, 255 | we can do that by passing a `&Vec` or slice of any of the forms used above. 256 | Keep in mind that you're always passing a reference here. 257 | 258 | On backends that support the `DEFAULT` keyword (all backends except SQLite), 259 | the data will be inserted in a single query. 260 | On SQLite, one query will be performed per row. 261 | 262 | For example, if we wanted to insert two rows with a single value, 263 | we can just use a `Vec`. 264 | 265 | ::: code-block 266 | 267 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L136-L142) 268 | 269 | ```rust 270 | use schema::users::dsl::*; 271 | 272 | insert_into(users) 273 | .values(&vec![name.eq("Sean"), name.eq("Tess")]) 274 | .execute(conn) 275 | ``` 276 | ::: 277 | 278 | Which generates the following SQL: 279 | 280 | ::: code-block 281 | 282 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L144-L153) 283 | 284 | ```sql 285 | INSERT INTO "users" ("name") VALUES ($1), ($2) 286 | -- binds ["Sean", "Tess"] 287 | ``` 288 | 289 | ::: 290 | 291 | Note that on SQLite, you won't be able to use `debug_query` for this, 292 | since it doesn't map to a single query. You can inspect each row like this: 293 | 294 | ::: code-block 295 | 296 | [src/lib.rs]() 297 | 298 | ```rust 299 | for row in &values { 300 | let query = insert_into(users).values(row); 301 | println!("{}", debug_query::(&query)); 302 | } 303 | ``` 304 | 305 | ::: 306 | 307 | If we wanted to use `DEFAULT` for some of our rows, we can use an option here. 308 | 309 | 310 | ::: code-block 311 | 312 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L155-L161) 313 | 314 | ```rust 315 | use schema::users::dsl::*; 316 | 317 | insert_into(users) 318 | .values(&vec![Some(name.eq("Sean")), None]) 319 | .execute(conn) 320 | ``` 321 | 322 | ::: 323 | 324 | Note that the type here is `Option>` not `Eq>`. 325 | Doing `column.eq(None)` would insert `NULL` not `DEFAULT`. 326 | This generates the following SQL: 327 | 328 | ::: code-block 329 | 330 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L163-L172) 331 | 332 | ```sql 333 | INSERT INTO "users" ("name") VALUES ($1), (DEFAULT) 334 | -- binds ["Sean"] 335 | ``` 336 | 337 | ::: 338 | 339 | We can do the same thing with tuples. 340 | 341 | ::: code-block 342 | 343 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L174-L183) 344 | 345 | ```rust 346 | use schema::users::dsl::*; 347 | 348 | insert_into(users) 349 | .values(&vec![ 350 | (name.eq("Sean"), hair_color.eq("Black")), 351 | (name.eq("Tess"), hair_color.eq("Brown")), 352 | ]) 353 | .execute(conn) 354 | ``` 355 | 356 | ::: 357 | 358 | Which generates the following SQL: 359 | 360 | 361 | ::: code-block 362 | 363 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L185-L198) 364 | 365 | ```sql 366 | INSERT INTO "users" ("name", "hair_color") 367 | VALUES ($1, $2), ($3, $4) 368 | -- binds: ["Sean", "Black", "Tess", "Brown"] 369 | ``` 370 | 371 | ::: 372 | 373 | Once again, we can use an `Option` for any of the fields to insert `DEFAULT`. 374 | 375 | ::: code-block 376 | 377 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L200-L209) 378 | 379 | ```rust 380 | use schema::users::dsl::*; 381 | 382 | insert_into(users) 383 | .values(&vec![ 384 | (name.eq("Sean"), Some(hair_color.eq("Black"))), 385 | (name.eq("Ruby"), None), 386 | ]) 387 | .execute(conn) 388 | ``` 389 | 390 | ::: 391 | 392 | Which generates the following SQL: 393 | 394 | ::: code-block 395 | 396 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L211-L224) 397 | 398 | ```sql 399 | INSERT INTO "users" ("name", "hair_color") 400 | VALUES ($1, $2), ($3, DEFAULT) 401 | -- binds: ["Sean", "Black", "Ruby"] 402 | ``` 403 | 404 | ::: 405 | 406 | Finally, `Insertable` structs can be used for batch insert as well. 407 | 408 | ::: code-block 409 | 410 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L226-L238) 411 | 412 | ```rust 413 | use schema::users::dsl::*; 414 | 415 | let json = r#"[ 416 | { "name": "Sean", "hair_color": "Black" }, 417 | { "name": "Tess", "hair_color": "Brown" } 418 | ]"#; 419 | let user_form = serde_json::from_str::>(json)?; 420 | 421 | insert_into(users).values(&user_form).execute(conn)?; 422 | 423 | Ok(()) 424 | ``` 425 | 426 | ::: 427 | 428 | This generates the same SQL as if we had used a tuple: 429 | 430 | ::: code-block 431 | 432 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L211-L224) 433 | 434 | ```sql 435 | INSERT INTO "users" ("name", "hair_color") 436 | VALUES ($1, $2), ($3, $4) 437 | -- binds: ["Sean", "Black", "Tess", "Brown"] 438 | ``` 439 | 440 | ::: 441 | 442 | ## The `RETURNING` Clause 443 | 444 | On backends that support the `RETURNING` clause (such as PostgreSQL and SQLite), 445 | we can get data back from our insert as well. 446 | On the SQLite backend, support for the `RETURNING` clause can be 447 | enabled with a feature flag, `returning_clauses_for_sqlite_3_35`. 448 | MySQL does not support `RETURNING` clauses. 449 | To get back all of the inserted rows, 450 | we can call [`.get_results`] instead of [`.execute`]. 451 | 452 | [`.get_results`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#method.get_results 453 | [`.execute`]: https://docs.diesel.rs/2.0.x/diesel/query_dsl/trait.RunQueryDsl.html#method.execute 454 | 455 | Given this struct: 456 | 457 | ::: code-block 458 | 459 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L32-L39) 460 | 461 | ```rust 462 | #[derive(Queryable, PartialEq, Debug)] 463 | struct User { 464 | id: i32, 465 | name: String, 466 | hair_color: Option, 467 | created_at: SystemTime, 468 | updated_at: SystemTime, 469 | } 470 | ``` 471 | 472 | ::: 473 | 474 | We can use `get_results` with this test: 475 | 476 | ::: code-block 477 | 478 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L256-L292) 479 | 480 | ```rust 481 | use diesel::select; 482 | use schema::users::dsl::*; 483 | 484 | let now = select(diesel::dsl::now).get_result::(conn)?; 485 | 486 | let inserted_users = insert_into(users) 487 | .values(&vec![ 488 | (id.eq(1), name.eq("Sean")), 489 | (id.eq(2), name.eq("Tess")), 490 | ]) 491 | .get_results(conn)?; 492 | 493 | let expected_users = vec![ 494 | User { 495 | id: 1, 496 | name: "Sean".into(), 497 | hair_color: None, 498 | created_at: now, 499 | updated_at: now, 500 | }, 501 | User { 502 | id: 2, 503 | name: "Tess".into(), 504 | hair_color: None, 505 | created_at: now, 506 | updated_at: now, 507 | }, 508 | ]; 509 | assert_eq!(expected_users, inserted_users); 510 | ``` 511 | 512 | ::: 513 | 514 | To inspect the SQL generated by `.get_results` or `.get_result`, 515 | we will need to call `.as_query` before passing it to `debug_query`. 516 | The query in the last test generates the following SQL: 517 | 518 | ::: code-block 519 | 520 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L294-L306) 521 | 522 | ```sql 523 | INSERT INTO "users" ("id", "name") VALUES ($1, $2), ($3, $4) 524 | RETURNING "users"."id", "users"."name", "users"."hair_color", 525 | "users"."created_at", "users"."updated_at" 526 | -- binds: [1, "Sean", 2, "Tess"] 527 | ``` 528 | 529 | ::: 530 | 531 | You'll notice that we've never given an explicit value for `created_at` and 532 | `updated_at` in any of our examples. 533 | With Diesel, you typically won't set those values in Rust. 534 | Typically these columns get set with `DEFAULT CURRENT_TIMESTAMP`, 535 | and a trigger is used to change `updated_at` on updates. 536 | If you're using PostgreSQL, you can use a built-in trigger 537 | by running `SELECT diesel_manage_updated_at('users');` in a migration. 538 | 539 | If we expect one row instead of multiple, we can call `.get_result` instead of 540 | `.get_results`. 541 | 542 | ::: code-block 543 | 544 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L308-L332) 545 | 546 | ```rust 547 | use diesel::select; 548 | use schema::users::dsl::*; 549 | 550 | let now = select(diesel::dsl::now).get_result::(conn)?; 551 | 552 | let inserted_user = insert_into(users) 553 | .values((id.eq(3), name.eq("Ruby"))) 554 | .get_result(conn)?; 555 | 556 | let expected_user = User { 557 | id: 3, 558 | name: "Ruby".into(), 559 | hair_color: None, 560 | created_at: now, 561 | updated_at: now, 562 | }; 563 | assert_eq!(expected_user, inserted_user); 564 | ``` 565 | 566 | ::: 567 | 568 | This generates the same SQL as `get_results`: 569 | 570 | ::: code-block 571 | 572 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L334-L347) 573 | 574 | ```sql 575 | INSERT INTO "users" ("id", "name") VALUES ($1, $2) 576 | RETURNING "users"."id", "users"."name", "users"."hair_color", 577 | "users"."created_at", "users"."updated_at" 578 | -- binds: [3, "Ruby"] 579 | ``` 580 | 581 | ::: 582 | 583 | Finally, if we only want a single column back, we can call `.returning()` explicitly. 584 | This code would return the inserted ID: 585 | 586 | ::: code-block 587 | 588 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L349-L356) 589 | 590 | ```rust 591 | use schema::users::dsl::*; 592 | 593 | insert_into(users) 594 | .values(name.eq("Ruby")) 595 | .returning(id) 596 | .get_result(conn) 597 | ``` 598 | 599 | ::: 600 | 601 | Which generates the following SQL: 602 | 603 | ::: code-block 604 | 605 | [Generated SQL](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/all_about_inserts/src/lib.rs#L358-L367) 606 | 607 | ```sql 608 | INSERT INTO "users" ("name") VALUES ($1) 609 | RETURNING "users"."id" 610 | -- binds: ["Ruby"] 611 | ``` 612 | 613 | ::: 614 | 615 | ## "Upsert" 616 | 617 | Every type of insert statement covered in this guide can also be used for 618 | "insert or update" queries, also known as "upsert". 619 | The specifics of upsert are covered extensively in the API documentation. 620 | 621 | For PostgreSQL and SQLite, see the [`diesel::upsert`] module. 622 | For MySQL, upsert is done via `REPLACE`. 623 | See [`replace_into`] for details. 624 | 625 | Diesel does not have support for MySQL's `ON DUPLICATE KEY` conflict, 626 | as its results are non-deterministic, and unsafe with replication. 627 | 628 | [`diesel::upsert`]: https://docs.diesel.rs/2.0.x/diesel/upsert/index.html 629 | [`replace_into`]: https://docs.diesel.rs/2.0.x/diesel/fn.replace_into.html 630 | 631 | ## Conclusion 632 | 633 | While there are a lot of examples in this guide, 634 | ultimately the only difference between various kinds of insert statements 635 | is the argument passed to `.values`. 636 | 637 | All examples in this guide are run as part of Diesel's test suite. 638 | You can find the full code examples for each backend at these links: 639 | 640 | - [PostgreSQL] 641 | - [MySQL] 642 | - [SQLite] 643 | 644 | [PostgreSQL]: https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/all_about_inserts 645 | [MySQL]: https://github.com/diesel-rs/diesel/tree/2.0.x/examples/mysql/all_about_inserts 646 | [SQLite]: https://github.com/diesel-rs/diesel/tree/2.0.x/examples/sqlite/all_about_inserts 647 | 648 | 649 | ::: 650 | ::: 651 | ::: 652 | -------------------------------------------------------------------------------- /src/guides/migration_guide.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Diesel 2.0 migration guide" 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 | Diesel 2.0 introduces substantial changes to Diesel's inner workings. 14 | In some cases this impacts code written using Diesel 1.4.x. 15 | This document outlines notable changes and presents potential update strategies. 16 | We recommend to start the upgrade by removing the usage of all items that 17 | are marked as deprecated in Diesel 1.4.x. 18 | 19 | Any code base migrating to Diesel 2.0 is expected to be affected at least by 20 | the following changes: 21 | 22 | * [Diesel now requires a mutable reference to the connection](#2-0-0-mutable-connection) 23 | * [Changed derive attributes](#2-0-0-derive-attributes) 24 | 25 | Users of `diesel_migration` are additionally affected by the following change: 26 | 27 | * [`diesel_migration` rewrite](#2-0-0-upgrade-migrations) 28 | 29 | Users of `BoxableExpression` might be affected by the following change: 30 | 31 | * [Changed nullability of operators](#2-0-0-nullability-ops) 32 | 33 | Users of tables containing a column of the type `Array` are affected by the following change: 34 | 35 | * [Changed nullability of array elemetns](#2-0-0-nullability-of-array-elements) 36 | 37 | Users that implement support for their SQL types or type mappings are affected 38 | by the following changes: 39 | 40 | * [Changed required traits for custom SQL types](#2-0-0-custom-type-implementation) 41 | * [Changed `ToSql` implementations](#2-0-0-to-sql) 42 | * [Changed `FromSql` implementations](#2-0-0-from-sql) 43 | 44 | `no_arg_sql_function!` macro is now pending deprecation. 45 | Users of the macro are advised to consider `sql_function!` macro. 46 | 47 | * [Deprecated usage of `no_arg_sql_function!` macro](#2-0-0-no_arg_sql_function) 48 | 49 | Users of `eq_any` on the PostgreSQL backend might hit type rejection error in rare cases. 50 | 51 | * [Changed accepted argument to `eq_any()` for the PostgreSQL backend](#2-0-0-changed_eq_any) 52 | 53 | Users that update generic Diesel code will also be affected by the following changes: 54 | 55 | * [Removing `NonAggregate` in favor of `ValidGrouping`](#2-0-0-upgrade-non-aggregate) 56 | * [Changed generic bounds](#2-0-0-generic-changes) 57 | 58 | Additionally this release contains many changes for users that implemented a custom backend/connection. 59 | We do not provide explicit migration steps but we encourage users to reach out with questions pertaining to these changes. 60 | 61 | 62 | ## Mutable Connections required 63 | 64 | Diesel now requires mutable access to the [`Connection`] to perform any database interaction. The following changes 65 | are required for all usages of any [`Connection`] type: 66 | 67 | [`Connection`]: http://docs.diesel.rs/2.0.x/diesel/connection/trait.Connection.html 68 | 69 | ::: code-block 70 | 71 | [Change]() 72 | 73 | ```diff 74 | - let connection = PgConnection::establish_connection("…")?; 75 | - let result = some_query.load(&connection)?; 76 | + let mut connection = PgConnection::establish_connection("…")?; 77 | + let result = some_query.load(&mut connection)?; 78 | ``` 79 | 80 | ::: 81 | 82 | We expect this to be a straightforward change as the connection already can execute only one query at a time. 83 | 84 | 85 | ## Derive attributes 86 | 87 | We have updated all of our Diesel derive attributes to follow the patterns that are used 88 | widely in the Rust's ecosystem. This means that all of them need to be wrapped by `#[diesel()]` now. You can now specify multiple attributes on the same line using `,` separator. 89 | 90 | This is backward compatible and thus all of your old attributes will still work, but with 91 | warnings. The attributes can be upgraded by either looking at the warnings or by reading 92 | diesel derive documentation reference. 93 | 94 | ## `diesel_migration` rewrite 95 | 96 | We have completely rewritten the `diesel_migration` crate. As a part of this rewrite all 97 | free standing functions are removed from `diesel_migration`. Equivalent functionality 98 | is now provided by the [`MigrationHarness`] trait, which is implemented for any [`Connection` ] 99 | type and for [`HarnessWithOutput`]. Refer to their documentations for details. 100 | 101 | Additionally, this rewrite changed the way we provide migrations. Instead of having our own implementation 102 | for file based and embedded migration we now provide a unified [`MigrationSource`] trait to abstract 103 | over the differences. `diesel_migration` provides two implementations: 104 | 105 | * [`FileBasedMigrations`], which mirrors the existing behaviour to load raw sql migrations at run time 106 | form a specific directory 107 | * [`EmbeddedMigrations`], which mirrors the existing [`embed_migrations!()`] macro. 108 | 109 | Finally the [`embed_migrations!()`] macro itself changed. Instead of generating a magical embedded module 110 | it now generates an instance of [`EmbeddedMigrations`], that could be stored in a constant for example. 111 | 112 | That means code using [`embed_migrations!()`] needs to be changed from 113 | 114 | [`MigrationHarness`]: http://docs.diesel.rs/2.0.x/diesel_migrations/trait.MigrationHarness.html 115 | [`HarnessWithOutput`]: http://docs.diesel.rs/2.0.x/diesel_migrations/struct.HarnessWithOutput.html 116 | [`FileBasedMigrations`]: http://docs.diesel.rs/2.0.x/diesel_migrations/struct.FileBasedMigrations.html 117 | [`EmbeddedMigrations`]: http://docs.diesel.rs/2.0.x/diesel_migrations/struct.EmbeddedMigrations.html 118 | [`embed_migrations!()`]: http://docs.diesel.rs/2.0.x/diesel_migrations/macro.embed_migrations.html 119 | [`MigrationSource`]: http://docs.diesel.rs/2.0.x/diesel/migration/trait.MigrationSource.html 120 | 121 | ::: code-block 122 | [Change]() 123 | 124 | ```rust 125 | embed_migrations!() 126 | 127 | fn run_migration(conn: &PgConnection) { 128 | embedded_migrations::run(conn).unwrap() 129 | } 130 | ``` 131 | ::: 132 | to 133 | 134 | ::: code-block 135 | 136 | [Change]() 137 | ```rust 138 | pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); 139 | 140 | fn run_migration(conn: &mut PgConnection) { 141 | conn.run_pending_migrations(MIGRATIONS).unwrap(); 142 | } 143 | ``` 144 | ::: 145 | 146 | ## Changed nullability of operators 147 | 148 | We changed the way how we handle the propagation of null values through binary operators. Diesel 1.x always assumed 149 | that the result of a binary operation `value_a > value_b` is not nullable, which does not match the behaviour of the 150 | underlying databases. `value_a > null` may return a `NULL` value there. With Diesel 2.0 we changed this to match more 151 | closely the behaviour of the underlying databases. We expect this change to have the biggest impact on existing usages 152 | of `BoxableExpression` as it may change the resulting sql type there. As a possible workaround for divering sql types 153 | there we recommend to use one of the following functions: 154 | 155 | * [`NullableExpressionMethods::nullable()`](http://docs.diesel.rs/2.0.x/diesel/expression_methods/trait.NullableExpressionMethods.html#method.nullable) 156 | * [`NullableExpressionMethods::assume_not_null()`](http://docs.diesel.rs/2.0.x/diesel/expression_methods/trait.NullableExpressionMethods.html#method.assume_not_null) 157 | 158 | ## Changed nullability of array elements 159 | 160 | We changed the inferred SQL type for columns with array types for the PostgreSQL backend. Instead of using `Array` 161 | we now infer `Array>` to support arrays containing `NULL` values. This change implies a change mapping 162 | of columns of the corresponding types. It is possible to handle this change using one of the following strategies: 163 | 164 | * Use `Vec>` as rust side type instead of `Vec` 165 | * Manually set the corresponding column to `Array` in your schema, to signal that this array does not contain null values. You may want to use the [`patch_file`] key for diesel CLI for this. 166 | * Use [`#[diesel(deserialize_as = "…")]`] to explicitly overwrite the deserialization implementation used for this specific struct field. Checkout the documentation of [`#[derive(Queryable)]`] for details. 167 | 168 | [`patch_file`]: ./configuring-diesel-cli.html 169 | [`#[diesel(deserialize_as = "…")]`]: http://docs.diesel.rs/2.0.x/diesel/deserialize/derive.Queryable.html#optional-field-attributes 170 | [`#[derive(Queryable)]`]: http://docs.diesel.rs/2.0.x/diesel/deserialize/derive.Queryable.html 171 | 172 | ## Custom SQL type implementations 173 | 174 | We changed how we mark sql types as nullable at type level. For this we replaced the `NonNull` trait with a 175 | more generic [`SqlType`] trait, which allows to mark a sql type as (non-) nullable. This may affect custom 176 | sql type implementations. 177 | 178 | Users that already use the existing [`#[derive(SqlType)]`] do not need to change any code. The derive internally 179 | generates the correct code after the update. Users that use a manual implementation of `NonNull` need to replace 180 | it with a corresponding [`SqlType`] implementation: 181 | 182 | [`SqlType`]: http://docs.diesel.rs/2.0.x/diesel/sql_types/trait.SqlType.html 183 | [`#[derive(SqlType)]`]: http://docs.diesel.rs/2.0.x/diesel/sql_types/derive.SqlType.html 184 | 185 | ::: code-block 186 | [Change]() 187 | ```diff 188 | - impl NonNull for MyCustomSqlType {} 189 | + impl SqlType for MyCustomSqlType { 190 | + type IsNull = diesel::sql_types::is_nullable::NotNull; 191 | + } 192 | ``` 193 | 194 | ::: 195 | 196 | Additionally, the diesel CLI tool was changed so that it automatically generates the Rust side definition of custom SQL types 197 | as long as they appear on any table. This feature currently only supports the PostgreSQL backend, as all other supported backends 198 | do not support real custom types at SQL level at all. 199 | 200 | ## Changed `ToSql` implementations 201 | 202 | We restructured the way Diesel serializes Rust values to their backend specific representation. 203 | This enables us to skip copying the value at all if the specific backend supports writing to a 204 | shared buffer. Unfortunately, this feature requires changes to the [`ToSql`] trait. This change introduces 205 | a lifetime that ensures that a value implementing [`ToSql`] outlives the underlying serialisation buffer. 206 | Additionally we separated the output buffer type for Sqlite from the type used for PostgreSQL and Mysql. 207 | 208 | This has the implication that for generic implementations using a inner existing [`ToSql`] implementation you cannot 209 | create temporary values anymore and forward them to the inner implementation. 210 | 211 | For backend concrete implementations, the following functions allow You to work around this limitation: 212 | 213 | * [`Output::reborrow()`] for the `Pg` and `Mysql` backend 214 | * [`Output::set_value()`] for the `Sqlite` backend (Refer to the documentation of [`SqliteBindValue`] for accepted values) 215 | 216 | [`ToSql`]: http://docs.diesel.rs/2.0.x/diesel/serialize/trait.ToSql.html 217 | [`Output::reborrow()`]: http://docs.diesel.rs/2.0.x/diesel/serialize/struct.Output.html#method.reborrow 218 | [`Output::set_value()`]: http://docs.diesel.rs/2.0.x/diesel/serialize/struct.Output.html#method.set_value 219 | [`SqliteBindValue`]: http://docs.diesel.rs/2.0.x/diesel/sqlite/struct.SqliteBindValue.html 220 | 221 | 222 | ## Changed `FromSql` implementations 223 | 224 | We changed the raw value representation for both PostgreSQL and MySQL 225 | backends, from a `&[u8]` to an opaque type. This allows us to include additional information like the database side 226 | type there. This change enables users to write [`FromSql`] implementations that decide dynamically what kind of value 227 | was received. The new value types for both backends expose a `as_bytes()` method to access the underlying byte buffer. 228 | 229 | [`FromSql`]: http://docs.diesel.rs/2.0.x/diesel/deserialize/trait.FromSql.html 230 | 231 | Any affected backend needs to perform the following changes: 232 | 233 | ::: code-block 234 | [Change]() 235 | ```diff 236 | impl FromSql for YourType { 237 | - fn from_sql(bytes: &[u8]) -> deserialize::Result { 238 | + fn from_sql(value: backend::RawValue<'_, DB>) -> deserialize::Result { 239 | + let bytes = value.as_bytes(); 240 | // … 241 | } 242 | } 243 | ``` 244 | ::: 245 | 246 | 247 | ## `no_arg_sql_function` 248 | 249 | The [`no_arg_sql_function!`] was deprecated without direct replacement. At the same time the 250 | [`sql_function!`] macro gained support for sql functions without argument. This support generates slightly 251 | different code. Instead of representing the sql function as zero sized struct, [`sql_function!`] will generate an ordinary function call without arguments. This requires changing any usage of the generated dsl. This change 252 | affects all of the usages of the [`no_arg_sql_function!`] in third party crates. 253 | 254 | [`no_arg_sql_function!`]: http://docs.diesel.rs/2.0.x/diesel/macro.no_arg_sql_function.html 255 | [`sql_function!`]: http://docs.diesel.rs/2.0.x/diesel/expression/functions/macro.sql_function.html 256 | 257 | ::: code-block 258 | [Change]() 259 | ```diff 260 | - no_arg_sql_function!(now, sql_types::Timestamp, "Represents the SQL NOW() function"); 261 | - 262 | - diesel::select(now) 263 | 264 | + sql_function!{ 265 | + /// Represents the SQL NOW() function 266 | + fn now() -> sql_types::Timestamp; 267 | + } 268 | + 269 | + diesel::select(now()) 270 | ``` 271 | 272 | ::: 273 | 274 | ## Changed accepted argument to `eq_any()` for the PostgreSQL backend 275 | 276 | Diesel 2.0 introduces an optimisation that replaces the `IN($1, ..., $n)` expression generated previously by `.eq_any()` with the more optimised `= ANY($1)` which binds the parameter as single array instead of binding each element separately. This improves the performance of large lists and allows us to keep such queries in the prepared statement cache, which enables future performance improvements. Unfortunately not all previously accepted arguments are accepted now. Newly rejected cases include: 277 | 278 | * A list of arrays where the `values` variable in `col.eq_any(values)` has the type `Vec>` 279 | * Using `.eq_any()` on several columns at one via `(table::col_a, table_col::b).eq_any(values)` 280 | 281 | Both cases can be worked around by using boxed queries and repeated chained equality checks. 282 | 283 | ## Replacement of `NonAggregate` with `ValidGrouping` 284 | 285 | Diesel now fully enforces the aggregation rules, which required us to change the way we represent the aggregation 286 | at the type system level. This is used to provide `group_by` support. Diesel's aggregation rules 287 | match the semantics of PostgreSQL or MySQL with the `ONLY_FULL_GROUP_BY` option enabled. 288 | 289 | As part of this change we removed the `NonAggregate` trait in favor of a new, more expressive [`ValidGrouping`] 290 | trait. Existing implementations of `NonAggregate` must be replaced with an equivalent [`ValidGrouping`] implementation. 291 | 292 | [`ValidGrouping`]: http://docs.diesel.rs/2.0.x/diesel/expression/trait.ValidGrouping.html 293 | 294 | The following change shows how to replace an existing implementation with a strictly equivalent implementation. 295 | 296 | ::: code-block 297 | [Change]() 298 | ```diff 299 | - impl NonAggregate for MyQueryNode {} 300 | + impl ValidGrouping<()> for MyQueryNode { 301 | + type IsAggregate = is_aggregate::No; 302 | + } 303 | ``` 304 | ::: 305 | 306 | Additional changes may be required to adapt custom query ast implementations to fully support `group_by` clauses. 307 | Refer to the documentation of [`ValidGrouping`] for details. 308 | 309 | In addition, any occurrence of `NonAggregate` in trait bounds needs to be replaced. Again, the following 310 | change shows the strictly equivalent version: 311 | 312 | ::: code-block 313 | [Change]() 314 | ```diff 315 | where 316 | - T: NonAggregate, 317 | + T: ValidGrouping<()>, 318 | + T::IsAggregate: MixedGrouping, 319 | + is_aggregate::No: MixedGrouping, 320 | ``` 321 | ::: 322 | 323 | ## Other changes to generics 324 | 325 | In addition to the changes listed above, we changed numerous internal details of Diesel. This will have impact on 326 | most codebases that include non-trivial generic code abstracting over Diesel. This section tries to list as much of those 327 | changes as possible 328 | 329 | ### Removed most of the non-public reachable API 330 | 331 | With Diesel 2.0 we removed most of the API which was marked with `#[doc(hidden)]`. Technically these parts of the API 332 | have always been private to Diesel. This change enforces this distinction in stricter way. In addition, some 333 | parts of these formerly hidden API are now documented and exposed behind the 334 | `i-implement-a-third-party-backend-and-opt-into-breaking-changes` crate feature. As the name already implies 335 | we reserve the right to change these APIs between different Diesel 2.x minor releases, so you should always pin 336 | a concrete minor release version if you use these APIs. 337 | If you depended on such an API and you cannot find a suitable replacement we invite you to work with us on exposing the corresponding 338 | feature as part of the stable API. 339 | 340 | ### Changed structure of the deserialization traits 341 | 342 | We changed the internal structure of the [`FromSqlRow`], [`Queryable`] and [`QueryableByName`] trait family used for deserialization. This change allows us to unify our deserialization code. 343 | We hopefully put sufficient wild card implementations in place so that old trait bounds imply 344 | the right trait anyway. For cases where this does not hold true, the following changes may be required: 345 | 346 | `Queryable` is now equivalent to `FromSqlRow`. The latter is used as an actual trait bound 347 | on the corresponding [`RunQueryDsl`] methods. 348 | `QueryableByName` is now equivalent to `FromSqlRow`. The latter is used as an actual trait 349 | on the corresponding [`RunQueryDsl`] methods. 350 | 351 | [`FromSqlRow`]: http://docs.diesel.rs/2.0.x/diesel/deserialize/trait.FromSqlRow.html 352 | [`Queryable`]: http://docs.diesel.rs/2.0.x/diesel/prelude/trait.Queryable.html 353 | [`QueryableByName`]: http://docs.diesel.rs/2.0.x/diesel/prelude/trait.QueryableByName.html 354 | 355 | ### Changed the scope of `QueryFragment` implementations 356 | 357 | With Diesel 2.0, we introduced a way to specialise [`QueryFragment`] implementations for specific backend, while 358 | providing a generic implementation for other backends. To be able to use this feature in the future we marked 359 | existing wild card [`QueryFragment`] implementations with an additional [`DieselReserveSpecialization`]. 360 | Rustc suggests just adding an additional trait bound on this trait. It's not possible to add a bound on 361 | this trait without opting into breaking changes and it's almost never required to actually do that. Any 362 | occurrence of an error mentioning this trait can simply be fixed by adding a trait bound like follows: 363 | 364 | [`QueryFragment`]: http://docs.diesel.rs/2.0.x/diesel/query_builder/trait.QueryFragment.html 365 | [`DieselReserveSpecialization`]: http://docs.diesel.rs/2.0.x/diesel/backend/trait.DieselReserveSpecialization.html 366 | 367 | ::: code-block 368 | [Change]() 369 | ```rust 370 | where 371 | QueryAstNodeMentionedInTheErrorMessage: QueryFragment 372 | ``` 373 | ::: 374 | 375 | This rule has one notable exception: Third party backend implementations. We expect those backends to opt into the 376 | `i-implement-a-third-party-backend-and-opt-into-breaking-changes` feature anyway, as it's otherwise not possible to 377 | implement a third party backend. 378 | 379 | ::: 380 | ::: 381 | ::: 382 | -------------------------------------------------------------------------------- /src/guides/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Getting Started" 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 | For this guide, we're going to walk through some simple examples for each of the pieces of CRUD, 14 | which stands for "Create Read Update Delete". Each step in this guide will build on the previous, 15 | and is meant to be followed along. 16 | 17 | **This guide assumes that you're using PostgreSQL.** Before we start, 18 | make sure you have PostgreSQL installed and running. If you are using some different database, like for example SQLite, some examples won't just run as the implemented API might differ. In the project repository, you may find various [examples](https://github.com/diesel-rs/diesel/tree/2.0.x/examples) for every supported database. 19 | 20 | 29 | 30 | ## Initializing a new project 31 | 32 | The first thing we need to do is generate our project. 33 | 34 | ::: code-block 35 | 36 | [Generate a new project]() 37 | 38 | ```sh 39 | cargo new --lib diesel_demo 40 | cd diesel_demo 41 | ``` 42 | 43 | ::: 44 | 45 | First, let's add Diesel to our dependencies. We're also going to use a tool called 46 | [`.env`][dotenvy] to manage our environment variables for us. We'll add it to our dependencies 47 | as well. 48 | 49 | [dotenvy]: https://github.com/allan2/dotenvy 50 | 51 | ::: code-block 52 | 53 | [Cargo.toml](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_1/Cargo.toml) 54 | 55 | ```toml 56 | [dependencies] 57 | diesel = { version = "2.0.0", features = ["postgres"] } 58 | dotenvy = "0.15" 59 | ``` 60 | 61 | ::: 62 | 63 | ## Installing Diesel CLI 64 | 65 | Diesel provides a separate [CLI] tool to help manage your project. Since it's a standalone binary, 66 | and doesn't affect your project's code directly, we don't add it to `Cargo.toml`. 67 | Instead, we just install it on our system. 68 | 69 | [CLI]: https://github.com/diesel-rs/diesel/tree/master/diesel_cli 70 | 71 | ::: code-block 72 | 73 | [Install the CLI tool]() 74 | 75 | ```sh 76 | cargo install diesel_cli 77 | ``` 78 | 79 | ::: 80 | 81 | 114 | 115 | ## Setup Diesel for your project 116 | 117 | We need to tell Diesel where to find our database. We do this by setting the `DATABASE_URL` 118 | environment variable. On our development machines, we'll likely have multiple projects going, 119 | and we don't want to pollute our environment. We can put the url in a `.env` file instead. 120 | 121 | ```sh 122 | echo DATABASE_URL=postgres://username:password@localhost/diesel_demo > .env 123 | ``` 124 | 125 | Now Diesel CLI can set everything up for us. 126 | 127 | ``` 128 | diesel setup 129 | ``` 130 | 131 | This will create our database (if it didn't already exist), and create an empty migrations directory 132 | that we can use to manage our schema (more on that later). 133 | 134 | Now we're going to write a small CLI that lets us manage a blog (ignoring the fact 135 | that we can only access the database from this CLI…). The first thing we're going to need is 136 | a table to store our posts. Let's create a migration for that: 137 | 138 | ```sh 139 | diesel migration generate create_posts 140 | ``` 141 | 142 | Diesel CLI will create two empty files for us in the required structure. 143 | You'll see output that looks something like this: 144 | 145 | ``` 146 | Creating migrations/20160815133237_create_posts/up.sql 147 | Creating migrations/20160815133237_create_posts/down.sql 148 | ``` 149 | 150 | Migrations allow us to evolve the database schema over time. Each migration can be applied 151 | (`up.sql`) or reverted (`down.sql`). Applying and immediately reverting a migration should 152 | leave your database schema unchanged. 153 | 154 | Next, we'll write the SQL for migrations: 155 | 156 | ::: code-block 157 | 158 | [up.sql]( https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/getting_started_step_1/migrations/20160815133237_create_posts/up.sql) 159 | 160 | ```sql 161 | CREATE TABLE posts ( 162 | id SERIAL PRIMARY KEY, 163 | title VARCHAR NOT NULL, 164 | body TEXT NOT NULL, 165 | published BOOLEAN NOT NULL DEFAULT FALSE 166 | ) 167 | ``` 168 | 169 | ::: 170 | 171 | ::: code-block 172 | 173 | [down.sql](https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/getting_started_step_1/migrations/20160815133237_create_posts/down.sql) 174 | 175 | ```sql 176 | DROP TABLE posts 177 | ``` 178 | 179 | ::: 180 | 181 | We can apply our new migration: 182 | 183 | ```sh 184 | diesel migration run 185 | ``` 186 | 187 | It's a good idea to make sure that `down.sql` is correct. You can quickly confirm that your `down.sql` 188 | rolls back your migration correctly by `redoing` the migration: 189 | 190 | ``` 191 | diesel migration redo 192 | ``` 193 | 194 | 202 | 203 | 219 | 220 | ## Write Rust 221 | 222 | OK enough SQL, let's write some Rust. We'll start by writing some code to show the last five published posts. 223 | The first thing we need to do is establish a database connection. 224 | 225 | ::: code-block 226 | 227 | [src/lib.rs]( https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_1/src/lib.rs) 228 | 229 | ```rust 230 | use diesel::pg::PgConnection; 231 | use diesel::prelude::*; 232 | use dotenvy::dotenv; 233 | use std::env; 234 | 235 | pub fn establish_connection() -> PgConnection { 236 | dotenv().ok(); 237 | 238 | let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); 239 | PgConnection::establish(&database_url) 240 | .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) 241 | } 242 | ``` 243 | 244 | ::: 245 | 246 | We'll also want to create a `Post` struct into which we can read our data, and have diesel generate the names 247 | we'll use to reference tables and columns in our queries. 248 | 249 | We'll add the following lines to the top of `src/lib.rs`: 250 | 251 | ::: code-block 252 | 253 | [src/lib.rs](https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/getting_started_step_1/src/lib.rs) 254 | 255 | ```rust 256 | pub mod models; 257 | pub mod schema; 258 | ``` 259 | 260 | ::: 261 | 262 | Next we need to create the two modules that we just declared. 263 | 264 | ::: code-block 265 | 266 | [src/models.rs](https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/getting_started_step_1/src/models.rs) 267 | 268 | ```rust 269 | use diesel::prelude::*; 270 | 271 | #[derive(Queryable)] 272 | pub struct Post { 273 | pub id: i32, 274 | pub title: String, 275 | pub body: String, 276 | pub published: bool, 277 | } 278 | ``` 279 | 280 | ::: 281 | 282 | `#[derive(Queryable)]` will generate all of the code needed to load a `Post` struct from a SQL query. 283 | 284 | Typically the schema module isn't created by hand, it gets generated by Diesel. When we ran `diesel setup`, 285 | a file called [diesel.toml] was created which tells Diesel to maintain a file at src/schema.rs for us. 286 | The file should look like this: 287 | 288 | [diesel.toml]: https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_1/diesel.toml 289 | 290 | ::: code-block 291 | 292 | [src/schema.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_1/src/schema.rs) 293 | 294 | ```rust 295 | // @generated automatically by Diesel CLI. 296 | 297 | diesel::table! { 298 | posts (id) { 299 | id -> Int4, 300 | title -> Varchar, 301 | body -> Text, 302 | published -> Bool, 303 | } 304 | } 305 | ``` 306 | 307 | ::: 308 | 309 | The exact output might vary slightly depending on your database, but it should be equivalent. 310 | 311 | The [`table!` macro] creates a bunch of code based on the database schema to represent 312 | all of the tables and columns. We'll see how exactly to use that in the next example. 313 | 314 | Any time we run or revert a migration, this file will get automatically updated. 315 | 316 | [`table!` macro]: https://docs.diesel.rs/2.0.x/diesel/macro.table.html 317 | 318 | 329 | 330 | Let's write the code to actually show us our posts. 331 | 332 | ::: code-block 333 | 334 | [src/bin/show_posts.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_1/src/bin/show_posts.rs) 335 | 336 | ```rust 337 | use self::models::*; 338 | use diesel::prelude::*; 339 | use diesel_demo::*; 340 | 341 | fn main() { 342 | use self::schema::posts::dsl::*; 343 | 344 | let connection = &mut establish_connection(); 345 | let results = posts 346 | .filter(published.eq(true)) 347 | .limit(5) 348 | .load::(connection) 349 | .expect("Error loading posts"); 350 | 351 | println!("Displaying {} posts", results.len()); 352 | for post in results { 353 | println!("{}", post.title); 354 | println!("-----------\n"); 355 | println!("{}", post.body); 356 | } 357 | } 358 | ``` 359 | 360 | ::: 361 | 362 | 363 | The use `self::schema::posts::dsl::*` line imports a bunch of aliases so that we can say `posts` 364 | instead of `posts::table`, and `published` instead of `posts::published`. It's useful 365 | when we're only dealing with a single table, but that's not always what we want. 366 | 367 | We can run our script with `cargo run --bin show_posts`. Unfortunately, the results 368 | won't be terribly interesting, as we don't actually have any posts in the database. 369 | Still, we've written a decent amount of code, so let's commit. 370 | 371 | The full code for the demo at this point can be found [here][full code]. 372 | 373 | [full code]: https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/getting_started_step_1/ 374 | 375 | Next, let's write some code to create a new post. We'll want a struct to use for inserting 376 | a new record. 377 | 378 | ::: code-block 379 | 380 | [src/models.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_2/src/models.rs) 381 | 382 | ```rust 383 | use crate::schema::posts; 384 | 385 | #[derive(Insertable)] 386 | #[diesel(table_name = posts)] 387 | pub struct NewPost<'a> { 388 | pub title: &'a str, 389 | pub body: &'a str, 390 | } 391 | ``` 392 | 393 | ::: 394 | 395 | Now let's add a function to save a new post. 396 | 397 | ::: code-block 398 | 399 | [src/lib.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_2/src/lib.rs) 400 | 401 | ```rust 402 | use self::models::{NewPost, Post}; 403 | 404 | pub fn create_post(conn: &mut PgConnection, title: &str, body: &str) -> Post { 405 | use crate::schema::posts; 406 | 407 | let new_post = NewPost { title, body }; 408 | 409 | diesel::insert_into(posts::table) 410 | .values(&new_post) 411 | .get_result(conn) 412 | .expect("Error saving new post") 413 | } 414 | ``` 415 | 416 | ::: 417 | 418 | When we call `.get_result` on an insert or update statement, it automatically adds `RETURNING *` 419 | to the end of the query, and lets us load it into any struct that implements `Queryable` 420 | for the right types. Neat! 421 | 422 | Diesel can insert more than one record in a single query. Just pass a `Vec` or slice to `insert`, 423 | and then call `get_results` instead of `get_result`. If you don't actually want to do anything 424 | with the row that was just inserted, call `.execute` instead. The compiler won't complain 425 | at you, that way. :) 426 | 427 | Now that we've got everything set up, we can create a little script to write a new post. 428 | 429 | ::: code-block 430 | 431 | [src/bin/write_post.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_2/src/bin/write_post.rs) 432 | 433 | ```rust 434 | use diesel_demo::*; 435 | use std::io::{stdin, Read}; 436 | 437 | fn main() { 438 | let connection = &mut establish_connection(); 439 | 440 | let mut title = String::new(); 441 | let mut body = String::new(); 442 | 443 | println!("What would you like your title to be?"); 444 | stdin().read_line(&mut title).unwrap(); 445 | let title = title.trim_end(); // Remove the trailing newline 446 | 447 | println!( 448 | "\nOk! Let's write {} (Press {} when finished)\n", 449 | title, EOF 450 | ); 451 | stdin().read_to_string(&mut body).unwrap(); 452 | 453 | let post = create_post(connection, title, &body); 454 | println!("\nSaved draft {} with id {}", title, post.id); 455 | } 456 | 457 | #[cfg(not(windows))] 458 | const EOF: &str = "CTRL+D"; 459 | 460 | #[cfg(windows)] 461 | const EOF: &str = "CTRL+Z"; 462 | ``` 463 | 464 | ::: 465 | 466 | We can run our new script with `cargo run --bin write_post`. Go ahead and write a blog post. 467 | Get creative! Here was mine: 468 | 469 | ``` 470 | Compiling diesel_demo v0.1.0 (file:///Users/sean/Documents/Projects/open-source/diesel_demo) 471 | Running `target/debug/write_post` 472 | 473 | What would you like your title to be? 474 | Diesel demo 475 | 476 | Ok! Let's write Diesel demo (Press CTRL+D when finished) 477 | 478 | You know, a CLI application probably isn't the best interface for a blog demo. 479 | But really I just wanted a semi-simple example, where I could focus on Diesel. 480 | I didn't want to get bogged down in some web framework here. 481 | Plus I don't really like the Rust web frameworks out there. We might make a 482 | new one, soon. 483 | 484 | Saved draft Diesel demo with id 1 485 | ``` 486 | 487 | Unfortunately, running `show_posts` still won't display our new post, 488 | because we saved it as a draft. If we look back to the code in 489 | `show_posts`, we added `.filter(published.eq(true))`, and we had 490 | `published` default to false in our migration. We need to publish it! 491 | But in order to do that, we'll need to look at how to update an 492 | existing record. First, let's commit. The code for this demo at this 493 | point can be found [here][commit-no-2]. 494 | 495 | [commit-no-2]: https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/getting_started_step_2/ 496 | 497 | Now that we've got create and read out of the way, update is actually 498 | relatively simple. Let's jump right into the script: 499 | 500 | ::: code-block 501 | 502 | [src/bin/publish_post.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_3/src/bin/publish_post.rs) 503 | 504 | ```rust 505 | use self::models::Post; 506 | use diesel::prelude::*; 507 | use diesel_demo::*; 508 | use std::env::args; 509 | 510 | fn main() { 511 | use self::schema::posts::dsl::{posts, published}; 512 | 513 | let id = args() 514 | .nth(1) 515 | .expect("publish_post requires a post id") 516 | .parse::() 517 | .expect("Invalid ID"); 518 | let connection = &mut establish_connection(); 519 | 520 | let post = diesel::update(posts.find(id)) 521 | .set(published.eq(true)) 522 | .get_result::(connection) 523 | .unwrap(); 524 | println!("Published post {}", post.title); 525 | } 526 | ``` 527 | 528 | ::: 529 | 530 | That's it! Let's try it out with `cargo run --bin publish_post 1`. 531 | 532 | ``` 533 | Compiling diesel_demo v0.1.0 (file:///Users/sean/Documents/Projects/open-source/diesel_demo) 534 | Running `target/debug/publish_post 1` 535 | Published post Diesel demo 536 | ``` 537 | 538 | And now, finally, we can see our post with `cargo run --bin show_posts`. 539 | 540 | ``` 541 | Running `target/debug/show_posts` 542 | Displaying 1 posts 543 | Diesel demo 544 | ---------- 545 | 546 | You know, a CLI application probably isn't the best interface for a blog demo. 547 | But really I just wanted a semi-simple example, where I could focus on Diesel. 548 | I didn't want to get bogged down in some web framework here. 549 | Plus I don't really like the Rust web frameworks out there. We might make a 550 | new one, soon. 551 | ``` 552 | 553 | We've still only covered three of the four letters of CRUD though. Let's show 554 | how to delete things. Sometimes we write something we really hate, and 555 | we don't have time to look up the ID. So let's delete based on the 556 | title, or even just some words in the title. 557 | 558 | ::: code-block 559 | 560 | [src/bin/delete_post.rs](https://github.com/diesel-rs/diesel/blob/2.0.x/examples/postgres/getting_started_step_3/src/bin/delete_post.rs) 561 | 562 | ```rust 563 | use diesel::prelude::*; 564 | use diesel_demo::*; 565 | use std::env::args; 566 | 567 | fn main() { 568 | use self::schema::posts::dsl::*; 569 | 570 | let target = args().nth(1).expect("Expected a target to match against"); 571 | let pattern = format!("%{}%", target); 572 | 573 | let connection = &mut establish_connection(); 574 | let num_deleted = diesel::delete(posts.filter(title.like(pattern))) 575 | .execute(connection) 576 | .expect("Error deleting posts"); 577 | 578 | println!("Deleted {} posts", num_deleted); 579 | } 580 | ``` 581 | 582 | ::: 583 | 584 | We can run the script with `cargo run --bin delete_post demo` (at least with the title I chose). 585 | Your output should look something like: 586 | 587 | ``` 588 | Compiling diesel_demo v0.1.0 (file:///Users/sean/Documents/Projects/open-source/diesel_demo) 589 | Running `target/debug/delete_post demo` 590 | Deleted 1 posts 591 | ``` 592 | 593 | When we try to run `cargo run --bin show_posts` again, we can see that the post was in fact deleted. 594 | This barely scratches the surface of what you can do with Diesel, but hopefully this tutorial 595 | has given you a good foundation to build off of. We recommend exploring the [API docs] to see more. 596 | The final code for this tutorial can be found [here][final]. 597 | 598 | [API docs]: https://docs.diesel.rs/2.0.x/diesel/index.html 599 | [final]: https://github.com/diesel-rs/diesel/tree/2.0.x/examples/postgres/getting_started_step_3/ 600 | 601 | ::: 602 | ::: 603 | ::: 604 | --------------------------------------------------------------------------------