From 593c6adae6d97560f9f0cfaf06caa6e095ae8e85 Mon Sep 17 00:00:00 2001 From: Stefan Stefanov Date: Sun, 25 Feb 2024 00:38:05 +0200 Subject: [PATCH 1/5] Implement partial support of "format" for type "string" --- postgres-json-schema--0.1.1.sql | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/postgres-json-schema--0.1.1.sql b/postgres-json-schema--0.1.1.sql index 7423fad..7dea096 100644 --- a/postgres-json-schema--0.1.1.sql +++ b/postgres-json-schema--0.1.1.sql @@ -237,6 +237,35 @@ BEGIN END IF; END IF; + IF schema ? 'format' AND jsonb_typeof(data) = 'string' + AND schema->>'format' in ('date-time', 'date', 'time', 'duration', 'ipv4', 'ipv6', 'uuid') THEN + declare + str_format text := schema->>'format'; + str_value text := data #>> '{}'; + dynsql text; + begin + dynsql := format( + 'select %L::%s', + str_value, + case str_format + when 'date-time' then 'timestamptz' + when 'duration' then 'interval' + when 'ipv4' then 'inet' + when 'ipv6' then 'inet' + else str_format + end + ); + -- raise notice '%', dynsql; + execute dynsql; + if str_format in ('ipv4', 'ipv6') and (str_format = 'ipv6' and str_value !~ ':' or + str_format = 'ipv4' and str_value ~ ':') then + RETURN false; + end if; + exception when others then + RETURN false; + end; + END IF; + IF schema ? 'patternProperties' AND jsonb_typeof(data) = 'object' THEN FOR prop IN SELECT jsonb_object_keys(data) LOOP FOR pattern IN SELECT jsonb_object_keys(schema->'patternProperties') LOOP From 8006e9b44e0452944967bb66127c8f0b5332abe1 Mon Sep 17 00:00:00 2001 From: Stefan Stefanov Date: Sun, 25 Feb 2024 20:25:34 +0200 Subject: [PATCH 2/5] Provide partial support for string "format" constraint --- postgres-json-schema--0.1.1.sql | 43 +++++++++++++-------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/postgres-json-schema--0.1.1.sql b/postgres-json-schema--0.1.1.sql index 7dea096..1d8d5c0 100644 --- a/postgres-json-schema--0.1.1.sql +++ b/postgres-json-schema--0.1.1.sql @@ -237,33 +237,24 @@ BEGIN END IF; END IF; - IF schema ? 'format' AND jsonb_typeof(data) = 'string' - AND schema->>'format' in ('date-time', 'date', 'time', 'duration', 'ipv4', 'ipv6', 'uuid') THEN - declare - str_format text := schema->>'format'; - str_value text := data #>> '{}'; - dynsql text; - begin - dynsql := format( - 'select %L::%s', - str_value, - case str_format - when 'date-time' then 'timestamptz' - when 'duration' then 'interval' - when 'ipv4' then 'inet' - when 'ipv6' then 'inet' - else str_format - end - ); - -- raise notice '%', dynsql; - execute dynsql; - if str_format in ('ipv4', 'ipv6') and (str_format = 'ipv6' and str_value !~ ':' or - str_format = 'ipv4' and str_value ~ ':') then - RETURN false; - end if; - exception when others then + IF schema ? 'format' AND jsonb_typeof(data) = 'string' THEN + DECLARE + target text := (data #>> '{}'); + BEGIN + CASE (schema->>'format') + WHEN 'date-time' THEN PERFORM target::timestamptz; + WHEN 'date' THEN PERFORM target::date; + WHEN 'time' THEN PERFORM target::time; + WHEN 'duration' THEN PERFORM target::interval; + WHEN 'uuid' THEN PERFORM target::uuid; + WHEN 'ipv6' THEN PERFORM target::inet; IF target NOT LIKE '%:%' THEN RAISE; END IF; + WHEN 'ipv4' THEN PERFORM target::inet; IF target LIKE '%:%' THEN RAISE; END IF; + WHEN 'regex' THEN PERFORM '' ~ target; + ELSE null; + END CASE; + EXCEPTION WHEN OTHERS THEN RETURN false; - end; + END; END IF; IF schema ? 'patternProperties' AND jsonb_typeof(data) = 'object' THEN From 24364f42f3ed1e43f13b80c9455ab963c76b0a88 Mon Sep 17 00:00:00 2001 From: Stefan Stefanov Date: Sun, 25 Feb 2024 21:01:22 +0200 Subject: [PATCH 3/5] Provide partial support for string "format" constraint --- postgres-json-schema--0.1.1.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postgres-json-schema--0.1.1.sql b/postgres-json-schema--0.1.1.sql index 1d8d5c0..f032267 100644 --- a/postgres-json-schema--0.1.1.sql +++ b/postgres-json-schema--0.1.1.sql @@ -250,7 +250,7 @@ BEGIN WHEN 'ipv6' THEN PERFORM target::inet; IF target NOT LIKE '%:%' THEN RAISE; END IF; WHEN 'ipv4' THEN PERFORM target::inet; IF target LIKE '%:%' THEN RAISE; END IF; WHEN 'regex' THEN PERFORM '' ~ target; - ELSE null; + ELSE null; -- consistent with current behaviour - validate positive for unsupported options END CASE; EXCEPTION WHEN OTHERS THEN RETURN false; From 76a47d1f55bae3ca96371c4edba76188bf76beea Mon Sep 17 00:00:00 2001 From: Stefan Stefanov Date: Thu, 7 Mar 2024 09:33:20 +0200 Subject: [PATCH 4/5] "email" format added, regex implementation Uses the HTML5-style regex suggested by [Pierre Baumard](https://github.com/pbaumard) --- postgres-json-schema--0.1.1.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/postgres-json-schema--0.1.1.sql b/postgres-json-schema--0.1.1.sql index f032267..4e4f829 100644 --- a/postgres-json-schema--0.1.1.sql +++ b/postgres-json-schema--0.1.1.sql @@ -240,6 +240,7 @@ BEGIN IF schema ? 'format' AND jsonb_typeof(data) = 'string' THEN DECLARE target text := (data #>> '{}'); + EMAIL_RX constant text := '^[\w.!#$%&''*+/=?^`{|}~-]+@[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?)*$'; BEGIN CASE (schema->>'format') WHEN 'date-time' THEN PERFORM target::timestamptz; @@ -250,6 +251,7 @@ BEGIN WHEN 'ipv6' THEN PERFORM target::inet; IF target NOT LIKE '%:%' THEN RAISE; END IF; WHEN 'ipv4' THEN PERFORM target::inet; IF target LIKE '%:%' THEN RAISE; END IF; WHEN 'regex' THEN PERFORM '' ~ target; + WHEN 'email' THEN IF target !~* EMAIL_RX THEN RAISE; END IF; ELSE null; -- consistent with current behaviour - validate positive for unsupported options END CASE; EXCEPTION WHEN OTHERS THEN From 942d4358f38c4206a7be4fd7aceab88e5f89f442 Mon Sep 17 00:00:00 2001 From: Stefan Stefanov Date: Wed, 20 Mar 2024 09:09:02 +0200 Subject: [PATCH 5/5] Case-sensitive mail gegex suggested by [pbaumard](https://github.com/pbaumard) --- postgres-json-schema--0.1.1.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postgres-json-schema--0.1.1.sql b/postgres-json-schema--0.1.1.sql index 4e4f829..880b3d0 100644 --- a/postgres-json-schema--0.1.1.sql +++ b/postgres-json-schema--0.1.1.sql @@ -240,7 +240,7 @@ BEGIN IF schema ? 'format' AND jsonb_typeof(data) = 'string' THEN DECLARE target text := (data #>> '{}'); - EMAIL_RX constant text := '^[\w.!#$%&''*+/=?^`{|}~-]+@[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?)*$'; + EMAIL_RX constant text := '^[a-zA-Z0-9.!#$%&''*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$'; BEGIN CASE (schema->>'format') WHEN 'date-time' THEN PERFORM target::timestamptz; @@ -251,7 +251,7 @@ BEGIN WHEN 'ipv6' THEN PERFORM target::inet; IF target NOT LIKE '%:%' THEN RAISE; END IF; WHEN 'ipv4' THEN PERFORM target::inet; IF target LIKE '%:%' THEN RAISE; END IF; WHEN 'regex' THEN PERFORM '' ~ target; - WHEN 'email' THEN IF target !~* EMAIL_RX THEN RAISE; END IF; + WHEN 'email' THEN IF target !~ EMAIL_RX THEN RAISE; END IF; ELSE null; -- consistent with current behaviour - validate positive for unsupported options END CASE; EXCEPTION WHEN OTHERS THEN