|  | 
| 1 |  | -CREATE TYPE fortune_t AS (id int, message text); | 
|  | 1 | +create domain "text/html" as text; | 
| 2 | 2 | 
 | 
| 3 |  | -create or replace function fortune_template(f fortune_t) returns text as $$ | 
| 4 |  | -   SELECT format('<tr><td>%s</td><td>%s</td></tr>', $1.id, regexp_replace($1.message, '<', '<','g'));  | 
| 5 |  | -$$ language sql volatile; | 
|  | 3 | +create or replace function sanitize_html(text) returns text as $$ | 
|  | 4 | +  select replace(replace(replace(replace(replace($1, '&', '&'), '"', '"'),'>', '>'),'<', '<'), '''', ''') | 
|  | 5 | +$$ language sql immutable; | 
| 6 | 6 | 
 | 
| 7 |  | -create or replace function fortunes_template(fortunes fortune_t[]) returns text as $$ | 
| 8 |  | -WITH header AS ( | 
| 9 |  | -   SELECT 0 as id,'<!DOCTYPE html> | 
| 10 |  | -<html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>' as html | 
| 11 |  | -), footer AS ( | 
| 12 |  | -   SELECT 2,'</table></body></html>' as html | 
| 13 |  | -), fortunes AS ( | 
| 14 |  | -   SELECT unnest as fortune from unnest($1)  | 
| 15 |  | -), additional AS ( | 
| 16 |  | -   SELECT (-1, 'Additional fortune added at request time.')::fortune_t as f | 
| 17 |  | -), all_fortunes AS ( | 
| 18 |  | -   SELECT * from (SELECT * FROM fortunes UNION ALL SELECT * from additional) p ORDER BY (fortune).message | 
| 19 |  | -), fortunes_html AS ( | 
| 20 |  | -   SELECT 1,string_agg(fortune_template(fortune), '') from all_fortunes | 
| 21 |  | -), html AS ( | 
| 22 |  | -   SELECT * FROM header UNION SELECT * FROM fortunes_html UNION SELECT * from footer ORDER BY id | 
| 23 |  | -) | 
| 24 |  | -SELECT string_agg(html,'') from html; | 
| 25 |  | -$$ language sql volatile; | 
|  | 7 | +create or replace function fortune_template("Fortune") returns text as $$ | 
|  | 8 | +   SELECT format('<tr><td>%s</td><td>%s</td></tr>', $1.id, sanitize_html($1.message)); | 
|  | 9 | +$$ language sql immutable; | 
| 26 | 10 | 
 | 
| 27 |  | -create or replace function "fortunes.html"() returns bytea as $$ | 
| 28 |  | -DECLARE | 
| 29 |  | -   fortunes fortune_t[]; | 
| 30 |  | -BEGIN | 
| 31 |  | -   SET LOCAL "response.headers" = '[{"Content-Type": "text/html"}]'; | 
| 32 |  | -   SELECT array_agg(CAST((id,message) AS fortune_t)) FROM "Fortunes" INTO fortunes; | 
| 33 |  | -   RETURN convert_to(fortunes_template(fortunes), 'UTF8'); | 
| 34 |  | -END | 
| 35 |  | -$$ language plpgsql volatile; | 
|  | 11 | +create or replace function fortunes() returns "text/html" as $$ | 
|  | 12 | +   -- This is only necessary bc. of the benchmark: The domain gives us content-type: text/html, | 
|  | 13 | +   -- but the benchmark explicitly tests for the charset in the content-type. | 
|  | 14 | +   select set_config('response.headers', '[{"Content-Type": "text/html; charset=utf-8"}]', true); | 
|  | 15 | + | 
|  | 16 | +   select '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>' | 
|  | 17 | +      || string_agg(fortune_template(f), NULL order by f.message collate unicode asc) | 
|  | 18 | +      || '</table></body></html>' | 
|  | 19 | +   from (select * from "Fortune" union all select 0, 'Additional fortune added at request time.') f; | 
|  | 20 | +$$ language sql volatile; | 
0 commit comments