|
| 1 | +# SQL Style Guide |
| 2 | + |
| 3 | +This guide establishes the standards for SQL in Boundary. |
| 4 | + |
| 5 | +## Purpose |
| 6 | + |
| 7 | +These guidelines are designed to make our SQL more readable, maintainable, and greppable. |
| 8 | + |
| 9 | +## Rules |
| 10 | + |
| 11 | +### Formatting |
| 12 | + |
| 13 | +- No tabs. 2 spaces per indent. |
| 14 | +- No trailing whitespace. |
| 15 | +- No more than two blank lines between statements. |
| 16 | +- No empty lines in the middle of a single statement. |
| 17 | +- Do not SHOUTCASE or "Sentence case" SQL keywords (e.g., use `select`, not `SELECT` or `Select`). |
| 18 | + |
| 19 | +### Data Definition Statements |
| 20 | + |
| 21 | +Keep the object of `create`, `alter`, `drop`, and `comment on` statements on the |
| 22 | +same line as the statement. |
| 23 | + |
| 24 | +### Tables |
| 25 | + |
| 26 | +```sql |
| 27 | +create table imaginary_basket ( |
| 28 | + public_id wt_public_id primary key, |
| 29 | + store_id wt_public_id not null |
| 30 | + constraint imaginary_store_fkey |
| 31 | + references imaginary_store (public_id) |
| 32 | + on delete cascade |
| 33 | + on update cascade, |
| 34 | + basket_name text not null |
| 35 | + constraint basket_name_must_not_be_empty |
| 36 | + check( |
| 37 | + length(trim(basket_name)) > 0 |
| 38 | + ), |
| 39 | + basket_type text not null default 'fruit_basket' |
| 40 | + constraint basket_type_enm_fkey |
| 41 | + references basket_type_enm (name) |
| 42 | + on delete restrict |
| 43 | + on update cascade, |
| 44 | + constraint imaginary_basket_store_id_basket_name_uq |
| 45 | + unique(store_id, basket_name) |
| 46 | +); |
| 47 | +comment on table imaginary_basket is |
| 48 | + 'imaginary_basket is a table where each row is a resource that represents an imaginary shopping basket.'; |
| 49 | +``` |
| 50 | + |
| 51 | +For `create table` statements: |
| 52 | + |
| 53 | +- Keep the `create table` statement and the name of the table on the same line. |
| 54 | +- For columns, keep the column name, column type, `not null` (if applicable), |
| 55 | + and default value (if applicable) on the same line. |
| 56 | +- Put the `primary key` declaration at the start of the `create table` statement. |
| 57 | +- If the `primary key` is a single column, put the `primary key` declaration on |
| 58 | + the same line as the column name and column type. |
| 59 | +- Put constraints for a single column on a new line indented under the column declaration. |
| 60 | + |
| 61 | +**Constraints**: |
| 62 | +- Name all constraints. |
| 63 | +- Give `check` constraints a name that describes what rule the constraint is enforcing. |
| 64 | +- The naming pattern for foreign key constraints is `reftable_fkey` where |
| 65 | + `reftable` is the name of the referenced table. |
| 66 | +- The naming pattern for unique constraints is `tablename_col1_colx_uq` where |
| 67 | + `tablename` is the name of the table and `col1_colx` is the name of each |
| 68 | + column in the unique constraint. |
| 69 | + |
| 70 | +### Functions |
| 71 | + |
| 72 | +```sql |
| 73 | +create or replace function colorize_basket(basket_id wt_public_id, basket_color text) returns void |
| 74 | +as $$ |
| 75 | +begin |
| 76 | + .... |
| 77 | +end; |
| 78 | +$$ language plpgsql; |
| 79 | +comment on function colorize_basket is |
| 80 | + 'colorize_basket is a function ....'; |
| 81 | +``` |
| 82 | + |
| 83 | +For `create function` and `create or replace function` statements: |
| 84 | + |
| 85 | +- Keep the `create function` statement, function name, function parameters and `returns` statement on the same line. |
| 86 | + |
| 87 | +### Triggers |
| 88 | + |
| 89 | +```sql |
| 90 | +create trigger update_version_column after update of version, termination_reason, key_id, tofu_token, server_id, server_type on session |
| 91 | + for each row execute procedure update_version_column(); |
| 92 | +``` |
| 93 | + |
| 94 | +For `create trigger` statements: |
| 95 | +- Keep the `create trigger` statement, trigger name, `before | after`, event |
| 96 | + name, and `on table_name` statement on the same line. |
| 97 | +- Put the remaining statements on a new indented line. |
| 98 | + |
| 99 | +### Domains |
| 100 | + |
| 101 | +```sql |
| 102 | +create domain wt_private_id as text not null |
| 103 | +check( |
| 104 | + length(trim(value)) > 10 |
| 105 | +); |
| 106 | +comment on domain wt_private_id is |
| 107 | + 'Random ID generated with github.com/hashicorp/go-secure-stdlib/base62'; |
| 108 | +``` |
| 109 | + |
| 110 | +For `create domain` statements: |
| 111 | +- Keep the `create domain` statement, domain name, `as data_type`, `not null` |
| 112 | + (if applicable), and default value (if applicable) on the same line. |
| 113 | +- Put check constraints on new lines. |
| 114 | + |
| 115 | +### `comment on` statements |
| 116 | + |
| 117 | +For `comment on` statements: |
| 118 | + |
| 119 | +- Keep the `comment on` statement, object type, object name, and `is` |
| 120 | + statement on the same line. |
| 121 | +- Put `comment on` statements directly below the database object the comment is on. |
| 122 | +- Do not put any blank lines between the `comment on` statement and the database |
| 123 | + object declaration block that comment is for. |
| 124 | +- Put the text of the `comment on` a new indented line. |
0 commit comments