Compare commits
+2
-2
@@ -1,5 +1,5 @@
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-2
|
||||
image_family: freebsd-14-3
|
||||
|
||||
build_task:
|
||||
# Don't change this name without adjusting .github/workflows/build.yaml
|
||||
@@ -35,7 +35,7 @@ build_task:
|
||||
- find main src -type f -iname '*.hs' -exec md5sum "{}" +
|
||||
|
||||
build_script: |
|
||||
stack build -j 1 --local-bin-path . --copy-bins --stack-yaml stack-21.7.yaml
|
||||
stack build -j 1 --local-bin-path . --copy-bins
|
||||
strip postgrest
|
||||
|
||||
bin_artifacts:
|
||||
|
||||
@@ -19,14 +19,14 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
if: ${{ startsWith(github.ref, 'refs/heads/') || (inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
key: ${{ runner.os }}-${{ inputs.prefix }}-${{ inputs.suffix }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ inputs.prefix }}-
|
||||
- uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
- uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
if: ${{ !startsWith(github.ref, 'refs/heads/') && !(inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
|
||||
@@ -11,7 +11,7 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: nixbuild/nix-quick-install-action@5bb6a3b3abe66fd09bbf250dce8ada94f856a703 # v30
|
||||
- uses: nixbuild/nix-quick-install-action@63ca48f939ee3b8d835f4126562537df0fee5b91 # v32
|
||||
with:
|
||||
nix_conf: |-
|
||||
always-allow-substitutes = true
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
name: Nix - Linux x86-64 static
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
name: Nix - MacOS
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -126,11 +126,11 @@ jobs:
|
||||
name: Stack - ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: haskell-actions/setup@64445b6b5dd545faf5f8e2acee8253eb5c2b29aa # v2.7.11
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: haskell-actions/setup@7909071ceec0344debcc968c6c7a96a52e8dd0d7 # v2.8.1
|
||||
with:
|
||||
# This must match the version in stack.yaml's resolver
|
||||
ghc-version: 9.6.6
|
||||
ghc-version: 9.6.7
|
||||
enable-stack: true
|
||||
stack-no-global: true
|
||||
stack-setup-ghc: true
|
||||
@@ -168,7 +168,7 @@ jobs:
|
||||
name: Stack - FreeBSD from CirrusCI
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: ./.github/actions/artifact-from-cirrus
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
@@ -180,13 +180,13 @@ jobs:
|
||||
cabal:
|
||||
strategy:
|
||||
matrix:
|
||||
ghc: ['9.6.6', '9.8.2']
|
||||
ghc: ['9.6.7', '9.8.4']
|
||||
fail-fast: false
|
||||
name: Cabal - Linux x86-64 - GHC ${{ matrix.ghc }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: haskell-actions/setup@64445b6b5dd545faf5f8e2acee8253eb5c2b29aa # v2.7.11
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: haskell-actions/setup@7909071ceec0344debcc968c6c7a96a52e8dd0d7 # v2.8.1
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
- name: Cache .cabal
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
name: Lint & Style
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
- test
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
|
||||
- name: Tag latest commit
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
name: Spellcheck
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
|
||||
@@ -8,7 +8,7 @@ jobs:
|
||||
linkcheck:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Check the version to be released
|
||||
run: |
|
||||
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||
@@ -64,9 +64,9 @@ jobs:
|
||||
needs:
|
||||
- prepare
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
path: artifacts
|
||||
- name: Create release bundle with archives for all builds
|
||||
@@ -138,17 +138,17 @@ jobs:
|
||||
env:
|
||||
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Download x86-64 Docker image
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
name: postgrest-docker-x86-64
|
||||
- name: Download aarch64 binary
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
name: postgrest-ubuntu-aarch64
|
||||
- uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
- uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
with:
|
||||
username: ${{ vars.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
@@ -194,7 +194,7 @@ jobs:
|
||||
vars.DOCKER_REPO && vars.DOCKER_USER &&
|
||||
github.ref == 'refs/tags/devel'
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4.0.2
|
||||
with:
|
||||
username: ${{ vars.DOCKER_USER }}
|
||||
|
||||
+14
-11
@@ -39,7 +39,7 @@ jobs:
|
||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
|
||||
run: postgrest-coverage
|
||||
- name: Upload coverage to codecov
|
||||
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
|
||||
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
|
||||
with:
|
||||
files: ./coverage/codecov.json
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
name: Memory
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
name: Loadtest
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Nix Environment
|
||||
@@ -125,13 +125,16 @@ jobs:
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: loadtest.loadtestAgainst.bin loadtest.report.bin
|
||||
- uses: WyriHaximus/github-action-get-previous-tag@04e8485ecb6487243907e330d522ff60f02283ce # v1.4.0
|
||||
id: get-latest-tag
|
||||
with:
|
||||
prefix: v
|
||||
- name: Run loadtest
|
||||
env:
|
||||
TARGET_BRANCH: ${{ github.base_ref || github.ref_name }}
|
||||
run: |
|
||||
postgrest-loadtest-against -k ${{ matrix.kind }} main ${{ steps.get-latest-tag.outputs.tag }}
|
||||
if [ "$TARGET_BRANCH" = "main" ]; then
|
||||
latest_tag=$(git tag --sort=-creatordate --list "v*" | head -n1)
|
||||
else
|
||||
latest_tag=$(git tag --merged HEAD --sort=-creatordate "v*" | head -n1)
|
||||
fi
|
||||
postgrest-loadtest-against -k ${{ matrix.kind }} "$TARGET_BRANCH" "$latest_tag"
|
||||
postgrest-loadtest-report >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
flake:
|
||||
@@ -146,7 +149,7 @@ jobs:
|
||||
name: Flake Check
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Nix Environment
|
||||
|
||||
+9
-9
@@ -18,16 +18,16 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/code-build.png">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
@@ -35,11 +35,6 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
<img width="296px" src="static/supabase.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -93,6 +88,11 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
<img width="222px" src="static/oblivious.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="222px" src="static/code-build.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -5,6 +5,22 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [13.0.5] - 2025-08-24
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix OpenAPI broken docs link by @taimoorzaeem in #4048
|
||||
- Fix OpenAPI specification incorrectly exposing GET methods for volatile functions by @joelonsql in #4174
|
||||
- Fix empty spread embeddings return unexpected SQL error by @taimoorzaeem in #3887
|
||||
- Fix `/metrics` endpoint not responding with `Content-Type` header by @taimoorzaeem in #4271
|
||||
|
||||
## [13.0.4] - 2025-06-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix regression that makes full-text search not work on domain types based on `tsvector` by @laurenceisla in #4135
|
||||
- Fix `jwt-aud` config not failing when set to an invalid URI by @taimoorzaeem in #4132
|
||||
|
||||
## [13.0.3] - 2025-06-16
|
||||
|
||||
### Fixed
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
# The x86-64 is a single-static-binary image built via Nix, see:
|
||||
# nix/tools/docker/README.md
|
||||
|
||||
FROM ubuntu:noble@sha256:6015f66923d7afbc53558d7ccffd325d43b4e249f41a6e93eef074c9505d2233 AS postgrest
|
||||
FROM ubuntu:noble@sha256:7c06e91f61fa88c08cc74f7e1b7c69ae24910d745357e0dfe1d2c0322aaf20f9 AS postgrest
|
||||
|
||||
RUN apt-get update -y \
|
||||
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
|
||||
|
||||
@@ -27,16 +27,16 @@ API than you are likely to write from scratch.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/code-build.png">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
@@ -44,11 +44,6 @@ API than you are likely to write from scratch.
|
||||
<img width="296px" src="static/supabase.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1 +1 @@
|
||||
index-state: hackage.haskell.org 2025-02-01T14:59:33Z
|
||||
index-state: hackage.haskell.org 2025-07-08T15:50:10Z
|
||||
|
||||
@@ -24,16 +24,16 @@ write from scratch.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/code-build.png">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/tembo.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
@@ -41,11 +41,6 @@ write from scratch.
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
+16
-26
@@ -41,28 +41,28 @@ Sponsors
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/neon-dark.jpg
|
||||
:target: https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:target: https://neon.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/neon.jpg
|
||||
:target: https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/code-build-dark.png
|
||||
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/code-build.png
|
||||
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
|
|
||||
:target: https://neon.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. image:: ../static/tembo.png
|
||||
:target: https://www.tembo.io/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
|
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/supabase-dark.png
|
||||
@@ -73,21 +73,11 @@ Sponsors
|
||||
.. image:: ../static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. The static/empty.png(created with `convert -size 320x95 xc:#fcfcfc empty.png`) is an ugly workaround
|
||||
to create space and center the logos. It's not easy to layout with restructuredText.
|
||||
|
||||
.. .. image:: _static/empty.png
|
||||
:target: #sponsors
|
||||
.. image:: _static/empty.png
|
||||
:target: #sponsors
|
||||
|
||||
|
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ unikernel
|
||||
unix
|
||||
updatable
|
||||
unfulfillable
|
||||
unselected
|
||||
Untyped
|
||||
UPSERT
|
||||
Upsert
|
||||
|
||||
@@ -12,7 +12,7 @@ Health Check
|
||||
|
||||
You can enable a health check to verify if PostgREST is available for client requests. Also to check the status of its internal state.
|
||||
|
||||
Two endpoints ``live`` and ``ready`` will then be available.
|
||||
Two endpoints ``live`` and ``ready`` will then be available. Both these endpoints reply with a status code and empty response body.
|
||||
|
||||
.. important::
|
||||
|
||||
|
||||
@@ -294,6 +294,23 @@ Let's get its :ref:`explain_plan` when calling it with filters applied:
|
||||
|
||||
Notice there's no "Function Scan" node in the plan, which tells us it has been inlined.
|
||||
|
||||
Horizontal Filtering
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Table-valued functions support horizontal filtering on selected and unselected columns.
|
||||
|
||||
For example, the following RPC with filter on unselected column returns:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/rpc/getallprojects?select=id,client_id&name=like.OSX"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{ "id": 4, "client_id": 2 }
|
||||
]
|
||||
|
||||
.. _scalar_functions:
|
||||
|
||||
Scalar functions
|
||||
|
||||
@@ -128,6 +128,11 @@ The ``metrics`` endpoint on the :ref:`admin_server` endpoint provides metrics in
|
||||
|
||||
curl "http://localhost:3001/metrics"
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
# HELP pgrst_schema_cache_query_time_seconds The query time in seconds of the last schema cache load
|
||||
# TYPE pgrst_schema_cache_query_time_seconds gauge
|
||||
pgrst_schema_cache_query_time_seconds 1.5937927e-2
|
||||
@@ -246,7 +251,7 @@ See :ref:`proxy-status_header`.
|
||||
Server-Timing Header
|
||||
--------------------
|
||||
|
||||
You can enable the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing>`_ header by setting :ref:`server-timing-enabled` on.
|
||||
You can enable the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing>`_ header by setting :ref:`server-timing-enabled` on.
|
||||
This header communicates metrics of the different phases in the request-response cycle.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
+14
-17
@@ -1,5 +1,5 @@
|
||||
name: postgrest
|
||||
version: 13.0.3
|
||||
version: 13.0.5
|
||||
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
|
||||
@@ -16,15 +16,13 @@ extra-source-files: CHANGELOG.md
|
||||
cabal-version: >= 1.10
|
||||
|
||||
tested-with:
|
||||
-- stack on FreeBSD
|
||||
GHC == 9.4.5
|
||||
-- nix, cabal on Ubuntu (arm)
|
||||
, GHC == 9.4.8
|
||||
-- nix
|
||||
GHC == 9.4.8
|
||||
-- cabal on Ubuntu
|
||||
-- stack on MacOS, Ubuntu, Windows
|
||||
, GHC == 9.6.6
|
||||
-- stack on FreeBSD, MacOS, Ubuntu, Windows
|
||||
, GHC == 9.6.7
|
||||
-- cabal on Ubuntu
|
||||
, GHC == 9.8.2
|
||||
, GHC == 9.8.4
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
@@ -91,12 +89,11 @@ library
|
||||
PostgREST.Response.GucHeader
|
||||
PostgREST.Response.Performance
|
||||
PostgREST.Version
|
||||
other-modules: Paths_postgrest
|
||||
build-depends: base >= 4.9 && < 4.20
|
||||
, HTTP >= 4000.3.7 && < 4000.5
|
||||
, Ranged-sets >= 0.3 && < 0.5
|
||||
, aeson >= 2.0.3 && < 2.3
|
||||
, auto-update >= 0.1.4 && < 0.2
|
||||
, auto-update >= 0.1.4 && < 0.3
|
||||
, base64-bytestring >= 1 && < 1.3
|
||||
, bytestring >= 0.10.8 && < 0.13
|
||||
, cache >= 0.1.3 && < 0.2.0
|
||||
@@ -105,7 +102,7 @@ library
|
||||
, clock >= 0.8.3 && < 0.9.0
|
||||
, configurator-pg >= 0.2 && < 0.3
|
||||
, containers >= 0.5.7 && < 0.7
|
||||
, cookie >= 0.4.2 && < 0.5
|
||||
, cookie >= 0.4.2 && < 0.6
|
||||
, directory >= 1.2.6 && < 1.4
|
||||
, either >= 4.4.1 && < 5.1
|
||||
, extra >= 1.7.0 && < 2.0
|
||||
@@ -114,17 +111,17 @@ library
|
||||
, hasql-dynamic-statements >= 0.3.1 && < 0.4
|
||||
, hasql-notifications >= 0.2.2.2 && < 0.2.3
|
||||
, hasql-pool >= 1.0.1 && < 1.1
|
||||
, hasql-transaction >= 1.0.1 && < 1.1
|
||||
, hasql-transaction >= 1.0.1 && < 1.2
|
||||
, heredoc >= 0.2 && < 0.3
|
||||
, http-types >= 0.12.2 && < 0.13
|
||||
, insert-ordered-containers >= 0.2.2 && < 0.3
|
||||
, iproute >= 1.7.0 && < 1.8
|
||||
, jose-jwt >= 0.9.6 && < 0.11
|
||||
, lens >= 4.14 && < 5.3
|
||||
, lens >= 4.14 && < 5.4
|
||||
, lens-aeson >= 1.0.1 && < 1.3
|
||||
, mtl >= 2.2.2 && < 2.4
|
||||
, neat-interpolation >= 0.5 && < 0.6
|
||||
, network >= 2.6 && < 3.2
|
||||
, network >= 2.6 && < 3.3
|
||||
, network-uri >= 2.6.1 && < 2.8
|
||||
, optparse-applicative >= 0.13 && < 0.19
|
||||
, parsec >= 3.1.11 && < 3.2
|
||||
@@ -152,7 +149,7 @@ library
|
||||
-- for unix sockets; this is tested in test/io/test_io.py. See
|
||||
-- https://github.com/kazu-yamamoto/logger/commit/3a71ca70afdbb93d4ecf0083eeba1fbbbcab3fc3
|
||||
, wai-logger >= 2.4.0
|
||||
, warp >= 3.3.19 && < 3.4
|
||||
, warp >= 3.3.19 && < 3.5
|
||||
-- -fno-spec-constr may help keep compile time memory use in check,
|
||||
-- see https://gitlab.haskell.org/ghc/ghc/issues/16017#note_219304
|
||||
-- -optP-Wno-nonportable-include-path
|
||||
@@ -261,14 +258,14 @@ test-suite spec
|
||||
, case-insensitive >= 1.2 && < 1.3
|
||||
, containers >= 0.5.7 && < 0.7
|
||||
, hasql-pool >= 1.0.1 && < 1.1
|
||||
, hasql-transaction >= 1.0.1 && < 1.1
|
||||
, hasql-transaction >= 1.0.1 && < 1.2
|
||||
, heredoc >= 0.2 && < 0.3
|
||||
, hspec >= 2.3 && < 2.12
|
||||
, hspec-wai >= 0.10 && < 0.12
|
||||
, hspec-wai-json >= 0.10 && < 0.12
|
||||
, http-types >= 0.12.3 && < 0.13
|
||||
, jose-jwt >= 0.9.6 && < 0.11
|
||||
, lens >= 4.14 && < 5.3
|
||||
, lens >= 4.14 && < 5.4
|
||||
, lens-aeson >= 1.0.1 && < 1.3
|
||||
, monad-control >= 1.0.1 && < 1.1
|
||||
, postgrest
|
||||
|
||||
@@ -16,6 +16,7 @@ import Network.Socket.ByteString
|
||||
|
||||
import PostgREST.AppState (AppState)
|
||||
import PostgREST.Config (AppConfig (..))
|
||||
import PostgREST.MediaType (MediaType (..), toContentType)
|
||||
import PostgREST.Metrics (metricsToText)
|
||||
import PostgREST.Network (resolveHost)
|
||||
import PostgREST.Observation (Observation (..))
|
||||
@@ -58,7 +59,7 @@ admin appState req respond = do
|
||||
respond $ Wai.responseLBS HTTP.status200 [] (maybe mempty JSON.encode sCache)
|
||||
["metrics"] -> do
|
||||
mets <- metricsToText
|
||||
respond $ Wai.responseLBS HTTP.status200 [] mets
|
||||
respond $ Wai.responseLBS HTTP.status200 [toContentType MTTextPlain] mets -- Content-Type is required for prometheus compliance
|
||||
_ ->
|
||||
respond $ Wai.responseLBS HTTP.status404 [] mempty
|
||||
|
||||
|
||||
+16
-2
@@ -48,7 +48,7 @@ import Data.List.NonEmpty (fromList, toList)
|
||||
import Data.Maybe (fromJust)
|
||||
import Data.Scientific (floatingOrInteger)
|
||||
import Jose.Jwk (Jwk, JwkSet)
|
||||
import Network.URI (escapeURIString,
|
||||
import Network.URI (escapeURIString, isURI,
|
||||
isUnescapedInURIComponent)
|
||||
import Numeric (readOct, showOct)
|
||||
import System.Environment (getEnvironment)
|
||||
@@ -281,7 +281,7 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
|
||||
<*> (fromMaybe "postgresql://" <$> optString "db-uri")
|
||||
<*> pure optPath
|
||||
<*> pure Nothing
|
||||
<*> optString "jwt-aud"
|
||||
<*> optStringOrURI "jwt-aud"
|
||||
<*> parseRoleClaimKey "jwt-role-claim-key" "role-claim-key"
|
||||
<*> (fmap encodeUtf8 <$> optString "jwt-secret")
|
||||
<*> (fromMaybe False <$> optWithAlias
|
||||
@@ -407,6 +407,20 @@ parser optPath env dbSettings roleSettings roleIsolationLvl =
|
||||
optStringEmptyable :: C.Key -> C.Parser C.Config (Maybe Text)
|
||||
optStringEmptyable k = overrideFromDbOrEnvironment C.optional k coerceText
|
||||
|
||||
optStringOrURI :: C.Key -> C.Parser C.Config (Maybe Text)
|
||||
optStringOrURI k = do
|
||||
stringOrURI <- mfilter (/= "") <$> overrideFromDbOrEnvironment C.optional k coerceText
|
||||
-- If the string contains ':' then it should
|
||||
-- be a valid URI according to RFC 3986
|
||||
case stringOrURI of
|
||||
Just s -> if T.isInfixOf ":" s then validateURI s else return (Just s)
|
||||
Nothing -> return Nothing
|
||||
where
|
||||
validateURI :: Text -> C.Parser C.Config (Maybe Text)
|
||||
validateURI s = if isURI (T.unpack s)
|
||||
then return $ Just s
|
||||
else fail "jwt-aud should be a string or a valid URI"
|
||||
|
||||
optInt :: (Read i, Integral i) => C.Key -> C.Parser C.Config (Maybe i)
|
||||
optInt k = join <$> overrideFromDbOrEnvironment C.optional k coerceInt
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ data ResolverContext = ResolverContext
|
||||
}
|
||||
|
||||
resolveColumnField :: Column -> Maybe ToTsVector -> CoercibleField
|
||||
resolveColumnField col toTsV = CoercibleField (colName col) mempty False toTsV (colNominalType col) Nothing (colDefault col) False
|
||||
resolveColumnField col toTsV = CoercibleField (colName col) mempty False toTsV (colNominalType col) (colType col) Nothing (colDefault col) False
|
||||
|
||||
resolveTableFieldName :: Table -> FieldName -> Maybe ToTsVector -> CoercibleField
|
||||
resolveTableFieldName table fieldName toTsV=
|
||||
@@ -291,12 +291,12 @@ resolveTypeOrUnknown ResolverContext{..} (fn, jp) toTsV =
|
||||
case res of
|
||||
-- types that are already json/jsonb don't need to be converted with `to_jsonb` for using arrow operators `data->attr`
|
||||
-- this prevents indexes not applying https://github.com/PostgREST/postgrest/issues/2594
|
||||
cf@CoercibleField{cfIRType="json"} -> cf{cfJsonPath=jp, cfToJson=False}
|
||||
cf@CoercibleField{cfIRType="jsonb"} -> cf{cfJsonPath=jp, cfToJson=False}
|
||||
cf@CoercibleField{cfIRType="json"} -> cf{cfJsonPath=jp, cfToJson=False}
|
||||
cf@CoercibleField{cfIRType="jsonb"} -> cf{cfJsonPath=jp, cfToJson=False}
|
||||
-- Do not apply to_tsvector to tsvector types
|
||||
cf@CoercibleField{cfIRType="tsvector"} -> cf{cfJsonPath=jp, cfToJson=True, cfToTsVector=Nothing}
|
||||
cf@CoercibleField{cfBaseType="tsvector"} -> cf{cfJsonPath=jp, cfToJson=True, cfToTsVector=Nothing}
|
||||
-- other types will get converted `to_jsonb(col)->attr`, even unknown types
|
||||
cf -> cf{cfJsonPath=jp, cfToJson=True}
|
||||
cf -> cf{cfJsonPath=jp, cfToJson=True}
|
||||
where
|
||||
res = fromMaybe (unknownField fn jp) $ HM.lookup qi tables >>=
|
||||
Just . (\t -> resolveTableFieldName t fn toTsV)
|
||||
@@ -891,7 +891,7 @@ addRelatedOrders (Node rp@ReadPlan{order,from} forest) = do
|
||||
-- where_ = [
|
||||
-- CoercibleStmnt (
|
||||
-- CoercibleFilter {
|
||||
-- field = CoercibleField {cfName = "projects", cfJsonPath = [], cfToJson=False, cfToTsVector = Nothing, cfIRType = "", cfTransform = Nothing, cfDefault = Nothing, cfFullRow = False},
|
||||
-- field = CoercibleField {cfName = "projects", cfJsonPath = [], cfToJson=False, cfToTsVector = Nothing, cfIRType = "", cfBaseType = "", cfTransform = Nothing, cfDefault = Nothing, cfFullRow = False},
|
||||
-- opExpr = op
|
||||
-- }
|
||||
-- )
|
||||
@@ -907,7 +907,7 @@ addRelatedOrders (Node rp@ReadPlan{order,from} forest) = do
|
||||
-- Don't do anything to the filter if there's no embedding (a subtree) on projects. Assume it's a normal filter.
|
||||
--
|
||||
-- >>> ReadPlan.where_ . rootLabel <$> addNullEmbedFilters (readPlanTree nullOp [])
|
||||
-- Right [CoercibleStmnt (CoercibleFilter {field = CoercibleField {cfName = "projects", cfJsonPath = [], cfToJson = False, cfToTsVector = Nothing, cfIRType = "", cfTransform = Nothing, cfDefault = Nothing, cfFullRow = False}, opExpr = OpExpr True (Is IsNull)})]
|
||||
-- Right [CoercibleStmnt (CoercibleFilter {field = CoercibleField {cfName = "projects", cfJsonPath = [], cfToJson = False, cfToTsVector = Nothing, cfIRType = "", cfBaseType = "", cfTransform = Nothing, cfDefault = Nothing, cfFullRow = False}, opExpr = OpExpr True (Is IsNull)})]
|
||||
--
|
||||
-- If there's an embedding on projects, then change the filter to use the internal aggregate name (`clients_projects_1`) so the filter can succeed later.
|
||||
--
|
||||
@@ -926,7 +926,7 @@ addNullEmbedFilters (Node rp@ReadPlan{where_=curLogic} forest) = do
|
||||
newNullFilters rPlans = \case
|
||||
(CoercibleExpr b lOp trees) ->
|
||||
CoercibleExpr b lOp <$> (newNullFilters rPlans `traverse` trees)
|
||||
flt@(CoercibleStmnt (CoercibleFilter (CoercibleField fld [] _ _ _ _ _ _) opExpr)) ->
|
||||
flt@(CoercibleStmnt (CoercibleFilter CoercibleField{cfName=fld, cfJsonPath=[]} opExpr)) ->
|
||||
let foundRP = find (\ReadPlan{relName, relAlias} -> fld == fromMaybe relName relAlias) rPlans in
|
||||
case (foundRP, opExpr) of
|
||||
(Just ReadPlan{relAggAlias}, OpExpr b (Is IsNull)) -> Right $ CoercibleStmnt $ CoercibleFilterNullEmbed b relAggAlias
|
||||
|
||||
@@ -44,13 +44,14 @@ data CoercibleField = CoercibleField
|
||||
, cfToJson :: Bool
|
||||
, cfToTsVector :: Maybe ToTsVector -- ^ If the field should be converted using to_tsvector(<language>, <field>)
|
||||
, cfIRType :: Text -- ^ The native Postgres type of the field, the intermediate (IR) type before mapping.
|
||||
, cfBaseType :: Text -- ^ The base type of the field in case of domains, or just the type otherwise (without modifiers in case of pg_catalog types)
|
||||
, cfTransform :: Maybe TransformerProc -- ^ The optional mapping from irType -> targetType.
|
||||
, cfDefault :: Maybe Text
|
||||
, cfFullRow :: Bool -- ^ True if the field represents the whole selected row. Used in spread rels: instead of COUNT(*), it does a COUNT(<row>) in order to not mix with other spreaded resources.
|
||||
} deriving (Eq, Show)
|
||||
|
||||
unknownField :: FieldName -> JsonPath -> CoercibleField
|
||||
unknownField name path = CoercibleField name path False Nothing "" Nothing Nothing False
|
||||
unknownField name path = CoercibleField name path False Nothing "" "" Nothing Nothing False
|
||||
|
||||
-- | Like an API request LogicTree, but with coercible field information.
|
||||
data CoercibleLogicTree
|
||||
|
||||
@@ -70,21 +70,21 @@ readPlanToQuery node@(Node ReadPlan{select,from=mainQi,fromAlias,where_=logicFor
|
||||
|
||||
getJoinSelects :: ReadPlanTree -> [SQL.Snippet]
|
||||
getJoinSelects (Node ReadPlan{relSelect} _) =
|
||||
mapMaybe relSelectToSnippet relSelect
|
||||
join $ map relSelectToSnippet relSelect
|
||||
where
|
||||
relSelectToSnippet :: RelSelectField -> Maybe SQL.Snippet
|
||||
relSelectToSnippet :: RelSelectField -> [SQL.Snippet]
|
||||
relSelectToSnippet fld =
|
||||
let aggAlias = pgFmtIdent $ rsAggAlias fld
|
||||
in
|
||||
case fld of
|
||||
JsonEmbed{rsEmptyEmbed = True} ->
|
||||
Nothing
|
||||
[]
|
||||
JsonEmbed{rsSelName, rsEmbedMode = JsonObject} ->
|
||||
Just $ "row_to_json(" <> aggAlias <> ".*)::jsonb AS " <> pgFmtIdent rsSelName
|
||||
["row_to_json(" <> aggAlias <> ".*)::jsonb AS " <> pgFmtIdent rsSelName]
|
||||
JsonEmbed{rsSelName, rsEmbedMode = JsonArray} ->
|
||||
Just $ "COALESCE( " <> aggAlias <> "." <> aggAlias <> ", '[]') AS " <> pgFmtIdent rsSelName
|
||||
["COALESCE( " <> aggAlias <> "." <> aggAlias <> ", '[]') AS " <> pgFmtIdent rsSelName]
|
||||
Spread{rsSpreadSel, rsAggAlias} ->
|
||||
Just $ intercalateSnippet ", " (pgFmtSpreadSelectItem rsAggAlias <$> rsSpreadSel)
|
||||
pgFmtSpreadSelectItem rsAggAlias <$> rsSpreadSel
|
||||
|
||||
getJoins :: ReadPlanTree -> [SQL.Snippet]
|
||||
getJoins (Node _ []) = []
|
||||
@@ -182,7 +182,7 @@ callPlanToQuery (FunctionCall qi params arguments returnsScalar returnsSetOfScal
|
||||
KeyParams [] -> "FROM " <> callIt mempty
|
||||
KeyParams prms -> case arguments of
|
||||
DirectArgs args -> "FROM " <> callIt (fmtArgs prms args)
|
||||
JsonArgs json -> fromJsonBodyF json ((\p -> CoercibleField (ppName p) mempty False Nothing (ppTypeMaxLength p) Nothing Nothing False) <$> prms) False True False <> ", " <>
|
||||
JsonArgs json -> fromJsonBodyF json ((\p -> CoercibleField (ppName p) mempty False Nothing (ppTypeMaxLength p) mempty Nothing Nothing False) <$> prms) False True False <> ", " <>
|
||||
"LATERAL " <> callIt (fmtParams prms)
|
||||
|
||||
callIt :: SQL.Snippet -> SQL.Snippet
|
||||
|
||||
@@ -30,7 +30,8 @@ import PostgREST.SchemaCache.Identifiers (QualifiedIdentifier (..))
|
||||
import PostgREST.SchemaCache.Relationship (Cardinality (..),
|
||||
Relationship (..),
|
||||
RelationshipsMap)
|
||||
import PostgREST.SchemaCache.Routine (Routine (..),
|
||||
import PostgREST.SchemaCache.Routine (FuncVolatility (..),
|
||||
Routine (..),
|
||||
RoutineParam (..))
|
||||
import PostgREST.SchemaCache.Table (Column (..), Table (..),
|
||||
TablesMap,
|
||||
@@ -355,9 +356,9 @@ makeProcPathItem pd = ("/rpc/" ++ toS (pdName pd), pe)
|
||||
& parameters .~ makeProcGetParams (pdParams pd)
|
||||
postOp = procOp
|
||||
& parameters .~ makeProcPostParams pd
|
||||
pe = (mempty :: PathItem)
|
||||
& get ?~ getOp
|
||||
& post ?~ postOp
|
||||
pe = case pdVolatility pd of
|
||||
Volatile -> (mempty :: PathItem) & post ?~ postOp
|
||||
_ -> (mempty :: PathItem) & get ?~ getOp & post ?~ postOp
|
||||
|
||||
makeRootPathItem :: (FilePath, PathItem)
|
||||
makeRootPathItem = ("/", p)
|
||||
|
||||
@@ -26,7 +26,7 @@ prettyVersion =
|
||||
docsVersion :: Text
|
||||
docsVersion
|
||||
| isPreRelease = "latest"
|
||||
| otherwise = "v" <> (T.intercalate "." . map show . take 1 $ version)
|
||||
| otherwise = "v" <> T.intercalate "." (take 1 version)
|
||||
|
||||
|
||||
-- | Versions with two components (e.g., '1.1') are treated as pre-releases.
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# We're keeping this at 21.7 for freebsd right now, because:
|
||||
# Error: [S-9443]
|
||||
# No setup information found for ghc-9.6.4 on your platform. This probably means a GHC binary
|
||||
# distribution has not yet been added for OS key freebsd64-ino64. Supported versions:
|
||||
# ghc-8.4.3, ghc-8.4.4, ghc-8.6.2, ghc-8.6.3, ghc-8.6.4, ghc-8.6.5, ghc-8.8.1, ghc-8.8.2,
|
||||
# ghc-8.8.3, ghc-8.8.4, ghc-8.10.3, ghc-8.10.4, ghc-8.10.6, ghc-8.10.7, ghc-9.0.2, ghc-9.2.5,
|
||||
# ghc-9.2.6, ghc-9.2.7, ghc-9.2.8 and ghc-9.4.5.
|
||||
resolver: lts-21.7 # 2023-08-14, GHC 9.4.5
|
||||
|
||||
nix:
|
||||
packages:
|
||||
- pcre
|
||||
- pkgconfig
|
||||
- postgresql
|
||||
- zlib
|
||||
# disable pure by default so that the test environment can be passed
|
||||
pure: false
|
||||
|
||||
extra-deps:
|
||||
- configurator-pg-0.2.10
|
||||
- fuzzyset-0.2.4
|
||||
- hasql-notifications-0.2.2.2
|
||||
- hasql-pool-1.0.1
|
||||
- postgresql-libpq-0.10.1.0
|
||||
@@ -1,47 +0,0 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
hackage: configurator-pg-0.2.10@sha256:dbb9381c4f491b838214289c59d465eff99f6b569334a98ccf836548970e0f49,2786
|
||||
pantry-tree:
|
||||
sha256: a6dc0197c8c8515877ff579c088511edcf2d1090d9a73ac618d884101d6cbed8
|
||||
size: 2463
|
||||
original:
|
||||
hackage: configurator-pg-0.2.10
|
||||
- completed:
|
||||
hackage: fuzzyset-0.2.4@sha256:f1b6de8bf33277bf6255207541d65028f1f1ea93af5541b654c86b5674995485,1618
|
||||
pantry-tree:
|
||||
sha256: cee68e8d88f530e9e0588b81b260236936fe3318ef9a66e9f43f680b4cd5f76e
|
||||
size: 574
|
||||
original:
|
||||
hackage: fuzzyset-0.2.4
|
||||
- completed:
|
||||
hackage: hasql-notifications-0.2.2.2@sha256:d1d6bc0d3ee5e418fc12ea023b78739e0decba6c34e2b43bec55b89e18bd4412,2025
|
||||
pantry-tree:
|
||||
sha256: 83a9cbb179b1efd0b2acd6509583c7afcdbe63469ab033d8581d48d675a80b44
|
||||
size: 452
|
||||
original:
|
||||
hackage: hasql-notifications-0.2.2.2
|
||||
- completed:
|
||||
hackage: hasql-pool-1.0.1@sha256:3cfb4c7153a6c536ac7e126c17723e6d26ee03794954deed2d72bcc826d05a40,2302
|
||||
pantry-tree:
|
||||
sha256: d98e1269bdd60989b0eb0b84e1d5357eaa9f92821439d9f206663b7251ee95b2
|
||||
size: 799
|
||||
original:
|
||||
hackage: hasql-pool-1.0.1
|
||||
- completed:
|
||||
hackage: postgresql-libpq-0.10.1.0@sha256:6b580c9d5068e78eecc13e655b2885c8e79cdacfca513c5d1e5a6b9dc61d9758,3166
|
||||
pantry-tree:
|
||||
sha256: ae81e7628a8f3d1ef33ace71fa0845c073c003ca7f1150cc9d9ba1e55fc84236
|
||||
size: 1096
|
||||
original:
|
||||
hackage: postgresql-libpq-0.10.1.0
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: 23bb9bb355bfdb1635252e120a29b712f0d5e8a6c6a65c5ab5bd6692f46c438e
|
||||
size: 640457
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/21/7.yaml
|
||||
original: lts-21.7
|
||||
+3
-5
@@ -1,10 +1,9 @@
|
||||
resolver: lts-22.41 # 2024-11-10, GHC 9.6.6
|
||||
resolver: lts-22.44 # 2025-05-02, GHC 9.6.7
|
||||
|
||||
nix:
|
||||
packages:
|
||||
- pcre
|
||||
- pkgconfig
|
||||
- postgresql
|
||||
- libpq
|
||||
- pkg-config
|
||||
- zlib
|
||||
# disable pure by default so that the test environment can be passed
|
||||
pure: false
|
||||
@@ -14,4 +13,3 @@ extra-deps:
|
||||
- hasql-pool-1.0.1
|
||||
- jose-jwt-0.10.0
|
||||
- postgresql-libpq-0.10.1.0
|
||||
- hasql-notifications-0.2.2.2
|
||||
|
||||
+5
-12
@@ -1,7 +1,7 @@
|
||||
# This file was autogenerated by Stack.
|
||||
# You should not edit this file by hand.
|
||||
# For more information, please see the documentation at:
|
||||
# https://docs.haskellstack.org/en/stable/lock_files
|
||||
# https://docs.haskellstack.org/en/stable/topics/lock_files
|
||||
|
||||
packages:
|
||||
- completed:
|
||||
@@ -32,16 +32,9 @@ packages:
|
||||
size: 1096
|
||||
original:
|
||||
hackage: postgresql-libpq-0.10.1.0
|
||||
- completed:
|
||||
hackage: hasql-notifications-0.2.2.2@sha256:d1d6bc0d3ee5e418fc12ea023b78739e0decba6c34e2b43bec55b89e18bd4412,2025
|
||||
pantry-tree:
|
||||
sha256: 83a9cbb179b1efd0b2acd6509583c7afcdbe63469ab033d8581d48d675a80b44
|
||||
size: 452
|
||||
original:
|
||||
hackage: hasql-notifications-0.2.2.2
|
||||
snapshots:
|
||||
- completed:
|
||||
sha256: 1e32b51d9082fdf6f3bd92accc9dfffd4ddaf406404427fb10bf76d2bc03cbbb
|
||||
size: 720263
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/41.yaml
|
||||
original: lts-22.41
|
||||
sha256: 238fa745b64f91184f9aa518fe04bdde6552533d169b0da5256670df83a0f1a9
|
||||
size: 721141
|
||||
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/44.yaml
|
||||
original: lts-22.44
|
||||
|
||||
@@ -41,12 +41,11 @@ cli:
|
||||
use_defaultenv: true
|
||||
env:
|
||||
PGRST_SERVER_UNIX_SOCKET_MODE: '778'
|
||||
# TODO: Bug needs to be fixed
|
||||
# - name: invalid jwt-aud
|
||||
# expect: error
|
||||
# use_defaultenv: true
|
||||
# env:
|
||||
# PGRST_JWT_AUD: 'htp:/@@localhorst.invalid'
|
||||
- name: invalid jwt-aud
|
||||
expect: error
|
||||
use_defaultenv: true
|
||||
env:
|
||||
PGRST_JWT_AUD: 'http://%%localhorst.invalid'
|
||||
- name: invalid log-level
|
||||
expect: error
|
||||
use_defaultenv: true
|
||||
|
||||
@@ -278,3 +278,15 @@ def test_schema_cache_snapshot(baseenv, key, snapshot_yaml):
|
||||
Dumper=yaml.SafeDumper if key == "dbTimezones" else ExtraNewLinesDumper,
|
||||
)
|
||||
assert formatted == snapshot_yaml
|
||||
|
||||
|
||||
def test_jwt_aud_config_set_to_invalid_uri(defaultenv):
|
||||
"PostgREST should exit with an error message in output if jwt-aud config is set to an invalid URI"
|
||||
env = {
|
||||
**defaultenv,
|
||||
"PGRST_JWT_AUD": "foo://%%$$^^.com",
|
||||
}
|
||||
|
||||
with pytest.raises(PostgrestError):
|
||||
dump = cli(["--dump-config"], env=env).split("\n")
|
||||
assert "jwt-aud should be a string or a valid URI" in dump
|
||||
|
||||
+7
-6
@@ -99,20 +99,20 @@ def test_jwt_errors(defaultenv):
|
||||
assert response.status_code == 401
|
||||
assert response.json()["message"] == 'role "not_existing" does not exist'
|
||||
|
||||
# -31 seconds, because we allow clock skew of 30 seconds
|
||||
headers = jwtauthheader({"exp": relativeSeconds(-31)}, SECRET)
|
||||
# -35 seconds, because we allow clock skew of 30 seconds
|
||||
headers = jwtauthheader({"exp": relativeSeconds(-35)}, SECRET)
|
||||
response = postgrest.session.get("/", headers=headers)
|
||||
assert response.status_code == 401
|
||||
assert response.json()["message"] == "JWT expired"
|
||||
|
||||
# 31 seconds, because we allow clock skew of 30 seconds
|
||||
headers = jwtauthheader({"nbf": relativeSeconds(31)}, SECRET)
|
||||
# 35 seconds, because we allow clock skew of 30 seconds
|
||||
headers = jwtauthheader({"nbf": relativeSeconds(35)}, SECRET)
|
||||
response = postgrest.session.get("/", headers=headers)
|
||||
assert response.status_code == 401
|
||||
assert response.json()["message"] == "JWT not yet valid"
|
||||
|
||||
# 31 seconds, because we allow clock skew of 30 seconds
|
||||
headers = jwtauthheader({"iat": relativeSeconds(31)}, SECRET)
|
||||
# 35 seconds, because we allow clock skew of 35 seconds
|
||||
headers = jwtauthheader({"iat": relativeSeconds(35)}, SECRET)
|
||||
response = postgrest.session.get("/", headers=headers)
|
||||
assert response.status_code == 401
|
||||
assert response.json()["message"] == "JWT issued at future"
|
||||
@@ -1723,6 +1723,7 @@ def test_admin_metrics(defaultenv):
|
||||
with run(env=defaultenv, port=freeport()) as postgrest:
|
||||
response = postgrest.admin.get("/metrics")
|
||||
assert response.status_code == 200
|
||||
assert response.headers["Content-Type"] == "text/plain; charset=utf-8"
|
||||
assert "pgrst_schema_cache_query_time_seconds" in response.text
|
||||
assert 'pgrst_schema_cache_loads_total{status="SUCCESS"}' in response.text
|
||||
assert "pgrst_db_pool_max" in response.text
|
||||
|
||||
@@ -1058,6 +1058,33 @@ spec = describe "OpenAPI" $ do
|
||||
}
|
||||
|]
|
||||
|
||||
it "only includes POST method for volatile functions" $ do
|
||||
r <- simpleBody <$> get "/"
|
||||
let volatileGet = r ^? key "paths" . key "/rpc/reset_table" . key "get"
|
||||
volatilePost = r ^? key "paths" . key "/rpc/reset_table" . key "post"
|
||||
|
||||
liftIO $ do
|
||||
volatileGet `shouldBe` Nothing
|
||||
volatilePost `shouldNotBe` Nothing
|
||||
|
||||
it "includes GET and POST methods for stable functions" $ do
|
||||
r <- simpleBody <$> get "/"
|
||||
let stableGet = r ^? key "paths" . key "/rpc/getallusers" . key "get"
|
||||
stablePost = r ^? key "paths" . key "/rpc/getallusers" . key "post"
|
||||
|
||||
liftIO $ do
|
||||
stableGet `shouldNotBe` Nothing
|
||||
stablePost `shouldNotBe` Nothing
|
||||
|
||||
it "includes GET and POST methods for immutable functions" $ do
|
||||
r <- simpleBody <$> get "/"
|
||||
let immutableGet = r ^? key "paths" . key "/rpc/jwt_test" . key "get"
|
||||
immutablePost = r ^? key "paths" . key "/rpc/jwt_test" . key "post"
|
||||
|
||||
liftIO $ do
|
||||
immutableGet `shouldNotBe` Nothing
|
||||
immutablePost `shouldNotBe` Nothing
|
||||
|
||||
describe "Security" $
|
||||
it "does not include security or security definitions by default" $ do
|
||||
r <- simpleBody <$> get "/"
|
||||
|
||||
@@ -294,6 +294,20 @@ spec = do
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works when the column type is a tsvector domain" $ do
|
||||
get "tsearch_to_tsvector?select=text_search_domain&text_search_domain=fts(simple).of" `shouldRespondWith`
|
||||
[json| [
|
||||
{"text_search_domain":"'do':7 'fun':5 'impossible':9 'it':1 'kind':3 'of':4 's':2 'the':8 'to':6"}
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "works when the column type is a recursive tsvector domain" $ do
|
||||
get "tsearch_to_tsvector?select=text_search_rec_domain&text_search_rec_domain=fts(simple).of" `shouldRespondWith`
|
||||
[json| [
|
||||
{"text_search_rec_domain":"'do':7 'fun':5 'impossible':9 'it':1 'kind':3 'of':4 's':2 'the':8 'to':6"}
|
||||
]|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
context "text and json columns" $ do
|
||||
it "finds matches with to_tsquery" $ do
|
||||
get "/tsearch_to_tsvector?select=text_search&text_search=fts.impossible" `shouldRespondWith`
|
||||
|
||||
@@ -998,6 +998,22 @@ spec =
|
||||
|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "should work with filters that use the fts operator when the column type is a tsvector domain" $
|
||||
get "/rpc/get_tsearch_to_tsvector?select=text_search_domain&text_search_domain=fts(simple).impossible" `shouldRespondWith`
|
||||
[json|[
|
||||
{"text_search_domain":"'do':7 'fun':5 'impossible':9 'it':1 'kind':3 'of':4 's':2 'the':8 'to':6"},
|
||||
{"text_search_domain":"'amusant':5 'c':1 'de':6 'est':2 'faire':7 'impossible':9 'l':8 'peu':4 'un':3"}]
|
||||
|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "should work with filters that use the fts operator when the column type is a recursive tsvector domain" $
|
||||
get "/rpc/get_tsearch_to_tsvector?select=text_search_rec_domain&text_search_rec_domain=fts(simple).impossible" `shouldRespondWith`
|
||||
[json|[
|
||||
{"text_search_rec_domain":"'do':7 'fun':5 'impossible':9 'it':1 'kind':3 'of':4 's':2 'the':8 'to':6"},
|
||||
{"text_search_rec_domain":"'amusant':5 'c':1 'de':6 'est':2 'faire':7 'impossible':9 'l':8 'peu':4 'un':3"}]
|
||||
|]
|
||||
{ matchHeaders = [matchContentTypeJson] }
|
||||
|
||||
it "should work with the phraseto_tsquery function" $
|
||||
get "/rpc/get_tsearch?text_search_vector=phfts(english).impossible" `shouldRespondWith`
|
||||
[json|[{"text_search_vector":"'fun':5 'imposs':9 'kind':3"}]|]
|
||||
|
||||
@@ -600,3 +600,37 @@ spec =
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
context "empty spreads embeds" $
|
||||
it "should work return the same as empty embeddings" $ do
|
||||
get "/actors?select=*,...films()"
|
||||
`shouldRespondWith`
|
||||
[json| [{"id":1,"name":"john"}, {"id":2,"name":"mary"}] |]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
get "/grandchild_entities?select=name,...child_entities(parent_name:name,...entities())"
|
||||
`shouldRespondWith`
|
||||
[json|
|
||||
[{"name":"grandchild entity 1","parent_name":"child entity 1"},
|
||||
{"name":"grandchild entity 2","parent_name":"child entity 1"},
|
||||
{"name":"grandchild entity 3","parent_name":"child entity 2"},
|
||||
{"name":"(grandchild,entity,4)","parent_name":"child entity 2"},
|
||||
{"name":"(grandchild,entity,5)","parent_name":"child entity 2"}]
|
||||
|]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
get "/factories?select=factory:name,...processes()"
|
||||
`shouldRespondWith`
|
||||
[json|
|
||||
[{"factory":"Factory A"},
|
||||
{"factory":"Factory B"},
|
||||
{"factory":"Factory C"},
|
||||
{"factory":"Factory D"}]
|
||||
|]
|
||||
{ matchStatus = 200
|
||||
, matchHeaders = [matchContentTypeJson]
|
||||
}
|
||||
|
||||
Vendored
+4
-3
@@ -954,15 +954,16 @@ INSERT INTO tsearch_to_tsvector(text_search) VALUES ('C''est un peu amusant de f
|
||||
INSERT INTO tsearch_to_tsvector(text_search) VALUES ('Es ist eine Art Spaß, das Unmögliche zu machen');
|
||||
|
||||
UPDATE tsearch_to_tsvector SET jsonb_search = jsonb_build_object('text_search', text_search);
|
||||
|
||||
UPDATE tsearch_to_tsvector SET text_search_domain = to_tsvector('simple', text_search);
|
||||
UPDATE tsearch_to_tsvector SET text_search_rec_domain = to_tsvector('simple', text_search);
|
||||
|
||||
TRUNCATE TABLE artists CASCADE;
|
||||
INSERT INTO artists
|
||||
VALUES (1, 'duster'), (2, 'black country, new road'), (3, 'bjork');
|
||||
|
||||
TRUNCATE TABLE albums CASCADE;
|
||||
INSERT INTO albums
|
||||
INSERT INTO albums
|
||||
VALUES (1, 'stratosphere', 1),
|
||||
(2, 'ants from up above',2),
|
||||
(2, 'ants from up above',2),
|
||||
(3, 'vespertine',3),
|
||||
(4, 'contemporary movement', 1);
|
||||
|
||||
Vendored
+9
-1
@@ -3750,9 +3750,17 @@ create table surr_gen_default_upsert (
|
||||
extra text
|
||||
);
|
||||
|
||||
create domain tsvector_not_null as tsvector
|
||||
constraint "tsvector is required" check (value is not null);
|
||||
|
||||
create domain tsvector_not_empty as tsvector_not_null
|
||||
constraint "tsvector is required and not empty" check (value <> '');
|
||||
|
||||
create table tsearch_to_tsvector (
|
||||
text_search text,
|
||||
jsonb_search jsonb
|
||||
jsonb_search jsonb,
|
||||
text_search_domain tsvector_not_null default '',
|
||||
text_search_rec_domain tsvector_not_empty default '.'
|
||||
);
|
||||
|
||||
create function test.get_tsearch_to_tsvector() returns setof test.tsearch_to_tsvector AS $$
|
||||
|
||||
Reference in New Issue
Block a user