Skip to content

Commit 10d4c69

Browse files
ppom0lovasoa
andauthored
Documentation: add "Extensions to SQL" page (#897)
Fix #877 Co-authored-by: ppom <> Co-authored-by: Ophir LOJKINE <[email protected]>
1 parent 8017ecd commit 10d4c69

File tree

3 files changed

+216
-0
lines changed

3 files changed

+216
-0
lines changed
+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
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+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
select 'http_header' as component,
2+
'public, max-age=300, stale-while-revalidate=3600, stale-if-error=86400' as "Cache-Control";
3+
4+
select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1;
5+
6+
-- Article by Matthew Larkin
7+
select 'text' as component,
8+
sqlpage.read_file_as_text('extensions-to-sql.md') as contents_md,
9+
false as article;

examples/official-site/sqlpage/migrations/01_documentation.sql

+1
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,7 @@ You see the [page layouts demo](./examples/layouts.sql) for a live example of th
12401240
{"link": "/your-first-sql-website", "title": "Getting started", "icon": "book"},
12411241
{"link": "/components.sql", "title": "All Components", "icon": "list-details"},
12421242
{"link": "/functions.sql", "title": "SQLPage Functions", "icon": "math-function"},
1243+
{"link": "/extensions-to-sql.sql", "title": "Extensions to SQL", "icon": "cube-plus"},
12431244
{"link": "/custom_components.sql", "title": "Custom Components", "icon": "puzzle"},
12441245
{"link": "//github.com/sqlpage/SQLPage/blob/main/configuration.md#configuring-sqlpage", "title": "Configuration", "icon": "settings"}
12451246
]}

0 commit comments

Comments
 (0)