From 887948d259c2e1c5d1ef6554dcb1c67d0ae11769 Mon Sep 17 00:00:00 2001 From: steve-chavez Date: Thu, 27 Apr 2023 15:09:57 -0500 Subject: [PATCH] fix: missing=default error msg on generated column --- CHANGELOG.md | 1 + src/PostgREST/SchemaCache.hs | 22 +++++++++++++++------ test/spec/Feature/Query/InsertSpec.hs | 28 +++++++++++++++++++++++++-- test/spec/fixtures/schema.sql | 16 +++++++++++++++ 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09a317bcd..e76b14a10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - #2762, Fixes "permission denied for schema" error during schema cache load - @steve-chavez + - #2756, Fix bad error message on generated columns when using `Prefer: missing=default` - @steve-chavez ## [11.0.0] - 2023-04-16 diff --git a/src/PostgREST/SchemaCache.hs b/src/PostgREST/SchemaCache.hs index 59ce5f205..b86eded9e 100644 --- a/src/PostgREST/SchemaCache.hs +++ b/src/PostgREST/SchemaCache.hs @@ -40,7 +40,7 @@ import Text.InterpolatedString.Perl6 (q) import PostgREST.Config.Database (pgVersionStatement) import PostgREST.Config.PgVersion (PgVersion, pgVersion100, - pgVersion110) + pgVersion110, pgVersion120) import PostgREST.SchemaCache.Identifiers (AccessSet, FieldName, QualifiedIdentifier (..), Schema) @@ -516,11 +516,8 @@ tablesSqlQuery pgVer = c.relname::name AS table_name, a.attname::name AS column_name, d.description AS description, - CASE - WHEN seqclass.relname is null - THEN pg_get_expr(ad.adbin, ad.adrelid)::text - ELSE format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname)) - END AS column_default, + |] <> columnDefault <> + [q| not (a.attnotnull OR t.typtype = 'd' AND t.typnotnull) AS is_nullable, CASE WHEN t.typtype = 'd' THEN @@ -708,6 +705,19 @@ tablesSqlQuery pgVer = "ORDER BY table_schema, table_name" where relIsPartition = if pgVer >= pgVersion100 then " AND not c.relispartition " else mempty + columnDefault + | pgVer >= pgVersion120 = [q| + CASE + WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname)) + WHEN a.attgenerated = 's' THEN null + ELSE pg_get_expr(ad.adbin, ad.adrelid)::text + END AS column_default,|] + | pgVer >= pgVersion100 = [q| + CASE + WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname)) + ELSE pg_get_expr(ad.adbin, ad.adrelid)::text + END AS column_default,|] + | otherwise = "pg_get_expr(ad.adbin, ad.adrelid)::text as column_default," -- | Gets many-to-one relationships and one-to-one(O2O) relationships, which are a refinement of the many-to-one's allM2OandO2ORels :: PgVersion -> Bool -> SQL.Statement () [Relationship] diff --git a/test/spec/Feature/Query/InsertSpec.hs b/test/spec/Feature/Query/InsertSpec.hs index fbb933d1b..9babe03e9 100644 --- a/test/spec/Feature/Query/InsertSpec.hs +++ b/test/spec/Feature/Query/InsertSpec.hs @@ -13,7 +13,8 @@ import Text.Heredoc import PostgREST.Config.PgVersion (PgVersion, pgVersion100, pgVersion110, pgVersion112, - pgVersion130) + pgVersion120, pgVersion130, + pgVersion140) import Protolude hiding (get) import SpecHelper @@ -496,7 +497,7 @@ spec actualPgVersion = do } when (actualPgVersion >= pgVersion100) $ - it "inserts a default on a generated by default as identity column" $ do + it "inserts a default on a generated by default as identity column" $ request methodPost "/channels?columns=id,data,slug&select=data,slug" [("Prefer", "return=representation"), ("Prefer", "missing=default")] [json| { "slug": "foo" } |] `shouldRespondWith` @@ -505,6 +506,29 @@ spec actualPgVersion = do , matchHeaders = ["Preference-Applied" <:> "missing=default"] } + when (actualPgVersion >= pgVersion120) $ + it "fails with a good error message on generated always columns" $ + request methodPost "/foo?columns=a,b" [("Prefer", "return=representation"), ("Prefer", "missing=default")] + [json| [ + {"a": "val"}, + {"a": "val", "b": "val"} + ]|] + `shouldRespondWith` + (if actualPgVersion < pgVersion140 + then [json| { + "code": "42601", + "details": "Column \"b\" is a generated column.", + "hint": null, + "message": "cannot insert into column \"b\"" + }|] + else [json| { + "code": "428C9", + "details": "Column \"b\" is a generated column.", + "hint": null, + "message": "cannot insert a non-DEFAULT value into column \"b\"" + }|]) + { matchStatus = 400 } + it "inserts json that has duplicate keys" $ do request methodPost "/tbl_w_json" [("Prefer", "return=representation")] [json| { "data": { "a": 1, "a": 2 }, "id": 3 } |] diff --git a/test/spec/fixtures/schema.sql b/test/spec/fixtures/schema.sql index fbb1bc3d1..d78f0eacb 100644 --- a/test/spec/fixtures/schema.sql +++ b/test/spec/fixtures/schema.sql @@ -3128,3 +3128,19 @@ LANGUAGE sql AS $$ select current_setting('is_superuser')::boolean; $$; + +DO $do$ +BEGIN + IF current_setting('server_version_num')::INT >= 120000 THEN + CREATE TABLE test.foo ( + a text, + b text GENERATED ALWAYS AS ( + case WHEN a = 'telegram' THEN 'im' + WHEN a = 'proton' THEN 'email' + WHEN a = 'infinity' THEN 'idea' + ELSE 'bad idea' + end) stored + ); + END IF; +END +$do$;