Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaa4fbc370 | ||
|
|
5e65b2afaf | ||
|
|
3408998629 | ||
|
|
b8c5d212ea | ||
|
|
c8e4f38984 | ||
|
|
44dd73adcc |
@@ -5,6 +5,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## [10.1.1] - 2022-11-08
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #2548, Fix regression when embedding views with partial references to multi column FKs - @wolfgangwalther
|
||||||
|
- #2558, Fix regression when requesting limit=0 and `db-max-row` is set - @laurenceisla
|
||||||
|
|
||||||
## [10.1.0] - 2022-10-28
|
## [10.1.0] - 2022-10-28
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@ let
|
|||||||
allOverlays.checked-shell-script
|
allOverlays.checked-shell-script
|
||||||
allOverlays.gitignore
|
allOverlays.gitignore
|
||||||
allOverlays.postgis
|
allOverlays.postgis
|
||||||
allOverlays.postgresql-default
|
(allOverlays.postgresql-default { inherit patches; })
|
||||||
allOverlays.postgresql-legacy
|
allOverlays.postgresql-legacy
|
||||||
allOverlays.postgresql-future
|
allOverlays.postgresql-future
|
||||||
(allOverlays.haskell-packages { inherit compiler; })
|
(allOverlays.haskell-packages { inherit compiler; })
|
||||||
|
|||||||
@@ -32,7 +32,14 @@ let
|
|||||||
hashtables = lib.dontCheck prev.hashtables_1_3_1;
|
hashtables = lib.dontCheck prev.hashtables_1_3_1;
|
||||||
hasql = lib.dontCheck prev.hasql_1_6_1_4;
|
hasql = lib.dontCheck prev.hasql_1_6_1_4;
|
||||||
hasql-dynamic-statements = lib.dontCheck prev.hasql-dynamic-statements_0_3_1_2;
|
hasql-dynamic-statements = lib.dontCheck prev.hasql-dynamic-statements_0_3_1_2;
|
||||||
hasql-pool = lib.dontCheck prev.hasql-pool_0_8_0_4;
|
hasql-pool = lib.dontCheck
|
||||||
|
(prev.callHackageDirect
|
||||||
|
{
|
||||||
|
pkg = "hasql-pool";
|
||||||
|
ver = "0.8.0.6";
|
||||||
|
sha256 = "sha256-2u/cwPk8XfXffaDRzGeyzhL+9k2+2T4b8bGOZwz8AX0=";
|
||||||
|
}
|
||||||
|
{ });
|
||||||
hasql-transaction = lib.dontCheck prev.hasql-transaction_1_0_1_2;
|
hasql-transaction = lib.dontCheck prev.hasql-transaction_1_0_1_2;
|
||||||
isomorphism-class = lib.unmarkBroken prev.isomorphism-class;
|
isomorphism-class = lib.unmarkBroken prev.isomorphism-class;
|
||||||
lens = lib.dontCheck prev.lens_5_2;
|
lens = lib.dontCheck prev.lens_5_2;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
self: super:
|
{ patches }: self: super:
|
||||||
# Overlay that sets the default version of PostgreSQL.
|
# Overlay that sets the default version of PostgreSQL.
|
||||||
|
with patches;
|
||||||
{
|
{
|
||||||
postgresql = super.postgresql_14;
|
postgresql = super.postgresql_15.overrideAttrs ({ patches ? [ ], ... }: {
|
||||||
|
patches = patches ++ [ postgresql-atexit ];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,6 @@
|
|||||||
./static-haskell-nix-ghc-bignum.patch;
|
./static-haskell-nix-ghc-bignum.patch;
|
||||||
static-haskell-nix-openssl =
|
static-haskell-nix-openssl =
|
||||||
./static-haskell-nix-openssl.patch;
|
./static-haskell-nix-openssl.patch;
|
||||||
|
postgresql-atexit =
|
||||||
|
./postgresql-atexit.patch;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
--- a/src/interfaces/libpq/Makefile
|
||||||
|
+++ b/src/interfaces/libpq/Makefile
|
||||||
|
@@ -118,7 +118,7 @@ backend_src = $(top_srcdir)/src/backend
|
||||||
|
libpq-refs-stamp: $(shlib)
|
||||||
|
ifneq ($(enable_coverage), yes)
|
||||||
|
ifeq (,$(filter aix solaris,$(PORTNAME)))
|
||||||
|
- @if nm -A -u $< 2>/dev/null | grep -v __cxa_atexit | grep exit; then \
|
||||||
|
+ @if nm -A -u $< 2>/dev/null | grep " exit"; then \
|
||||||
|
echo 'libpq must not be calling any function which invokes exit'; exit 1; \
|
||||||
|
fi
|
||||||
|
endif
|
||||||
@@ -35,7 +35,7 @@ let
|
|||||||
overlays =
|
overlays =
|
||||||
[
|
[
|
||||||
allOverlays.postgresql-future
|
allOverlays.postgresql-future
|
||||||
allOverlays.postgresql-default
|
(allOverlays.postgresql-default { inherit patches; })
|
||||||
(allOverlays.haskell-packages { inherit compiler extraOverrides; })
|
(allOverlays.haskell-packages { inherit compiler extraOverrides; })
|
||||||
# Disable failing tests for postgresql on musl that should have no impact
|
# Disable failing tests for postgresql on musl that should have no impact
|
||||||
# on the libpq that we need (collate.icu.utf8 and foreign regression
|
# on the libpq that we need (collate.icu.utf8 and foreign regression
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
name: postgrest
|
name: postgrest
|
||||||
version: 10.1.0
|
version: 10.1.1
|
||||||
synopsis: REST API for any Postgres database
|
synopsis: REST API for any Postgres database
|
||||||
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
description: Reads the schema of a PostgreSQL database and creates RESTful routes
|
||||||
for tables, views, and functions, supporting all HTTP methods that security
|
for tables, views, and functions, supporting all HTTP methods that security
|
||||||
@@ -89,7 +89,7 @@ library
|
|||||||
, hasql >= 1.6.1.1 && < 1.7
|
, hasql >= 1.6.1.1 && < 1.7
|
||||||
, hasql-dynamic-statements >= 0.3.1 && < 0.4
|
, hasql-dynamic-statements >= 0.3.1 && < 0.4
|
||||||
, hasql-notifications >= 0.1 && < 0.3
|
, hasql-notifications >= 0.1 && < 0.3
|
||||||
, hasql-pool >= 0.8.0.2 && < 0.9
|
, hasql-pool >= 0.8.0.6 && < 0.9
|
||||||
, hasql-transaction >= 1.0.1 && < 1.1
|
, hasql-transaction >= 1.0.1 && < 1.1
|
||||||
, heredoc >= 0.2 && < 0.3
|
, heredoc >= 0.2 && < 0.3
|
||||||
, http-types >= 0.12.2 && < 0.13
|
, http-types >= 0.12.2 && < 0.13
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ import PostgREST.MediaType (MTPlanAttrs (..),
|
|||||||
MTPlanFormat (..),
|
MTPlanFormat (..),
|
||||||
MediaType (..))
|
MediaType (..))
|
||||||
import PostgREST.RangeQuery (NonnegRange, allRange,
|
import PostgREST.RangeQuery (NonnegRange, allRange,
|
||||||
|
convertToLimitZeroRange,
|
||||||
hasLimitZero,
|
hasLimitZero,
|
||||||
limitZeroRange,
|
|
||||||
rangeRequested)
|
rangeRequested)
|
||||||
import PostgREST.SchemaCache (SchemaCache (..))
|
import PostgREST.SchemaCache (SchemaCache (..))
|
||||||
import PostgREST.SchemaCache.Identifiers (FieldName,
|
import PostgREST.SchemaCache.Identifiers (FieldName,
|
||||||
@@ -354,7 +354,7 @@ apiRequest conf sCache req reqBody queryparams@QueryParams{..} PathInfo{pathName
|
|||||||
|
|
||||||
-- Bypass all the ranges and send only the limit zero range (0 <= x <= -1) if
|
-- Bypass all the ranges and send only the limit zero range (0 <= x <= -1) if
|
||||||
-- limit=0 is present in the query params (not allowed for the Range header)
|
-- limit=0 is present in the query params (not allowed for the Range header)
|
||||||
ranges = HM.insert "limit" (if hasLimitZero limitRange then limitZeroRange else headerAndLimitRange) qsRanges
|
ranges = HM.insert "limit" (convertToLimitZeroRange limitRange headerAndLimitRange) qsRanges
|
||||||
-- The only emptyRange allowed is the limit zero range
|
-- The only emptyRange allowed is the limit zero range
|
||||||
isInvalidRange = topLevelRange == emptyRange && not (hasLimitZero limitRange)
|
isInvalidRange = topLevelRange == emptyRange && not (hasLimitZero limitRange)
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import PostgREST.Config (AppConfig (..))
|
|||||||
import PostgREST.Error (Error (..))
|
import PostgREST.Error (Error (..))
|
||||||
import PostgREST.Query.SqlFragment (sourceCTEName)
|
import PostgREST.Query.SqlFragment (sourceCTEName)
|
||||||
import PostgREST.RangeQuery (NonnegRange, allRange,
|
import PostgREST.RangeQuery (NonnegRange, allRange,
|
||||||
|
convertToLimitZeroRange,
|
||||||
restrictRange)
|
restrictRange)
|
||||||
import PostgREST.SchemaCache (SchemaCache (..))
|
import PostgREST.SchemaCache (SchemaCache (..))
|
||||||
import PostgREST.SchemaCache.Identifiers (FieldName,
|
import PostgREST.SchemaCache.Identifiers (FieldName,
|
||||||
@@ -123,7 +124,7 @@ treeRestrictRange _ (ActionMutate _) request = Right request
|
|||||||
treeRestrictRange maxRows _ request = pure $ nodeRestrictRange maxRows <$> request
|
treeRestrictRange maxRows _ request = pure $ nodeRestrictRange maxRows <$> request
|
||||||
where
|
where
|
||||||
nodeRestrictRange :: Maybe Integer -> ReadPlan -> ReadPlan
|
nodeRestrictRange :: Maybe Integer -> ReadPlan -> ReadPlan
|
||||||
nodeRestrictRange m q@ReadPlan{range_=r} = q{range_=restrictRange m r }
|
nodeRestrictRange m q@ReadPlan{range_=r} = q{range_= convertToLimitZeroRange r (restrictRange m r) }
|
||||||
|
|
||||||
-- add relationships to the nodes of the tree by traversing the forest while keeping track of the parentNode(https://stackoverflow.com/questions/22721064/get-the-parent-of-a-node-in-data-tree-haskell#comment34627048_22721064)
|
-- add relationships to the nodes of the tree by traversing the forest while keeping track of the parentNode(https://stackoverflow.com/questions/22721064/get-the-parent-of-a-node-in-data-tree-haskell#comment34627048_22721064)
|
||||||
-- also adds aliasing
|
-- also adds aliasing
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ module PostgREST.RangeQuery (
|
|||||||
, allRange
|
, allRange
|
||||||
, limitZeroRange
|
, limitZeroRange
|
||||||
, hasLimitZero
|
, hasLimitZero
|
||||||
|
, convertToLimitZeroRange
|
||||||
, NonnegRange
|
, NonnegRange
|
||||||
, rangeStatusHeader
|
, rangeStatusHeader
|
||||||
, contentRangeH
|
, contentRangeH
|
||||||
@@ -86,6 +87,12 @@ limitZeroRange = Range (BoundaryBelow 0) (BoundaryAbove (-1))
|
|||||||
hasLimitZero :: Range Integer -> Bool
|
hasLimitZero :: Range Integer -> Bool
|
||||||
hasLimitZero r = rangeUpper r == rangeUpper limitZeroRange
|
hasLimitZero r = rangeUpper r == rangeUpper limitZeroRange
|
||||||
|
|
||||||
|
-- Used to convert a range into a special limitZeroRange if it has a
|
||||||
|
-- limit=0 in order to bypass validations for empty ranges.
|
||||||
|
convertToLimitZeroRange :: Range Integer -> Range Integer -> Range Integer
|
||||||
|
convertToLimitZeroRange range fallbackRange =
|
||||||
|
if hasLimitZero range then limitZeroRange else fallbackRange
|
||||||
|
|
||||||
rangeStatusHeader :: NonnegRange -> Int64 -> Maybe Int64 -> (Status, Header)
|
rangeStatusHeader :: NonnegRange -> Int64 -> Maybe Int64 -> (Status, Header)
|
||||||
rangeStatusHeader topLevelRange queryTotal tableTotal =
|
rangeStatusHeader topLevelRange queryTotal tableTotal =
|
||||||
let lower = rangeOffset topLevelRange
|
let lower = rangeOffset topLevelRange
|
||||||
|
|||||||
@@ -804,6 +804,7 @@ allViewsKeyDependencies =
|
|||||||
select
|
select
|
||||||
contype::text as contype,
|
contype::text as contype,
|
||||||
conname,
|
conname,
|
||||||
|
array_length(conkey, 1) as ncol,
|
||||||
conrelid as resorigtbl,
|
conrelid as resorigtbl,
|
||||||
col as resorigcol,
|
col as resorigcol,
|
||||||
ord
|
ord
|
||||||
@@ -815,6 +816,7 @@ allViewsKeyDependencies =
|
|||||||
select
|
select
|
||||||
concat(contype, '_ref') as contype,
|
concat(contype, '_ref') as contype,
|
||||||
conname,
|
conname,
|
||||||
|
array_length(confkey, 1) as ncol,
|
||||||
confrelid,
|
confrelid,
|
||||||
col,
|
col,
|
||||||
ord
|
ord
|
||||||
@@ -980,7 +982,9 @@ allViewsKeyDependencies =
|
|||||||
join pg_class tbl on tbl.oid = rep.resorigtbl
|
join pg_class tbl on tbl.oid = rep.resorigtbl
|
||||||
join pg_attribute col on col.attrelid = tbl.oid and col.attnum = rep.resorigcol
|
join pg_attribute col on col.attrelid = tbl.oid and col.attnum = rep.resorigcol
|
||||||
join pg_namespace sch on sch.oid = tbl.relnamespace
|
join pg_namespace sch on sch.oid = tbl.relnamespace
|
||||||
group by sch.nspname, tbl.relname, rep.view_schema, rep.view_name, pks_fks.conname, pks_fks.contype
|
group by sch.nspname, tbl.relname, rep.view_schema, rep.view_name, pks_fks.conname, pks_fks.contype, pks_fks.ncol
|
||||||
|
-- make sure we only return key for which all columns are referenced in the view - no partial PKs or FKs
|
||||||
|
having ncol = array_length(array_agg(row(col.attname, view_columns) order by pks_fks.ord), 1)
|
||||||
|]
|
|]
|
||||||
|
|
||||||
param :: HE.Value a -> HE.Params a
|
param :: HE.Value a -> HE.Params a
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ extra-deps:
|
|||||||
- hasql-dynamic-statements-0.3.1.2
|
- hasql-dynamic-statements-0.3.1.2
|
||||||
- hasql-implicits-0.1.0.5
|
- hasql-implicits-0.1.0.5
|
||||||
- hasql-notifications-0.2.0.3
|
- hasql-notifications-0.2.0.3
|
||||||
- hasql-pool-0.8.0.2
|
- hasql-pool-0.8.0.6
|
||||||
- hasql-transaction-1.0.1.2
|
- hasql-transaction-1.0.1.2
|
||||||
- isomorphism-class-0.1.0.6
|
- isomorphism-class-0.1.0.6
|
||||||
- lens-aeson-1.1.3
|
- lens-aeson-1.1.3
|
||||||
|
|||||||
+4
-4
@@ -61,12 +61,12 @@ packages:
|
|||||||
original:
|
original:
|
||||||
hackage: hasql-notifications-0.2.0.3
|
hackage: hasql-notifications-0.2.0.3
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-pool-0.8.0.2@sha256:15473f336c2bd1da161cd03635841f38b0c177d7b8662762c8708c239a428f04,1907
|
hackage: hasql-pool-0.8.0.6@sha256:b63bb83409bab5bc20ff24f5d62205e9b117701a0fc24531ddeac20ab8c2a42c,1818
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
size: 505
|
size: 346
|
||||||
sha256: 495dfdf8b7f7d910e2e8a7a7e8d71c8dbf9d439e048de5bc2a66a762011cbdc2
|
sha256: c4100946b7eae44375511e35a393abe2e1db0e5637c68cea8f53176b796bfd5b
|
||||||
original:
|
original:
|
||||||
hackage: hasql-pool-0.8.0.2
|
hackage: hasql-pool-0.8.0.6
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-transaction-1.0.1.2@sha256:297b158cd1f0727f9b0e175bd7d3741c1bcb725a8094956d0ee79b41aafdb30a,2890
|
hackage: hasql-transaction-1.0.1.2@sha256:297b158cd1f0727f9b0e175bd7d3741c1bcb725a8094956d0ee79b41aafdb30a,2890
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
|
|||||||
@@ -563,3 +563,6 @@ spec =
|
|||||||
}
|
}
|
||||||
]|]
|
]|]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
|
it "should not expose hidden FKs" $
|
||||||
|
get "/va?select=vb(*)" `shouldRespondWith` 200
|
||||||
|
|||||||
@@ -114,3 +114,10 @@ spec =
|
|||||||
{ "first_name": "Daniel B.", "last_name": "Lyon" },
|
{ "first_name": "Daniel B.", "last_name": "Lyon" },
|
||||||
{ "first_name": "Edwin S.", "last_name": "Smith" } ]|]
|
{ "first_name": "Edwin S.", "last_name": "Smith" } ]|]
|
||||||
{ matchStatus = 200 }
|
{ matchStatus = 200 }
|
||||||
|
|
||||||
|
context "max-rows is set and limits are requested" $ do
|
||||||
|
it "should work with limit 0" $
|
||||||
|
get "/items?limit=0"
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json| [] |]
|
||||||
|
{ matchHeaders = ["Content-Range" <:> "*/*"] }
|
||||||
|
|||||||
Vendored
+16
@@ -2980,3 +2980,19 @@ select parent.parent as grandparent,
|
|||||||
join public.i2459_self_t as child
|
join public.i2459_self_t as child
|
||||||
on child.parent = parent.id
|
on child.parent = parent.id
|
||||||
where child.type = 'B';
|
where child.type = 'B';
|
||||||
|
|
||||||
|
-- issue https://github.com/PostgREST/postgrest/issues/2548
|
||||||
|
CREATE TABLE public.ta (
|
||||||
|
a1 INT PRIMARY KEY,
|
||||||
|
a2 INT,
|
||||||
|
UNIQUE (a1, a2)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE public.tb (
|
||||||
|
b1 INT REFERENCES public.ta (a1),
|
||||||
|
b2 INT,
|
||||||
|
FOREIGN KEY (b1, b2) REFERENCES public.ta (a1, a2)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE VIEW test.va AS SELECT a1 FROM public.ta;
|
||||||
|
CREATE VIEW test.vb AS SELECT b1 FROM public.tb;
|
||||||
|
|||||||
Reference in New Issue
Block a user