|
| 1 | +--- |
| 2 | +title: "Announcing v0.17: Triggers and Improved ERDs" |
| 3 | +authors: rotemtam |
| 4 | +tags: [release, erd, schemadoc, triggers, postgres, mysql, mariadb, sqlite] |
| 5 | +--- |
| 6 | + |
| 7 | +import InstallationInstructions from '../../md/components/_installation_instructions.mdx' |
| 8 | + |
| 9 | +Hi everyone, |
| 10 | + |
| 11 | +I hope you are enjoying the holiday season, because we are here today with the first Atlas |
| 12 | +release of 2024: [v0.17](https://github.com/ariga/atlas/releases/tag/v0.16.1). It's been |
| 13 | +only a bit over a week since our last release, but we have some exciting new features we |
| 14 | +couldn't wait to share with you: |
| 15 | + |
| 16 | +* **Trigger Support** - Atlas now supports managing triggers on MySQL, PostgreSQL, MariaDB and SQLite databases. |
| 17 | +* **Improved ERDs** - You can now visualize your schema's SQL views, as well as create filters to select the specific |
| 18 | +database objects you wish to see. |
| 19 | + |
| 20 | +Without further ado, let's dive in! |
| 21 | + |
| 22 | +### Trigger Support |
| 23 | + |
| 24 | +:::info BETA FEATURE |
| 25 | +Triggers are currently in beta and available to logged-in users only. To use this feature, run: |
| 26 | + |
| 27 | +``` |
| 28 | +atlas login |
| 29 | +``` |
| 30 | + |
| 31 | +::: |
| 32 | + |
| 33 | +Triggers are a powerful feature of relational databases that allow you to run custom |
| 34 | +code when certain events occur on a table or a view. For example, you can use triggers to |
| 35 | +automatically update the amount of stock in your inventory when a new order is |
| 36 | +placed or to create an audit log of changes to a table. Using this event-based approach, |
| 37 | +you can implement complex business logic in your database, without having to write |
| 38 | +any additional code in your application. |
| 39 | + |
| 40 | +Managing triggers as part of the software development lifecycle can be quite a challenge. |
| 41 | +Luckily, Atlas's database schema-as-code approach makes it easy to do! |
| 42 | + |
| 43 | +Let's use Atlas to build a small chunk of a simple e-commerce application: |
| 44 | + |
| 45 | +1. Download the latest version of the Atlas CLI: |
| 46 | + <InstallationInstructions/> |
| 47 | +2. Make sure you are logged in to Atlas: |
| 48 | + ``` |
| 49 | + atlas login |
| 50 | + ``` |
| 51 | +3. Let's spin up a new PostgreSQL database using `docker`: |
| 52 | + |
| 53 | + ```bash |
| 54 | + docker run --name db -e POSTGRES_PASSWORD=pass -d -p 5432:5432 postgres:16 |
| 55 | + ``` |
| 56 | +4. Next, let's define and apply the base tables for our application: |
| 57 | + |
| 58 | + ```hcl title=schema.hcl |
| 59 | + table "inventory" { |
| 60 | + schema = schema.public |
| 61 | + column "item_id" { |
| 62 | + null = false |
| 63 | + type = serial |
| 64 | + } |
| 65 | + column "item_name" { |
| 66 | + null = false |
| 67 | + type = character_varying(255) |
| 68 | + } |
| 69 | + column "quantity" { |
| 70 | + null = false |
| 71 | + type = integer |
| 72 | + } |
| 73 | + primary_key { |
| 74 | + columns = [column.item_id] |
| 75 | + } |
| 76 | + } |
| 77 | + table "orders" { |
| 78 | + schema = schema.public |
| 79 | + column "order_id" { |
| 80 | + null = false |
| 81 | + type = serial |
| 82 | + } |
| 83 | + column "item_id" { |
| 84 | + null = false |
| 85 | + type = integer |
| 86 | + } |
| 87 | + column "order_quantity" { |
| 88 | + null = false |
| 89 | + type = integer |
| 90 | + } |
| 91 | + primary_key { |
| 92 | + columns = [column.order_id] |
| 93 | + } |
| 94 | + foreign_key "orders_item_id_fkey" { |
| 95 | + columns = [column.item_id] |
| 96 | + ref_columns = [table.inventory.column.item_id] |
| 97 | + on_update = NO_ACTION |
| 98 | + on_delete = NO_ACTION |
| 99 | + } |
| 100 | + } |
| 101 | + ``` |
| 102 | + This defines two tables: `inventory` and `orders`. The `inventory` table holds |
| 103 | + information about the items in our store, and the `orders` table holds information |
| 104 | + about orders placed by our customers. The `orders` table has a foreign key |
| 105 | + constraint to the `inventory` table, to ensure that we can't place an order for |
| 106 | + an item that doesn't exist in our inventory. |
| 107 | + |
| 108 | + Apply this schema on our local Postgres instance using the Atlas CLI: |
| 109 | + |
| 110 | + ```bash |
| 111 | + atlas schema apply \ |
| 112 | + --dev-url 'docker://postgres/16?search_path=public' \ |
| 113 | + --to file://schema.hcl \ |
| 114 | + -u 'postgres://postgres:pass@:5432/postgres?search_path=public&sslmode=disable' \ |
| 115 | + --auto-approve |
| 116 | + ``` |
| 117 | + This command will apply the schema defined in `schema.hcl` to the local Postgres instance. |
| 118 | + Notice the `--auto-approve` flag, which instructs Atlas to automatically apply the schema |
| 119 | + without prompting for confirmation. |
| 120 | + |
| 121 | +5. Let's now populate our database with some inventory items. We can do this using |
| 122 | + the `psql` command that is installed inside the default PostgreSQL Docker image: |
| 123 | + |
| 124 | + ```bash |
| 125 | + docker exec -it db psql -U postgres -c "INSERT INTO inventory (item_name, quantity) VALUES ('Apple', 10);" |
| 126 | + docker exec -it db psql -U postgres -c "INSERT INTO inventory (item_name, quantity) VALUES ('Banana', 20);" |
| 127 | + docker exec -it db psql -U postgres -c "INSERT INTO inventory (item_name, quantity) VALUES ('Orange', 30);" |
| 128 | + ``` |
| 129 | + |
| 130 | +6. Now, let's define the business logic for our store using a `FUNCTION` and a `TRIGGER`. Append |
| 131 | + these definitions to `schema.hcl`: |
| 132 | + ```hcl title=schema.hcl (continued) |
| 133 | + function "update_inventory" { |
| 134 | + schema = schema.public |
| 135 | + lang = PLpgSQL |
| 136 | + return = trigger |
| 137 | + as = <<-SQL |
| 138 | + BEGIN |
| 139 | + UPDATE inventory |
| 140 | + SET quantity = quantity - NEW.order_quantity |
| 141 | + WHERE item_id = NEW.item_id; |
| 142 | + RETURN NEW; |
| 143 | + END; |
| 144 | + SQL |
| 145 | + } |
| 146 | + trigger "after_order_insert" { |
| 147 | + on = table.orders |
| 148 | + after { |
| 149 | + insert = true |
| 150 | + } |
| 151 | + foreach = ROW |
| 152 | + execute { |
| 153 | + function = function.update_inventory |
| 154 | + } |
| 155 | + } |
| 156 | + ``` |
| 157 | + We start by defining a `FUNCTION` called `update_inventory`. This function is |
| 158 | + written in `PL/pgSQL`, the procedural language for PostgreSQL. The function |
| 159 | + accepts a single argument, which is a `TRIGGER` object. The function updates |
| 160 | + the `inventory` table to reflect the new order, and then returns the `NEW` |
| 161 | + row, which is the row that was just inserted into the `orders` table. |
| 162 | + |
| 163 | + Next, we define a `TRIGGER` called `after_order_insert`. This trigger is |
| 164 | + executed after a new row is inserted into the `orders` table. The trigger |
| 165 | + executes the `update_inventory` function for each row that was inserted. |
| 166 | + |
| 167 | + Apply the updated schema using the Atlas CLI: |
| 168 | + ```bash |
| 169 | + atlas schema apply \ |
| 170 | + --dev-url 'docker://postgres/16?search_path=public' \ |
| 171 | + --to file://schema.hcl \ |
| 172 | + -u 'postgres://postgres:pass@:5432/postgres?search_path=public&sslmode=disable' \ |
| 173 | + --auto-approve |
| 174 | + ``` |
| 175 | + Notice that Atlas automatically detects that we have added a new `FUNCTION` |
| 176 | + and a new `TRIGGER`, and applies them to the database. |
| 177 | +7. Finally, let's test our application to see that it actually works. We can do |
| 178 | + this by inserting a new row into the `orders` table: |
| 179 | + ```bash |
| 180 | + docker exec -it db psql -U postgres -c "INSERT INTO orders (item_id, order_quantity) VALUES (1, 5);" |
| 181 | + ``` |
| 182 | + This statement creates a new order for 5 `Apple`s. |
| 183 | + |
| 184 | + Now, let's check the `inventory` table to see that the order was processed |
| 185 | + correctly: |
| 186 | + |
| 187 | + ```bash |
| 188 | + docker exec -it db psql -U postgres -c "SELECT quantity FROM inventory WHERE item_name='Apple';" |
| 189 | + ``` |
| 190 | + You should see the following output: |
| 191 | + ``` |
| 192 | + quantity |
| 193 | + --------- |
| 194 | + 5 |
| 195 | + (1 row) |
| 196 | + ``` |
| 197 | + |
| 198 | + Amazing! Our trigger automatically detected the creation of a new order of apples, |
| 199 | + and updated the inventory accordingly from 10 to 5. |
| 200 | + |
| 201 | + |
| 202 | +### Improved ERDs |
| 203 | + |
| 204 | +One of the most frequently used capabilities in Atlas is schema visualization. Having a visual representation of your |
| 205 | +data model can be helpful as it allows for easier comprehension of complex data structures, and enables |
| 206 | +developers to better understand and collaborate on the data model of the application they are building. |
| 207 | + |
| 208 | +#### Visualizing Database Views |
| 209 | + |
| 210 | + |
| 211 | + |
| 212 | +Until recently, the ERD showed schema's tables and the relations between them. With the most recent release, |
| 213 | +the ERD now visualizes database views! |
| 214 | + |
| 215 | +Within each view you can find its: |
| 216 | + |
| 217 | + * Columns - the view's columns, including their data types and nullability. |
| 218 | + * Create Statement - the SQL CREATE statement, based on your specific database type. |
| 219 | + * Dependencies - a list of the tables (or other views) it is connected to. Clicking on this |
| 220 | + will map edges to each connected object in the schema. |
| 221 | + |
| 222 | +As of recently (including this release), we have added support for functions, stored procedures and triggers which are all |
| 223 | +coming soon to the ERD! |
| 224 | + |
| 225 | +To play with a schema that contains this feature, head over to the [live demo](https://gh.atlasgo.cloud/dirs/4294967296). |
| 226 | + |
| 227 | +#### ERD Filters |
| 228 | + |
| 229 | +In cases where you have many database objects and prefer to focus in on a specific set of tables and views, |
| 230 | +you can narrow down your selection by creating a filter. Filters can be saved for future use. This can be great when working |
| 231 | +on a feature that affects a specific part of the schema, this way you can easily refer to it as needed. |
| 232 | + |
| 233 | + |
| 234 | + |
| 235 | +### Wrapping up |
| 236 | + |
| 237 | +That's it! I hope you try out (and enjoy) all of these new features and find them useful. |
| 238 | +As always, we would love to hear your feedback and suggestions on our [Discord server](https://discord.gg/zZ6sWVg6NT). |
0 commit comments