|
| 1 | +# Extensions to SQL |
| 2 | + |
| 3 | +SQLPage makes some special treatment before executing your SQL queries. |
| 4 | + |
| 5 | +When executing your SQL file, SQLPage executes each query one at a time. |
| 6 | +It doesn't send the whole file as-is to the database engine. |
| 7 | + |
| 8 | +## Performance |
| 9 | + |
| 10 | +See the [performance page](/performance.sql) for details on the optimizations |
| 11 | +made to run your queries as fast as possible. |
| 12 | + |
| 13 | +## Variables |
| 14 | + |
| 15 | +SQL doesn't have its own mechanism for variables. |
| 16 | +SQLPage implements variables in the following way: |
| 17 | + |
| 18 | +### POST parameters |
| 19 | + |
| 20 | +When sending a POST request, most often by sending a form with the |
| 21 | +[form component](/component.sql?component=form), the form data is made |
| 22 | +available as variables prefixed by a semi-colon. |
| 23 | + |
| 24 | +So when this form is sent: |
| 25 | + |
| 26 | +`form.sql` |
| 27 | +```sql |
| 28 | +SELECT |
| 29 | + 'form' AS component, |
| 30 | + 'POST' AS method, -- form defaults to using the HTTP POST method |
| 31 | + 'result.sql' AS action; |
| 32 | + |
| 33 | +SELECT |
| 34 | + 'age' AS name, |
| 35 | + 'How old are you?' AS label, |
| 36 | + 'number' AS type; |
| 37 | +``` |
| 38 | + |
| 39 | +It will make a request to this page: |
| 40 | + |
| 41 | +`result.sql` |
| 42 | +```sql |
| 43 | +SELECT |
| 44 | + 'text' AS component, |
| 45 | + 'You are ' || :age || ' years old!' AS contents; |
| 46 | +``` |
| 47 | + |
| 48 | +`:age` will be substituted by the actual value of the POST parameter. |
| 49 | + |
| 50 | +### URL parameters |
| 51 | + |
| 52 | +Likewise, URL parameters are available as variables prefixed by a dollar sign. |
| 53 | + |
| 54 | +> URL parameters are often called GET parameters because they can originate |
| 55 | +> from a form with 'GET' as the method. |
| 56 | +
|
| 57 | +So the previous example can be reworked to handle URL parameters: |
| 58 | + |
| 59 | +`result.sql` |
| 60 | +```sql |
| 61 | +SELECT |
| 62 | + 'text' AS component, |
| 63 | + 'You are ' || $age || ' years old!' AS contents; |
| 64 | +``` |
| 65 | + |
| 66 | +By querying this page with this URL: `/request.sql?age=42` |
| 67 | +we would get `You are 42 years old!` as a response. |
| 68 | + |
| 69 | +### The `SET` command |
| 70 | + |
| 71 | +SQLPage overrides the behavior of `SET` statements in SQL to store variables in SQLPage itself instead of running the statement on the database. |
| 72 | + |
| 73 | +```sql |
| 74 | +SET coalesced_post_id = COALESCE($post_id, 0); |
| 75 | +``` |
| 76 | + |
| 77 | +`SET` statements are transformed into `SELECT` queries, and their result is stored in a `$`-variable: |
| 78 | + |
| 79 | +```sql |
| 80 | +SELECT COALESCE($post_id, 0); |
| 81 | +``` |
| 82 | + |
| 83 | +We can override a previous `$`-variable: |
| 84 | + |
| 85 | +```sql |
| 86 | +SET post_id = COALESCE($post_id, 0); |
| 87 | +``` |
| 88 | + |
| 89 | +### Limitations |
| 90 | + |
| 91 | +`$`-variables and `:`-variables are stored by SQLPage, not in the database. |
| 92 | + |
| 93 | +They can only store a string, or null. |
| 94 | + |
| 95 | +As such, they're not designed to store table-valued results. |
| 96 | +They will only store the first value of the first column: |
| 97 | + |
| 98 | +```sql |
| 99 | +CREATE TABLE t(a, b); |
| 100 | +INSERT INTO t(a, b) VALUES (1, 2), (3, 4); |
| 101 | + |
| 102 | +SET var = (SELECT * FROM t); |
| 103 | + |
| 104 | +-- now $var contains '1' |
| 105 | +``` |
| 106 | + |
| 107 | +Temporary table-valued results can be stored in two ways: |
| 108 | + |
| 109 | +1. Using temporary tables |
| 110 | + |
| 111 | +This is the most efficient method to store large values. |
| 112 | +```sql |
| 113 | +-- Database connections are reused and temporary tables are stored at the |
| 114 | +-- connection level, so we make sure the table doesn't exist already |
| 115 | +DROP TABLE IF EXISTS my_temp_table; |
| 116 | +CREATE TEMPORARY TABLE my_temp_table AS |
| 117 | +SELECT a, b |
| 118 | +FROM my_stored_table ... |
| 119 | + |
| 120 | +-- Insert data from direct values |
| 121 | +INSERT INTO my_temp_table(a, b) |
| 122 | +VALUES (1, 2), (3, 4); |
| 123 | +``` |
| 124 | + |
| 125 | +2. Using JSON |
| 126 | + |
| 127 | +This can be more convenient, but should only be used for small values, because data |
| 128 | +is copied from the database into SQLPage memory, and to the database again at each use. |
| 129 | + |
| 130 | +You can use the [JSON functions from your database](/blog.sql?post=JSON in SQL%3A A Comprehensive Guide). |
| 131 | +Here are some examples with SQLite: |
| 132 | +```sql |
| 133 | +-- CREATE TABLE my_table(a, b); |
| 134 | +-- INSERT INTO my_table(a, b) |
| 135 | +-- VALUES (1, 2), (3, 4); |
| 136 | + |
| 137 | +SET my_json = ( |
| 138 | + SELECT json_group_array(a) |
| 139 | + FROM my_table |
| 140 | +); |
| 141 | +-- [1, 3] |
| 142 | + |
| 143 | +SET my_json = json_array(1, 2, 3); |
| 144 | +-- [1, 2, 3] |
| 145 | +``` |
| 146 | + |
| 147 | +## Functions |
| 148 | + |
| 149 | +Functions starting with `sqlpage.` are executed by SQLPage, not by your database engine. |
| 150 | +See the [functions page](/functions.sql) for more details. |
| 151 | + |
| 152 | +They're either executed before or after the query is run in the database. |
| 153 | + |
| 154 | +### Execution functions *before* sending a query to the database |
| 155 | + |
| 156 | +When they don't process results coming from the database: |
| 157 | + |
| 158 | +```sql |
| 159 | +SELECT * FROM blog WHERE slug = sqlpage.path() |
| 160 | +``` |
| 161 | + |
| 162 | +`sqlpage.path()` will get replaced by the result of the function. |
| 163 | + |
| 164 | +### Execution functions *after* receiving results from the database |
| 165 | + |
| 166 | +When they process results coming from the database: |
| 167 | + |
| 168 | +```sql |
| 169 | +SELECT sqlpage.read_file_as_text(blog_post_file) AS title FROM blog; |
| 170 | +``` |
| 171 | + |
| 172 | +The query executed will be: |
| 173 | + |
| 174 | +```sql |
| 175 | +SELECT blog_post_file AS title FROM blog; |
| 176 | +``` |
| 177 | + |
| 178 | +Then `sqlpage.read_file_as_text()` will be called on each row. |
| 179 | + |
| 180 | +## Implementation details of variables and functions |
| 181 | + |
| 182 | +All queries run by SQLPage in the database are first prepared, then executed. |
| 183 | + |
| 184 | +Statements are prepared and cached the first time they're encountered by SQLPage. |
| 185 | +Then those cached prepared statements are executed at each run, with parameter substitution. |
| 186 | + |
| 187 | +All variables and function results are cast as text, to let the |
| 188 | +database query optimizer know only strings (or nulls) will be passed. |
| 189 | + |
| 190 | +Examples: |
| 191 | + |
| 192 | +```sql |
| 193 | +-- Source query |
| 194 | +SELECT * FROM blog WHERE slug = sqlpage.path(); |
| 195 | + |
| 196 | +-- Prepared statement (SQLite syntax) |
| 197 | +SELECT * FROM blog WHERE slug = CAST(?1 AS TEXT) |
| 198 | +``` |
| 199 | + |
| 200 | +```sql |
| 201 | +-- Source query |
| 202 | +SET post_id = COALESCE($post_id, 0); |
| 203 | + |
| 204 | +-- Prepared statement (SQLite syntax) |
| 205 | +SELECT COALESCE(CAST(?1 AS TEXT), 0) |
| 206 | +``` |
0 commit comments