Skip to content

Commit e9fc962

Browse files
committed
run_sql can now be called with a specific set of variables to pass to the included sql file
new html component
1 parent 2e509e9 commit e9fc962

File tree

7 files changed

+74
-2
lines changed

7 files changed

+74
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
- updated the SQL parser to [v0.48](https://github.com/sqlparser-rs/sqlparser-rs/blob/main/CHANGELOG.md#0480-2024-07-09)
1919
- upport UPDATE statements that contain tuple assignments , like `UPDATE table SET (a, b) = (SELECT 1, 2)`
2020
- support custom operators in postgres. Usefull when using extensions like PostGIS, PGroonga, pgtrgm, or pg_similarity, which define custom operators like `&&&`, `@>`, `<->`, `~>`, `~>=`, `~<=`, `<@`...
21+
- New `html` component to display raw HTML content. This component is meant to be used by advanced users who want to display HTML content that cannot be expressed with the other components. Make sure you understand the security implications before using this component, as using untrusted HTML content can expose your users to [cross-site scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) attacks.
22+
- New parameter in the [`run_sql`](https://sql.ophir.dev/functions.sql?function=run_sql#function) function to pass variables to the included SQL file, instead of using the global variables. Together with the new ability to pass data from the database to SQLPage functions, this allows you to create more modular and reusable SQL files. For instance, the following is finally possible:
23+
```sql
24+
select 'dynamic' as component, sqlpage.run_sql('display_product.sql', json_object('product_id', product_id)) as properties from products;
25+
```
2126

2227
## 0.24.0 (2024-06-23)
2328
- in the form component, searchable `select` fields now support more than 50 options. They used to display only the first 50 options.

examples/official-site/sqlpage/migrations/38_run_sql.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,10 @@ VALUES (
4747
'Path to the SQL file to execute, can be absolute, or relative to the web root (the root folder of your website sql files).
4848
In-database files, from the sqlpage_files(path, contents, last_modified) table are supported.',
4949
'TEXT'
50+
),(
51+
'run_sql',
52+
2,
53+
'parameters',
54+
'Optional JSON object to pass as parameters to the included SQL file. The keys of the object will be available as variables in the included file. By default, the included file will have access to the same variables as the calling file.',
55+
'JSON'
5056
);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- Documentation for the RSS component
2+
INSERT INTO component (name, description, icon, introduced_in_version) VALUES (
3+
'html',
4+
'Include raw HTML in the output. For advanced users only. Use this component to create complex layouts or to include external content.
5+
Be very careful when using this component with user-generated content, as it can lead to security vulnerabilities.
6+
Use this component only if you are familiar with the security implications of including raw HTML, and understand the risks of cross-site scripting (XSS) attacks.',
7+
'html',
8+
'0.25.0'
9+
);
10+
11+
INSERT INTO parameter (component,name,description,type,top_level,optional) VALUES (
12+
'html',
13+
'html',
14+
'Raw HTML content to include in the page. This will not be sanitized or escaped. If you include content from an untrusted source, your page will be vulnerable to cross-site scripting attacks.',
15+
'TEXT',
16+
TRUE,
17+
TRUE
18+
),(
19+
'html',
20+
'html',
21+
'Raw HTML content to include in the page. This will not be sanitized or escaped. If you include content from an untrusted source, your page will be vulnerable to cross-site scripting attacks.',
22+
'TEXT',
23+
FALSE,
24+
TRUE
25+
);
26+
27+
-- Insert example(s) for the component
28+
INSERT INTO example (component, description, properties) VALUES (
29+
'html',
30+
'Include a simple HTML snippet. In this example, the HTML code is hardcoded in the SQL query, so it is safe. You should never include data that may be manipulated by a user in the HTML content.
31+
',
32+
JSON('[{
33+
"component": "html",
34+
"html": "<h1 class=\"text-blue\">This text is safe because it is <mark>hardcoded</mark>!</h1>"
35+
}]')
36+
),
37+
(
38+
'html',
39+
'Include multiple html snippets as row-level parameters. Again, be careful what you include in the HTML content. If the data comes from a user, it can be manipulated to include malicious code.',
40+
JSON('[{"component":"html"},
41+
{"html":"<h1>First snippet</h1>"},
42+
{"html":"<h2>Second snippet</h2>"},
43+
{"html":"<h3>Third snippet</h3>"}]')
44+
)
45+
;

sqlpage/templates/html.handlebars

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
{{{~html~}}}
2+
{{~#each_row~}}{{{~html~}}}{{~/each_row~}}

src/webserver/database/sqlpage_functions/functions.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::RequestInfo;
2-
use crate::webserver::{database::execute_queries::DbConn, http::SingleOrVec, ErrorWithStatus};
2+
use crate::webserver::{database::execute_queries::DbConn, request_variables::ParamMap, http::SingleOrVec, ErrorWithStatus};
33
use anyhow::{anyhow, Context};
44
use futures_util::StreamExt;
55
use mime_guess::mime;
@@ -28,7 +28,7 @@ super::function_definition_macro::sqlpage_functions! {
2828
read_file_as_data_url((&RequestInfo), file_path: Option<Cow<str>>);
2929
read_file_as_text((&RequestInfo), file_path: Option<Cow<str>>);
3030
request_method((&RequestInfo));
31-
run_sql((&RequestInfo, &mut DbConn), sql_file_path: Option<Cow<str>>);
31+
run_sql((&RequestInfo, &mut DbConn), sql_file_path: Option<Cow<str>>, variables: Option<Cow<str>>);
3232

3333
uploaded_file_mime_type((&RequestInfo), upload_name: Cow<str>);
3434
uploaded_file_path((&RequestInfo), upload_name: Cow<str>);
@@ -348,6 +348,7 @@ async fn run_sql<'a>(
348348
request: &'a RequestInfo,
349349
db_connection: &mut DbConn,
350350
sql_file_path: Option<Cow<'a, str>>,
351+
variables: Option<Cow<'a, str>>,
351352
) -> anyhow::Result<Option<Cow<'a, str>>> {
352353
use serde::ser::{SerializeSeq, Serializer};
353354
let Some(sql_file_path) = sql_file_path else {
@@ -365,6 +366,11 @@ async fn run_sql<'a>(
365366
.await
366367
.with_context(|| format!("run_sql: invalid path {sql_file_path:?}"))?;
367368
let mut tmp_req = request.clone();
369+
if let Some(variables) = variables {
370+
let variables: ParamMap = serde_json::from_str(&variables)?;
371+
tmp_req.get_variables = variables;
372+
tmp_req.post_variables = Default::default();
373+
}
368374
if tmp_req.clone_depth > 8 {
369375
anyhow::bail!("Too many nested inclusions. run_sql can include a file that includes another file, but the depth is limited to 8 levels. \n\
370376
Executing sqlpage.run_sql('{sql_file_path}') would exceed this limit. \n\

tests/display_text.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
select 'html' as component, $html as html;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
select 'dynamic' as component,
2+
sqlpage.run_sql('tests/display_text.sql', CONCAT('{"html": "', html,'"}')) as properties
3+
from (
4+
select 'It ' as html
5+
union all
6+
select 'works !'
7+
);

0 commit comments

Comments
 (0)