Explorar o código

Fix postgrest (#10064)

* Fix response media type for plaintext.

* raw-media-types option was removed.

* Rename json route, as json is a reserved keyword.

* Use stricter stability modifiers.

* Rename update path according to wiki

* Minor improvement to docker-compose file.

* Minor changes.

* Fix fortunes benchmark.

* Add clarifying comment for content-type.

* Disable 'fortunes' benchmark, as harness doesn't count queries correctly.

---------

Co-authored-by: Benjamin Maurer <[email protected]>
Benjamin M. hai 3 días
pai
achega
f99bb89276

+ 17 - 6
frameworks/Haskell/postgrest/README.md

@@ -7,7 +7,7 @@
 * [DB](src/db.sql)
 * [QUERY](src/query.sql)
 * [CACHED QUERY] Not Implemented
-* [UPDATE] Not Implemented
+* [UPDATE](src/update.sql)
 * [FORTUNES](src/fortunes.sql)
 
 ## Important Libraries
@@ -17,7 +17,7 @@ The tests were run with:
 ## Test URLs
 ### JSON
 
-http://localhost:3000/rpc/json
+http://localhost:3000/rpc/jsonser
 
 ### PLAINTEXT
 
@@ -31,14 +31,25 @@ http://localhost:3000/rpc/db
 
 http://localhost:3000/rpc/query?queries=
 
-### CACHED QUERY Not Implemented
+### CACHED QUERY - Not Implemented
 
 http://localhost:8080/cached_query?queries=
 
-### UPDATE Not Implemented
+### UPDATE - Not Working
 
-http://localhost:3000/rpc/update?queries=
+http://localhost:3000/rpc/updates?queries=
 
-### FORTUNES
+Technically, this is implemented (maybe not correctly though).
+However, the benchmark issues this as a GET request.
+PostgREST sets the transaction to READ ONLY for GET requests,
+as they are supposed to be idempotent.
+Hence this results in an error. Calling the endpoint with POST
+works though.
+
+### FORTUNES - Disabled
 
 http://localhost:3000/rpc/fortunes.html
+
+This is supposed to work, but somehow the benchmark harness
+doesn't count the queries correctly?
+Was not able to figure this one out.

+ 1 - 2
frameworks/Haskell/postgrest/benchmark_config.json

@@ -3,11 +3,10 @@
   "tests": [
     {
       "default": {
-        "json_url": "/rpc/json",
+        "json_url": "/rpc/jsonser",
         "plaintext_url": "/rpc/plaintext",
         "db_url": "/rpc/db",
         "query_url": "/rpc/queries?queries=",
-        "fortunes_url": "/rpc/fortunes.html",
         "port": 3000,
         "approach": "Realistic",
         "classification": "Micro",

+ 1 - 0
frameworks/Haskell/postgrest/config.toml

@@ -6,6 +6,7 @@ urls.plaintext = "/rpc/plaintext"
 urls.json = "/rpc/json"
 urls.db = "/rpc/db"
 urls.query = "/rpc/queries?queries="
+urls.fortune = "/rpc/fortunes"
 approach = "Realistic"
 classification = "Micro"
 database = "postgres"

+ 3 - 2
frameworks/Haskell/postgrest/docker-compose.yml

@@ -1,4 +1,3 @@
-version: '3'
 services:
   tfb-database:
     build: 
@@ -13,4 +12,6 @@ services:
       dockerfile: postgrest.dockerfile
       context: .
     ports:
-      - 3030:3000
+      - 3030:3000
+    depends_on:
+      - tfb-database

+ 0 - 1
frameworks/Haskell/postgrest/postgrest.conf

@@ -16,4 +16,3 @@ role-claim-key = "$(PGRST_ROLE_CLAIM_KEY)"
 max-rows = "$(PGRST_MAX_ROWS)"
 pre-request = "$(PGRST_PRE_REQUEST)"
 root-spec = "$(PGRST_ROOT_SPEC)"
-raw-media-types = "$(PGRST_RAW_MEDIA_TYPES)"

+ 1 - 3
frameworks/Haskell/postgrest/postgrest.dockerfile

@@ -23,12 +23,10 @@ ENV PGRST_MAX_ROWS=
 ENV PGRST_PRE_REQUEST=
 ENV PGRST_ROLE_CLAIM_KEY=.role
 ENV PGRST_ROOT_SPEC=
-ENV PGRST_RAW_MEDIA_TYPES=
 
 ENV PGRST_DB_URI=postgres://benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world
 ENV PGRST_DB_SCHEMA=public
 ENV PGRST_DB_ANON_ROLE=benchmarkdbuser
-ENV PGRST_RAW_MEDIA_TYPES="text/html, text/plain"
 ENV PGRST_DB_POOL=64
 RUN mkdir /app
 COPY src /app
@@ -37,4 +35,4 @@ WORKDIR /app
 
 EXPOSE 3000
 
-ENTRYPOINT [ "/app/entrypoint.sh" ]
+ENTRYPOINT [ "/app/entrypoint.sh" ]

+ 17 - 32
frameworks/Haskell/postgrest/src/fortunes.sql

@@ -1,35 +1,20 @@
-CREATE TYPE fortune_t AS (id int, message text);
+create domain "text/html" as text;
 
-create or replace function fortune_template(f fortune_t) returns text as $$
-   SELECT format('<tr><td>%s</td><td>%s</td></tr>', $1.id, regexp_replace($1.message, '<', '&lt;','g')); 
-$$ language sql volatile;
+create or replace function sanitize_html(text) returns text as $$
+  select replace(replace(replace(replace(replace($1, '&', '&amp;'), '"', '&quot;'),'>', '&gt;'),'<', '&lt;'), '''', '&apos;')
+$$ language sql immutable;
 
-create or replace function fortunes_template(fortunes fortune_t[]) returns text as $$
-WITH header AS (
-   SELECT 0 as id,'<!DOCTYPE html>
-<html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>' as html
-), footer AS (
-   SELECT 2,'</table></body></html>' as html
-), fortunes AS (
-   SELECT unnest as fortune from unnest($1) 
-), additional AS (
-   SELECT (-1, 'Additional fortune added at request time.')::fortune_t as f
-), all_fortunes AS (
-   SELECT * from (SELECT * FROM fortunes UNION ALL SELECT * from additional) p ORDER BY (fortune).message
-), fortunes_html AS (
-   SELECT 1,string_agg(fortune_template(fortune), '') from all_fortunes
-), html AS (
-   SELECT * FROM header UNION SELECT * FROM fortunes_html UNION SELECT * from footer ORDER BY id
-)
-SELECT string_agg(html,'') from html;
-$$ language sql volatile;
+create or replace function fortune_template("Fortune") returns text as $$
+   SELECT format('<tr><td>%s</td><td>%s</td></tr>', $1.id, sanitize_html($1.message));
+$$ language sql immutable;
 
-create or replace function "fortunes.html"() returns bytea as $$
-DECLARE
-   fortunes fortune_t[];
-BEGIN
-   SET LOCAL "response.headers" = '[{"Content-Type": "text/html"}]';
-   SELECT array_agg(CAST((id,message) AS fortune_t)) FROM "Fortunes" INTO fortunes;
-   RETURN convert_to(fortunes_template(fortunes), 'UTF8');
-END
-$$ language plpgsql volatile;
+create or replace function fortunes() returns "text/html" as $$
+   -- This is only necessary bc. of the benchmark: The domain gives us content-type: text/html,
+   -- but the benchmark explicitly tests for the charset in the content-type.
+   select set_config('response.headers', '[{"Content-Type": "text/html; charset=utf-8"}]', true);
+
+   select '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
+      || string_agg(fortune_template(f), NULL order by f.message collate unicode asc)
+      || '</table></body></html>'
+   from (select * from "Fortune" union all select 0, 'Additional fortune added at request time.') f;
+$$ language sql volatile;

+ 2 - 2
frameworks/Haskell/postgrest/src/json.sql

@@ -1,3 +1,3 @@
-create function json() returns json as $$
+create function jsonser() returns json as $$
    SELECT json_build_object('message', 'Hello, World!');
-$$ language sql volatile;
+$$ language sql immutable;

+ 4 - 2
frameworks/Haskell/postgrest/src/plaintext.sql

@@ -1,3 +1,5 @@
-create function plaintext() returns text as $$
+create domain "text/plain" as text;
+
+create function plaintext() returns "text/plain" as $$
    SELECT 'Hello, World!';
-$$ language sql volatile;
+$$ language sql immutable;

+ 3 - 4
frameworks/Haskell/postgrest/src/update.sql

@@ -1,4 +1,4 @@
-create or replace function update(queries text default '') returns jsonb as $$
+create or replace function updates(queries text default '') returns jsonb as $$
 DECLARE
    r "World"%ROWTYPE;
    j jsonb := jsonb_build_array();
@@ -6,11 +6,10 @@ DECLARE
    rnd_id int;
    count int;
 BEGIN
-   SET TRANSACTION READ WRITE;
    IF queries ~ '^[1-9]\d{0,2}$' THEN
       count := CAST(queries as int);
-   ELSE 
-      count := 1;   
+   ELSE
+      count := 1;
    END IF;
    IF count > 500 THEN
       count := 500;