Compare commits

...
10 Commits
Author SHA1 Message Date
steve-chavez b3b4e5ff35 bump version to 14.1 2025-11-05 09:09:22 -05:00
Taimoor ZaeemandWolfgang Walther 84f437b6c9 chore(changelog): update versioning scheme description
The changelog description mentioned that we follow semantic
versioning but from now on we don't. Hence updated the description
to reflect new versioning policy.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
(cherry picked from commit eb908c696f)
2025-11-04 07:25:35 +00:00
renovate[bot]andWolfgang Walther a3c8065e58 chore(deps): update actions/checkout digest to 71cf226 2025-11-03 20:47:35 +00:00
Taimoor ZaeemandWolfgang Walther c797c09e22 fix: server-host !6 incorrectly binds to IPv4 address
Updates streaming-commons to version 0.2.3.1. This resolves #3202.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
(cherry picked from commit 58efc2680e)
2025-11-03 11:40:09 +00:00
Taimoor ZaeemandWolfgang Walther fc6fbe9748 chore(changelog): fix typo in changelog entry
Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
(cherry picked from commit 8180905890)
2025-11-01 09:18:20 +00:00
Taimoor ZaeemandSteve Chavez 4aa712b9d8 fix: db-pre-config function failing with pg reserved words
When db-pre-config is accidentally set to a pg reserved word
like "true", it fails with a confusing error. The function
names should be properly quoted to avoid such errors. This commit
resolves this by quoting the pre-config function name.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
(cherry picked from commit a688878236)
2025-10-30 14:32:37 -05:00
Taimoor ZaeemandSteve Chavez 939061baff refactor: move escapeIdent function to Identifiers.hs
Moves the functions `escapeIdent` and `trimNullChars` to
SchemaCache/Identifiers.hs module.

Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
(cherry picked from commit 66f84c5903)
2025-10-30 12:24:49 -05:00
Taimoor ZaeemandSteve Chavez 6150d53592 refactor: sort exports of Identifiers.hs and SqlFragments.hs
Signed-off-by: Taimoor Zaeem <taimoorzaeem@gmail.com>
(cherry picked from commit 5d9b169380)
2025-10-30 12:24:49 -05:00
Wolfgang Walther d245e07df5 ci: fix tag job with new release workflow
A single component version is the development version, everything with
more components is not. Thus, we only need to check for a single dot.
2025-10-25 10:21:51 +02:00
renovate[bot]andWolfgang Walther e913efb8cb chore(deps): update all dependencies 2025-10-25 08:12:30 +00:00
19 changed files with 137 additions and 50 deletions
@@ -112,7 +112,7 @@ runs:
echo "artifacts=${artifacts}" >> "$GITHUB_OUTPUT"
- name: Save artifact to GitHub Actions
if: steps.find-task.outputs.task_found
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.upload }}
path: ${{ steps.download.outputs.artifacts }}
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
# This is required for backport action to cherry-pick the PR
- name: Fetch PR ref
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493
uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e
with:
ref: ${{ github.event.pull_request.head.sha }}
token: ${{ steps.app-token.outputs.token }}
+3 -3
View File
@@ -42,7 +42,7 @@ jobs:
- name: Build static executable
run: nix-build -A postgrestStatic -A postgrestStatic.tests
- name: Save built executable as artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: postgrest-linux-static-x86-64
path: result/bin/postgrest
@@ -51,7 +51,7 @@ jobs:
- name: Build Docker image
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
- name: Save built Docker image as artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: postgrest-docker-x86-64
path: postgrest-docker.tar.gz
@@ -155,7 +155,7 @@ jobs:
- name: Strip Executable
run: strip result/postgrest*
- name: Save built executable as artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ matrix.artifact }}
path: |
+1 -1
View File
@@ -57,7 +57,7 @@ jobs:
run: |
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
if [[ "$cabal_version" == *.*.*.* ]]; then
if [[ "$cabal_version" == *.* ]]; then
git fetch --tags
if [ -z "$(git tag --list "v$cabal_version")" ]; then
+5 -5
View File
@@ -49,7 +49,7 @@ jobs:
echo "Relevant extract from CHANGELOG.md:"
cat CHANGES.md
- name: Save CHANGES.md as artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: release-changes
path: CHANGES.md
@@ -66,7 +66,7 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Download all artifacts
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
path: artifacts
- name: Create release bundle with archives for all builds
@@ -94,7 +94,7 @@ jobs:
artifacts/postgrest-windows-x86-64/postgrest.exe
- name: Save release bundle
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: release-bundle
path: release-bundle
@@ -140,11 +140,11 @@ jobs:
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Download x86-64 Docker image
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: postgrest-docker-x86-64
- name: Download aarch64 binary
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
with:
name: postgrest-ubuntu-aarch64
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
+8 -2
View File
@@ -1,10 +1,16 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
All notable changes to this project will be documented in this file. From version `14.0` onwards PostgREST follows a `MAJOR.PATCH` two-part versioning. Only even-numbered MAJOR versions will be released, reserving odd-numbered MAJOR versions for development.
## Unreleased
## [14.1] - 2025-11-05
## Fixed
- Fix `db-pre-config` function failing when function names are pg reserved words by @taimoorzaeem in #4380
- Fix `server-host=!6` incorrectly binds to IPv4 address by @taimoorzaeem in #3202
## [14.0] - 2025-10-24
### Added
+1 -1
View File
@@ -1 +1 @@
index-state: hackage.haskell.org 2025-10-13T04:53:27Z
index-state: hackage.haskell.org 2025-10-29T04:02:18Z
+10
View File
@@ -60,6 +60,16 @@ let
}
{ };
# TODO: Remove once available in nixpkgs haskellPackages
streaming-commons =
prev.callHackageDirect
{
pkg = "streaming-commons";
ver = "0.2.3.1";
sha256 = "sha256-Gl2eaJcWe1sxmcE/octWlH9uSnERguf+5H66K4fV87s=";
}
{ };
# Downgrade hasql and related packages while we are still on GHC 9.4 for the static build.
hasql = lib.dontCheck (lib.doJailbreak prev.hasql_1_6_4_4);
hasql-dynamic-statements = lib.dontCheck prev.hasql-dynamic-statements_0_3_1_5;
+2 -2
View File
@@ -1,5 +1,5 @@
name: postgrest
version: 14.0
version: 14.1
synopsis: REST API for any Postgres database
description: Reads the schema of a PostgreSQL database and creates RESTful routes
for tables, views, and functions, supporting all HTTP methods that security
@@ -139,7 +139,7 @@ library
, regex-tdfa >= 1.2.2 && < 1.4
, retry >= 0.7.4 && < 0.10
, scientific >= 0.3.4 && < 0.4
, streaming-commons >= 0.1.1 && < 0.3
, streaming-commons >= 0.2.3.1 && < 0.3
, swagger2 >= 2.4 && < 2.9
, text >= 1.2.2 && < 2.2
, time >= 1.6 && < 1.13
+2 -2
View File
@@ -69,7 +69,7 @@ import PostgREST.Config.PgVersion (PgVersion (..),
import PostgREST.SchemaCache (SchemaCache (..),
querySchemaCache,
showSummary)
import PostgREST.SchemaCache.Identifiers (dumpQi)
import PostgREST.SchemaCache.Identifiers (quoteQi)
import PostgREST.Unix (createAndBindDomainSocket)
import Data.Streaming.Network (bindPortTCP, bindRandomPortTCP)
@@ -441,7 +441,7 @@ readInDbConfig startingUp appState@AppState{stateObserver=observer} = do
pgVer <- getPgVersion appState
dbSettings <-
if configDbConfig conf then do
qDbSettings <- usePool appState (queryDbSettings (dumpQi <$> configDbPreConfig conf) (configDbPreparedStatements conf))
qDbSettings <- usePool appState (queryDbSettings (quoteQi <$> configDbPreConfig conf) (configDbPreparedStatements conf))
case qDbSettings of
Left e -> do
observer $ ConfigReadErrorObs e
+19 -24
View File
@@ -6,13 +6,22 @@ Module : PostgREST.Query.SqlFragment
Description : Helper functions for PostgREST.QueryBuilder.
-}
module PostgREST.Query.SqlFragment
( noLocationF
, handlerF
( accessibleFuncs
, accessibleTables
, addConfigPgrstInserted
, countF
, groupF
, currentSettingF
, escapeIdent
, escapeIdentList
, explainF
, fromJsonBodyF
, fromQi
, groupF
, handlerF
, intercalateSnippet
, limitOffsetF
, locationF
, noLocationF
, orderF
, pgFmtColumn
, pgFmtFilter
@@ -21,28 +30,19 @@ module PostgREST.Query.SqlFragment
, pgFmtLogicTree
, pgFmtOrderTerm
, pgFmtSelectItem
, pgFmtSpreadSelectItem
, pgFmtSpreadJoinSelectItem
, fromJsonBodyF
, pgFmtSpreadSelectItem
, responseHeadersF
, responseStatusF
, addConfigPgrstInserted
, currentSettingF
, returningF
, schemaDescription
, setConfigWithConstantName
, setConfigWithConstantNameJSON
, setConfigWithDynamicName
, singleParameter
, sourceCTE
, sourceCTEName
, unknownEncoder
, intercalateSnippet
, explainF
, setConfigWithConstantName
, setConfigWithDynamicName
, setConfigWithConstantNameJSON
, escapeIdent
, escapeIdentList
, schemaDescription
, accessibleTables
, accessibleFuncs
) where
import qualified Data.Aeson as JSON
@@ -90,7 +90,8 @@ import PostgREST.RangeQuery (NonnegRange, allRange,
rangeLimit, rangeOffset)
import PostgREST.SchemaCache.Identifiers (FieldName,
QualifiedIdentifier (..),
RelIdentifier (..))
RelIdentifier (..),
escapeIdent, trimNullChars)
import PostgREST.SchemaCache.Routine (MediaHandler (..),
Routine (..),
funcReturnsScalar,
@@ -163,9 +164,6 @@ pgBuildArrayLiteral vals =
pgFmtIdent :: Text -> SQL.Snippet
pgFmtIdent x = SQL.sql . encodeUtf8 $ escapeIdent x
escapeIdent :: Text -> Text
escapeIdent x = "\"" <> T.replace "\"" "\"\"" (trimNullChars x) <> "\""
-- Only use it if the input comes from the database itself, like on `jsonb_build_object('column_from_a_table', val)..`
pgFmtLit :: Text -> Text
pgFmtLit x =
@@ -176,9 +174,6 @@ pgFmtLit x =
then "E" <> slashed
else slashed
trimNullChars :: Text -> Text
trimNullChars = T.takeWhile (/= '\x0')
-- |
-- Format a list of identifiers and separate them by commas.
--
+2 -2
View File
@@ -42,11 +42,11 @@ import NeatInterpolation (trimming)
import PostgREST.Config (AppConfig (..))
import PostgREST.Config.Database (TimezoneNames,
toIsolationLevel)
import PostgREST.Query.SqlFragment (escapeIdent)
import PostgREST.SchemaCache.Identifiers (FieldName,
QualifiedIdentifier (..),
RelIdentifier (..),
Schema, isAnyElement)
Schema, escapeIdent,
isAnyElement)
import PostgREST.SchemaCache.Relationship (Cardinality (..),
Junction (..),
Relationship (..),
+16 -3
View File
@@ -2,14 +2,17 @@
{-# LANGUAGE DeriveGeneric #-}
module PostgREST.SchemaCache.Identifiers
( QualifiedIdentifier(..)
( FieldName
, QualifiedIdentifier(..)
, RelIdentifier(..)
, isAnyElement
, Schema
, TableName
, FieldName
, dumpQi
, escapeIdent
, isAnyElement
, quoteQi
, toQi
, trimNullChars
) where
import qualified Data.Aeson as JSON
@@ -39,6 +42,10 @@ dumpQi :: QualifiedIdentifier -> Text
dumpQi (QualifiedIdentifier s i) =
(if T.null s then mempty else s <> ".") <> i
quoteQi :: QualifiedIdentifier -> Text
quoteQi (QualifiedIdentifier s i) =
(if T.null s then mempty else escapeIdent s <> ".") <> escapeIdent i
-- TODO: Handle a case where the QI comes like this: "my.fav.schema"."my.identifier"
-- Right now it only handles the schema.identifier case
toQi :: Text -> QualifiedIdentifier
@@ -46,6 +53,12 @@ toQi txt = case T.drop 1 <$> T.breakOn "." txt of
(i, "") -> QualifiedIdentifier mempty i
(s, i) -> QualifiedIdentifier s i
escapeIdent :: Text -> Text
escapeIdent x = "\"" <> T.replace "\"" "\"\"" (trimNullChars x) <> "\""
trimNullChars :: Text -> Text
trimNullChars = T.takeWhile (/= '\x0')
type Schema = Text
type TableName = Text
type FieldName = Text
+1
View File
@@ -14,3 +14,4 @@ extra-deps:
- hasql-pool-1.0.1
- jose-jwt-0.10.0
- postgresql-libpq-0.10.1.0
- streaming-commons-0.2.3.1
+7
View File
@@ -39,6 +39,13 @@ packages:
size: 1096
original:
hackage: postgresql-libpq-0.10.1.0
- completed:
hackage: streaming-commons-0.2.3.1@sha256:ed7999fea9e912b1211ea93d7e20a7998bf4753166370c94048885650f303bf0,4841
pantry-tree:
sha256: f08e83fb00fd45865fa8cfdef5ecef7161d5135257ee98050bfbe4b807ad65f8
size: 2374
original:
hackage: streaming-commons-0.2.3.1
snapshots:
- completed:
sha256: 238fa745b64f91184f9aa518fe04bdde6552533d169b0da5256670df83a0f1a9
@@ -461,6 +461,23 @@
pdSchema: public
pdVolatility: Volatile
- - qiName: 'true'
qiSchema: public
- - pdDescription: null
pdFuncSettings: []
pdHasVariadic: false
pdName: 'true'
pdParams: []
pdReturnType:
contents:
contents:
qiName: bool
qiSchema: pg_catalog
tag: Scalar
tag: Single
pdSchema: public
pdVolatility: Volatile
- - qiName: create_function
qiSchema: public
- - pdDescription: null
+4
View File
@@ -256,3 +256,7 @@ select * from projects;
create or replace view infinite_recursion as
select * from infinite_recursion;
create or replace function "true"() returns boolean as $_$
select true;
$_$ language sql;
+11 -3
View File
@@ -94,11 +94,19 @@ def run(
"Run PostgREST and yield an endpoint that is ready for connections."
with tempfile.TemporaryDirectory() as tmpdir:
# with python requests, "localhost" doesn't automatically resolves
# to [::1], hence we use this explicitly when host is ipv6 special
# address
ipv6_special_addresses = ["*6", "!6"]
localhost = "[::1]" if host in ipv6_special_addresses else "localhost"
if port:
env["PGRST_SERVER_PORT"] = str(port)
env["PGRST_SERVER_HOST"] = host or "localhost"
env["PGRST_SERVER_HOST"] = host or localhost
# When constructing IPv6 address, host address should be bracketed like [host]
apihost = f"[{host}]" if host and is_ipv6(host) else "localhost"
apihost = f"[{host}]" if host and is_ipv6(host) else localhost
baseurl = f"http://{apihost}:{port}"
else:
socketfile = pathlib.Path(tmpdir) / "postgrest.sock"
@@ -107,7 +115,7 @@ def run(
adminport = freeport(port)
env["PGRST_ADMIN_SERVER_PORT"] = str(adminport)
adminhost = f"[{host}]" if host and is_ipv6(host) else "localhost"
adminhost = f"[{host}]" if host and is_ipv6(host) else localhost
adminurl = f"http://{adminhost}:{adminport}"
command = [POSTGREST_BIN]
+26
View File
@@ -2046,3 +2046,29 @@ def test_log_listener_connection_errors(defaultenv):
in line
for line in output
)
def test_db_pre_config_with_pg_reserved_words(defaultenv):
"The db-pre-config should not fail unexpectedly when function name is a postgres reserved word"
env = {
**defaultenv,
"PGRST_DB_PRE_CONFIG": "true", # call true function
}
with run(env=env) as postgrest:
response = postgrest.session.post("/rpc/true")
assert response.status_code == 200
env = {
**defaultenv,
"PGRST_DB_PRE_CONFIG": "select", # no "select" function in our fixtures, fail gracefully at startup
}
with run(env=env, no_startup_stdout=False, wait_for_readiness=False) as postgrest:
output = postgrest.read_stdout(nlines=8)
assert any(
'Failed to query database settings for the config parameters.{"code":"42883","details":null,"hint":"No function matches the given name and argument types. You might need to add explicit type casts.","message":"function select() does not exist"}'
in line
for line in output
)