Compare commits
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
freebsd_instance:
|
freebsd_instance:
|
||||||
image_family: freebsd-14-0
|
image_family: freebsd-14-1
|
||||||
|
|
||||||
build_task:
|
build_task:
|
||||||
# Don't change this name without adjusting .github/workflows/build.yaml
|
# Don't change this name without adjusting .github/workflows/build.yaml
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# TODO: Remove this once a new actionlint release has been cut
|
||||||
|
# and made its way to us through nixpkgs.
|
||||||
|
self-hosted-runner:
|
||||||
|
labels:
|
||||||
|
- ubuntu-24.04-arm
|
||||||
@@ -112,7 +112,7 @@ runs:
|
|||||||
echo "artifacts=${artifacts}" >> "$GITHUB_OUTPUT"
|
echo "artifacts=${artifacts}" >> "$GITHUB_OUTPUT"
|
||||||
- name: Save artifact to GitHub Actions
|
- name: Save artifact to GitHub Actions
|
||||||
if: steps.find-task.outputs.task_found
|
if: steps.find-task.outputs.task_found
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
name: ${{ inputs.upload }}
|
name: ${{ inputs.upload }}
|
||||||
path: ${{ steps.download.outputs.artifacts }}
|
path: ${{ steps.download.outputs.artifacts }}
|
||||||
|
|||||||
@@ -19,14 +19,14 @@ inputs:
|
|||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
- uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
if: ${{ startsWith(github.ref, 'refs/heads/') || (inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
if: ${{ startsWith(github.ref, 'refs/heads/') || (inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||||
with:
|
with:
|
||||||
path: ${{ inputs.path }}
|
path: ${{ inputs.path }}
|
||||||
key: ${{ runner.os }}-${{ inputs.prefix }}-${{ inputs.suffix }}
|
key: ${{ runner.os }}-${{ inputs.prefix }}-${{ inputs.suffix }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-${{ inputs.prefix }}-
|
${{ runner.os }}-${{ inputs.prefix }}-
|
||||||
- uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
- uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||||
if: ${{ !startsWith(github.ref, 'refs/heads/') && !(inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
if: ${{ !startsWith(github.ref, 'refs/heads/') && !(inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||||
with:
|
with:
|
||||||
path: ${{ inputs.path }}
|
path: ${{ inputs.path }}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ inputs:
|
|||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- uses: nixbuild/nix-quick-install-action@60e9c39264d4714139af3cdf15f691b19eec3530 # v28
|
- uses: nixbuild/nix-quick-install-action@25aff27c252e0c8cdda3264805f7b6bcd92c8718 # v29
|
||||||
with:
|
with:
|
||||||
nix_conf: |-
|
nix_conf: |-
|
||||||
always-allow-substitutes = true
|
always-allow-substitutes = true
|
||||||
@@ -20,6 +20,7 @@ runs:
|
|||||||
with:
|
with:
|
||||||
name: postgrest
|
name: postgrest
|
||||||
authToken: ${{ inputs.authToken }}
|
authToken: ${{ inputs.authToken }}
|
||||||
|
skipPush: ${{ inputs.authToken == '' }}
|
||||||
- if: ${{ inputs.tools }}
|
- if: ${{ inputs.tools }}
|
||||||
run: nix-env -f default.nix -iA ${{ inputs.tools }}
|
run: nix-env -f default.nix -iA ${{ inputs.tools }}
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -1,72 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -Eeuo pipefail
|
|
||||||
|
|
||||||
# This script builds PostgREST in a remote ARM server
|
|
||||||
|
|
||||||
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
|
|
||||||
[ -z "$2" ] && { echo "Missing 2nd argument: Build environment directory name"; exit 1; }
|
|
||||||
[ -z "$3" ] && { echo "Missing 3rd argument: GHC version"; exit 1; }
|
|
||||||
|
|
||||||
PGRST_GITHUB_COMMIT="$1"
|
|
||||||
SCRIPT_DIR="$2"
|
|
||||||
|
|
||||||
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
|
|
||||||
# latest is a shortcut documented on https://www.haskell.org/ghcup/guide/#tags-and-shortcuts
|
|
||||||
CABAL_VERSION="latest"
|
|
||||||
GHC_VERSION="$3"
|
|
||||||
|
|
||||||
install_packages() {
|
|
||||||
sudo apt-get update -y
|
|
||||||
sudo apt-get upgrade -y
|
|
||||||
sudo apt-get install -y git build-essential curl libffi-dev libffi7 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5 llvm libnuma-dev zlib1g-dev libpq-dev jq gcc
|
|
||||||
sudo apt-get clean
|
|
||||||
}
|
|
||||||
|
|
||||||
install_ghcup() {
|
|
||||||
export BOOTSTRAP_HASKELL_NONINTERACTIVE=1
|
|
||||||
export BOOTSTRAP_HASKELL_MINIMAL=1
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
|
|
||||||
source ~/.ghcup/env
|
|
||||||
}
|
|
||||||
|
|
||||||
install_cabal() {
|
|
||||||
ghcup upgrade
|
|
||||||
ghcup install cabal $CABAL_VERSION
|
|
||||||
ghcup set cabal $CABAL_VERSION
|
|
||||||
}
|
|
||||||
|
|
||||||
install_ghc() {
|
|
||||||
ghcup upgrade
|
|
||||||
ghcup install ghc $GHC_VERSION
|
|
||||||
ghcup set ghc $GHC_VERSION
|
|
||||||
}
|
|
||||||
|
|
||||||
install_packages
|
|
||||||
|
|
||||||
# Add ghcup to the PATH for this session
|
|
||||||
[ -f ~/.ghcup/env ] && source ~/.ghcup/env
|
|
||||||
|
|
||||||
ghcup --version || install_ghcup
|
|
||||||
ghcup set cabal $CABAL_VERSION || install_cabal
|
|
||||||
ghcup set ghc $GHC_VERSION || install_ghc
|
|
||||||
|
|
||||||
cd ~/$SCRIPT_DIR
|
|
||||||
|
|
||||||
# Clone the repository and build the project
|
|
||||||
git clone https://github.com/PostgREST/postgrest.git
|
|
||||||
cd postgrest
|
|
||||||
git checkout $PGRST_GITHUB_COMMIT
|
|
||||||
cabal v2-update && cabal v2-build
|
|
||||||
|
|
||||||
# Strip unused symbols from executable
|
|
||||||
PGRST_BIN=$(cabal exec which postgrest | tail -1)
|
|
||||||
strip $PGRST_BIN
|
|
||||||
|
|
||||||
# Copy the built binary to the Dockerfile directory
|
|
||||||
cp $PGRST_BIN ~/$DOCKER_BUILD_DIR
|
|
||||||
|
|
||||||
# Move and compress the built binary
|
|
||||||
mkdir -p ~/$SCRIPT_DIR/result
|
|
||||||
mv $PGRST_BIN ~/$SCRIPT_DIR/result
|
|
||||||
cd ~/$SCRIPT_DIR
|
|
||||||
tar -cJf result.tar.xz result
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -Eeuo pipefail
|
|
||||||
|
|
||||||
# This script publishes the Docker ARM images to Docker Hub.
|
|
||||||
|
|
||||||
[ -z "$1" ] && { echo "Missing 1st argument: PostgREST github commit SHA"; exit 1; }
|
|
||||||
[ -z "$2" ] && { echo "Missing 2nd argument: Docker repo"; exit 1; }
|
|
||||||
[ -z "$3" ] && { echo "Missing 3rd argument: Docker username"; exit 1; }
|
|
||||||
[ -z "$4" ] && { echo "Missing 4th argument: Docker password"; exit 1; }
|
|
||||||
[ -z "$5" ] && { echo "Missing 5th argument: Build environment directory name"; exit 1; }
|
|
||||||
[ -z "$6" ] && { echo "Missing 6th argument: PostgREST version"; exit 1; }
|
|
||||||
|
|
||||||
PGRST_GITHUB_COMMIT="$1"
|
|
||||||
DOCKER_REPO="$2"
|
|
||||||
DOCKER_USER="$3"
|
|
||||||
DOCKER_PASS="$4"
|
|
||||||
SCRIPT_DIR="$5"
|
|
||||||
PGRST_VERSION="$6"
|
|
||||||
|
|
||||||
DOCKER_BUILD_DIR="$SCRIPT_DIR/docker-env"
|
|
||||||
|
|
||||||
clean_env()
|
|
||||||
{
|
|
||||||
sudo docker logout
|
|
||||||
}
|
|
||||||
|
|
||||||
# Login to Docker
|
|
||||||
sudo docker logout
|
|
||||||
{ echo $DOCKER_PASS | sudo docker login -u $DOCKER_USER --password-stdin; } || { echo "Couldn't login to docker"; exit 1; }
|
|
||||||
|
|
||||||
trap clean_env sigint sigterm exit
|
|
||||||
|
|
||||||
# Move to the docker build environment
|
|
||||||
cd ~/$DOCKER_BUILD_DIR
|
|
||||||
|
|
||||||
# Push final images to Docker hub
|
|
||||||
# NOTE: This command publishes a separate ARM image because the builds cannot
|
|
||||||
# be added to the manifest if they are not in the registry beforehand.
|
|
||||||
# This image must be manually deleted from Docker Hub at the end of the process.
|
|
||||||
sudo docker buildx build --build-arg PGRST_GITHUB_COMMIT=$PGRST_GITHUB_COMMIT \
|
|
||||||
-t $DOCKER_REPO/postgrest:$PGRST_VERSION-arm \
|
|
||||||
--push .
|
|
||||||
|
|
||||||
# Add the arm images to the manifest
|
|
||||||
# NOTE: This assumes that there already is a `postgrest:<version>` image
|
|
||||||
# for the amd64 architecture pushed to Docker Hub
|
|
||||||
sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:$PGRST_VERSION $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
|
|
||||||
[ "$PGRST_VERSION" != "devel" ] && sudo docker buildx imagetools create --append -t $DOCKER_REPO/postgrest:latest $DOCKER_REPO/postgrest:$PGRST_VERSION-arm
|
|
||||||
|
|
||||||
sudo docker logout
|
|
||||||
@@ -30,10 +30,10 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
static:
|
static:
|
||||||
name: Nix - Linux static
|
name: Nix - Linux x86-64 static
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -42,27 +42,27 @@ jobs:
|
|||||||
- name: Build static executable
|
- name: Build static executable
|
||||||
run: nix-build -A postgrestStatic
|
run: nix-build -A postgrestStatic
|
||||||
- name: Save built executable as artifact
|
- name: Save built executable as artifact
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
name: postgrest-linux-static-x64
|
name: postgrest-linux-static-x86-64
|
||||||
path: result/bin/postgrest
|
path: result/bin/postgrest
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
|
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
|
||||||
- name: Save built Docker image as artifact
|
- name: Save built Docker image as artifact
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
name: postgrest-docker-x64
|
name: postgrest-docker-x86-64
|
||||||
path: postgrest-docker.tar.gz
|
path: postgrest-docker.tar.gz
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
name: Nix - MacOS
|
name: Nix - MacOS
|
||||||
runs-on: macos-12
|
runs-on: macos-14
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -87,39 +87,38 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- name: Linux
|
- name: Linux aarch64
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04-arm
|
||||||
cache: |
|
cache: |
|
||||||
~/.stack/pantry
|
~/.stack/pantry
|
||||||
~/.stack/snapshots
|
~/.stack/snapshots
|
||||||
~/.stack/stack.sqlite3
|
~/.stack/stack.sqlite3
|
||||||
# no artifact for Linux, because we use the static build
|
artifact: postgrest-ubuntu-aarch64
|
||||||
|
deps: sudo apt-get update && sudo apt-get install libpq-dev
|
||||||
|
|
||||||
- name: MacOS
|
- name: MacOS
|
||||||
runs-on: macos-12
|
runs-on: macos-14
|
||||||
cache: |
|
cache: |
|
||||||
~/.stack/pantry
|
~/.stack/pantry
|
||||||
~/.stack/snapshots
|
~/.stack/snapshots
|
||||||
~/.stack/stack.sqlite3
|
~/.stack/stack.sqlite3
|
||||||
artifact: postgrest-macos-x64
|
artifact: postgrest-macos-aarch64
|
||||||
|
deps: brew link --force libpq
|
||||||
|
|
||||||
- name: Windows
|
- name: Windows
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
cache: |
|
cache: |
|
||||||
~\AppData\Roaming\stack\pantry
|
C:\sr\pantry
|
||||||
~\AppData\Local\Programs\stack\pantry
|
C:\sr\snapshots
|
||||||
~\AppData\Roaming\stack\snapshots
|
C:\sr\stack.sqlite3
|
||||||
~\AppData\Local\Programs\stack\snapshots
|
|
||||||
~\AppData\Roaming\stack\stack.sqlite3
|
|
||||||
~\AppData\Local\Programs\stack\stack.sqlite3
|
|
||||||
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
|
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
|
||||||
artifact: postgrest-windows-x64
|
artifact: postgrest-windows-x86-64
|
||||||
|
|
||||||
name: Stack - ${{ matrix.name }}
|
name: Stack - ${{ matrix.name }}
|
||||||
runs-on: ${{ matrix.runs-on }}
|
runs-on: ${{ matrix.runs-on }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: haskell-actions/setup@33585e1a16afa5875e124b0ebc89dd0c2f872c21 # v2.7.3
|
- uses: haskell-actions/setup@bbd90a29996ac33b1c644a42206e312fc0379748 # v2.7.9
|
||||||
with:
|
with:
|
||||||
# This must match the version in stack.yaml's resolver
|
# This must match the version in stack.yaml's resolver
|
||||||
ghc-version: 9.6.5
|
ghc-version: 9.6.5
|
||||||
@@ -147,8 +146,7 @@ jobs:
|
|||||||
- name: Strip Executable
|
- name: Strip Executable
|
||||||
run: strip result/postgrest*
|
run: strip result/postgrest*
|
||||||
- name: Save built executable as artifact
|
- name: Save built executable as artifact
|
||||||
if: matrix.artifact
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
|
||||||
with:
|
with:
|
||||||
name: ${{ matrix.artifact }}
|
name: ${{ matrix.artifact }}
|
||||||
path: |
|
path: |
|
||||||
@@ -159,15 +157,15 @@ jobs:
|
|||||||
|
|
||||||
freebsd:
|
freebsd:
|
||||||
name: Stack - FreeBSD from CirrusCI
|
name: Stack - FreeBSD from CirrusCI
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: ./.github/actions/artifact-from-cirrus
|
- uses: ./.github/actions/artifact-from-cirrus
|
||||||
with:
|
with:
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
task: Build FreeBSD (Stack)
|
task: Build FreeBSD (Stack)
|
||||||
download: bin
|
download: bin
|
||||||
upload: postgrest-freebsd-x64
|
upload: postgrest-freebsd-x86-64
|
||||||
|
|
||||||
|
|
||||||
cabal:
|
cabal:
|
||||||
@@ -175,11 +173,11 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
ghc: ['9.6.4', '9.8.2']
|
ghc: ['9.6.4', '9.8.2']
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
name: Cabal - Linux GHC ${{ matrix.ghc }}
|
name: Cabal - Linux x86-64 - GHC ${{ matrix.ghc }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- uses: haskell-actions/setup@33585e1a16afa5875e124b0ebc89dd0c2f872c21 # v2.7.3
|
- uses: haskell-actions/setup@bbd90a29996ac33b1c644a42206e312fc0379748 # v2.7.9
|
||||||
with:
|
with:
|
||||||
ghc-version: ${{ matrix.ghc }}
|
ghc-version: ${{ matrix.ghc }}
|
||||||
- name: Cache .cabal
|
- name: Cache .cabal
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
lint-style:
|
lint-style:
|
||||||
name: Lint & Style
|
name: Lint & Style
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
|
|||||||
+4
-296
@@ -5,21 +5,10 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- v[0-9]+
|
- v[0-9]+
|
||||||
tags:
|
|
||||||
- devel
|
|
||||||
- v*
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
# Terminate all previous runs of the same workflow for the same tag, but never
|
|
||||||
# cancel for the main / release branch pipelines.
|
|
||||||
group: ci-${{ (github.ref_type == 'tag' && github.ref) || github.run_id }}
|
|
||||||
# TODO: Enable this once https://github.com/orgs/community/discussions/13015 is solved
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check:
|
check:
|
||||||
name: Check
|
name: Check
|
||||||
if: startsWith(github.ref, 'refs/heads/')
|
|
||||||
uses: ./.github/workflows/check.yaml
|
uses: ./.github/workflows/check.yaml
|
||||||
secrets:
|
secrets:
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
@@ -27,7 +16,6 @@ jobs:
|
|||||||
|
|
||||||
docs:
|
docs:
|
||||||
name: Docs
|
name: Docs
|
||||||
if: startsWith(github.ref, 'refs/heads/')
|
|
||||||
uses: ./.github/workflows/docs.yaml
|
uses: ./.github/workflows/docs.yaml
|
||||||
secrets:
|
secrets:
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
@@ -35,7 +23,6 @@ jobs:
|
|||||||
|
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
if: startsWith(github.ref, 'refs/heads/')
|
|
||||||
uses: ./.github/workflows/test.yaml
|
uses: ./.github/workflows/test.yaml
|
||||||
secrets:
|
secrets:
|
||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
@@ -49,87 +36,21 @@ jobs:
|
|||||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
arm:
|
|
||||||
name: Build / Cabal - aarch64 GHC 9.4.8
|
|
||||||
if: vars.SSH_ARM_ENABLED
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
outputs:
|
|
||||||
remotepath: ${{ steps.Remote-Dir.outputs.remotepath }}
|
|
||||||
env:
|
|
||||||
GITHUB_COMMIT: ${{ github.sha }}
|
|
||||||
GHC_VERSION: '9.4.8'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
|
||||||
- id: Remote-Dir
|
|
||||||
name: Unique directory name for the remote build
|
|
||||||
run: echo "remotepath=postgrest-build-$(uuidgen)" >> "$GITHUB_OUTPUT"
|
|
||||||
- name: Copy script files to the remote server
|
|
||||||
uses: appleboy/scp-action@master
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SSH_ARM_HOST }}
|
|
||||||
username: ubuntu
|
|
||||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
|
||||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
|
||||||
source: ".github/scripts/arm/*"
|
|
||||||
target: ${{ steps.Remote-Dir.outputs.remotepath }}
|
|
||||||
strip_components: 3
|
|
||||||
- name: Build ARM
|
|
||||||
uses: appleboy/ssh-action@master
|
|
||||||
env:
|
|
||||||
REMOTE_DIR: ${{ steps.Remote-Dir.outputs.remotepath }}
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SSH_ARM_HOST }}
|
|
||||||
username: ubuntu
|
|
||||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
|
||||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
|
||||||
command_timeout: 120m
|
|
||||||
script_stop: true
|
|
||||||
envs: GITHUB_COMMIT,REMOTE_DIR,GHC_VERSION
|
|
||||||
script: bash ~/$REMOTE_DIR/build.sh "$GITHUB_COMMIT" "$REMOTE_DIR" "$GHC_VERSION"
|
|
||||||
- name: Download binaries from remote server
|
|
||||||
uses: nicklasfrahm/scp-action@main
|
|
||||||
with:
|
|
||||||
direction: download
|
|
||||||
host: ${{ secrets.SSH_ARM_HOST }}
|
|
||||||
username: ubuntu
|
|
||||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
|
||||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
|
||||||
source: "${{ steps.Remote-Dir.outputs.remotepath }}/result.tar.xz"
|
|
||||||
target: "result.tar.xz"
|
|
||||||
- name: Extract downloaded binaries
|
|
||||||
run: tar -xvf result.tar.xz && rm result.tar.xz
|
|
||||||
- name: Save aarch64 executable as artifact
|
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
|
||||||
with:
|
|
||||||
name: postgrest-ubuntu-aarch64
|
|
||||||
path: result/postgrest
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
|
|
||||||
tag:
|
tag:
|
||||||
name: Release / Tag
|
name: Tag
|
||||||
concurrency:
|
concurrency:
|
||||||
# Never tag outdated commits on the main branch by skipping superseded commits
|
# Never tag outdated commits on the main branch by skipping superseded commits
|
||||||
group: ci-tag-${{ (github.ref == 'refs/heads/main' && github.ref) || github.run_id }}
|
group: ci-tag-${{ (github.ref == 'refs/heads/main' && github.ref) || github.run_id }}
|
||||||
# TODO: Enable this once https://github.com/orgs/community/discussions/13015 is solved
|
# TODO: Enable this once https://github.com/orgs/community/discussions/13015 is solved
|
||||||
cancel-in-progress: false
|
cancel-in-progress: false
|
||||||
if: |
|
if: vars.RELEASE_ENABLED
|
||||||
vars.RELEASE_ENABLED &&
|
runs-on: ubuntu-24.04
|
||||||
startsWith(github.ref, 'refs/heads/') &&
|
|
||||||
needs.docs.result == 'success' &&
|
|
||||||
needs.test.result == 'success' &&
|
|
||||||
needs.build.result == 'success' &&
|
|
||||||
(needs.arm.result == 'skipped' || success())
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs:
|
needs:
|
||||||
- docs
|
- docs
|
||||||
- test
|
- test
|
||||||
- build
|
- build
|
||||||
- arm
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
|
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
|
||||||
- name: Tag latest commit
|
- name: Tag latest commit
|
||||||
@@ -147,216 +68,3 @@ jobs:
|
|||||||
git tag -f "devel"
|
git tag -f "devel"
|
||||||
git push -f origin "devel"
|
git push -f origin "devel"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
prepare:
|
|
||||||
name: Release / Prepare
|
|
||||||
if: |
|
|
||||||
startsWith(github.ref, 'refs/tags/') &&
|
|
||||||
needs.build.result == 'success' &&
|
|
||||||
(needs.arm.result == 'skipped' || success())
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs:
|
|
||||||
- build
|
|
||||||
- arm
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
|
||||||
- name: Check the version to be released
|
|
||||||
run: |
|
|
||||||
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
|
||||||
|
|
||||||
if [ "${GITHUB_REF_NAME}" != "devel" ] && [ "${GITHUB_REF_NAME}" != "v$cabal_version" ]; then
|
|
||||||
echo "Tagged version ($GITHUB_REF_NAME) does not match the one in postgrest.cabal (v$cabal_version). Aborting release..."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
- name: Identify changes from CHANGELOG.md
|
|
||||||
run: |
|
|
||||||
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
|
|
||||||
echo "Getting unreleased changes..."
|
|
||||||
sed -n "1,/## Unreleased/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
|
|
||||||
else
|
|
||||||
version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
|
||||||
echo "Propper release, getting changes for version $version ..."
|
|
||||||
sed -n "1,/## \[$version\]/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Relevant extract from CHANGELOG.md:"
|
|
||||||
cat CHANGES.md
|
|
||||||
- name: Save CHANGES.md as artifact
|
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
|
||||||
with:
|
|
||||||
name: release-changes
|
|
||||||
path: CHANGES.md
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
|
|
||||||
github:
|
|
||||||
name: Release / GitHub
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs:
|
|
||||||
- prepare
|
|
||||||
if: success() || needs.prepare.result == 'success'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
|
||||||
- name: Download all artifacts
|
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
|
||||||
with:
|
|
||||||
path: artifacts
|
|
||||||
- name: Create release bundle with archives for all builds
|
|
||||||
run: |
|
|
||||||
find artifacts -type f -iname postgrest -exec chmod +x {} \;
|
|
||||||
|
|
||||||
mkdir -p release-bundle
|
|
||||||
|
|
||||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-linux-static-x64.tar.xz" \
|
|
||||||
-C artifacts/postgrest-linux-static-x64 postgrest
|
|
||||||
|
|
||||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-macos-x64.tar.xz" \
|
|
||||||
-C artifacts/postgrest-macos-x64 postgrest
|
|
||||||
|
|
||||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-freebsd-x64.tar.xz" \
|
|
||||||
-C artifacts/postgrest-freebsd-x64 postgrest
|
|
||||||
|
|
||||||
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-ubuntu-aarch64.tar.xz" \
|
|
||||||
-C artifacts/postgrest-ubuntu-aarch64 postgrest
|
|
||||||
|
|
||||||
zip "release-bundle/postgrest-${GITHUB_REF_NAME}-windows-x64.zip" \
|
|
||||||
artifacts/postgrest-windows-x64/postgrest.exe
|
|
||||||
|
|
||||||
- name: Save release bundle
|
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
|
||||||
with:
|
|
||||||
name: release-bundle
|
|
||||||
path: release-bundle
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Publish release on GitHub
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
echo "Releasing version ${GITHUB_REF_NAME} on GitHub..."
|
|
||||||
|
|
||||||
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
|
|
||||||
# To replace the existing release, we must first delete the old assets,
|
|
||||||
# then modify the release, then add the new assets.
|
|
||||||
gh release view devel --json assets \
|
|
||||||
| jq -r '.assets[] | .name' \
|
|
||||||
| xargs -rn1 \
|
|
||||||
gh release delete-asset -y devel
|
|
||||||
gh release edit devel \
|
|
||||||
-t devel \
|
|
||||||
--verify-tag \
|
|
||||||
-F artifacts/release-changes/CHANGES.md \
|
|
||||||
--prerelease
|
|
||||||
gh release upload --clobber devel release-bundle/*
|
|
||||||
else
|
|
||||||
gh release create "${GITHUB_REF_NAME}" \
|
|
||||||
-t "${GITHUB_REF_NAME}" \
|
|
||||||
--verify-tag \
|
|
||||||
-F artifacts/release-changes/CHANGES.md \
|
|
||||||
release-bundle/*
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
docker:
|
|
||||||
name: Release / Docker Hub
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs:
|
|
||||||
- prepare
|
|
||||||
if: |
|
|
||||||
vars.DOCKER_REPO && vars.DOCKER_USER &&
|
|
||||||
(success() || needs.prepare.result == 'success')
|
|
||||||
env:
|
|
||||||
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
|
|
||||||
DOCKER_USER: ${{ vars.DOCKER_USER }}
|
|
||||||
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
|
||||||
- name: Download Docker image
|
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
|
||||||
with:
|
|
||||||
name: postgrest-docker-x64
|
|
||||||
- name: Publish images on Docker Hub
|
|
||||||
run: |
|
|
||||||
docker login -u "$DOCKER_USER" -p "$DOCKER_PASS"
|
|
||||||
docker load -i postgrest-docker.tar.gz
|
|
||||||
|
|
||||||
docker tag postgrest:latest "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}"
|
|
||||||
docker push "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}"
|
|
||||||
|
|
||||||
# Only tag 'latest' for full releases
|
|
||||||
if [ "${GITHUB_REF_NAME}" != "devel" ]; then
|
|
||||||
echo "Pushing to 'latest' tag for full release of ${GITHUB_REF_NAME} ..."
|
|
||||||
docker tag postgrest:latest "$DOCKER_REPO"/postgrest:latest
|
|
||||||
docker push "$DOCKER_REPO"/postgrest:latest
|
|
||||||
else
|
|
||||||
echo "Skipping push to 'latest' tag for pre-release..."
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
docker-description:
|
|
||||||
name: Release / Docker Hub Description
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
if: |
|
|
||||||
vars.DOCKER_REPO && vars.DOCKER_USER &&
|
|
||||||
github.ref == 'refs/tags/devel'
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
|
||||||
- uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae # v4.0.0
|
|
||||||
with:
|
|
||||||
username: ${{ vars.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_PASS }}
|
|
||||||
repository: ${{ vars.DOCKER_REPO }}/postgrest
|
|
||||||
short-description: ${{ github.event.repository.description }}
|
|
||||||
readme-filepath: ./docker-hub-readme.md
|
|
||||||
|
|
||||||
|
|
||||||
docker-arm:
|
|
||||||
name: Release / Docker Hub Arm
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
needs:
|
|
||||||
- arm
|
|
||||||
- docker
|
|
||||||
env:
|
|
||||||
GITHUB_COMMIT: ${{ github.sha }}
|
|
||||||
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
|
|
||||||
DOCKER_USER: ${{ vars.DOCKER_USER }}
|
|
||||||
DOCKER_PASS: ${{ secrets.DOCKER_PASS }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
|
||||||
- name: Publish images for ARM builds on Docker Hub
|
|
||||||
uses: appleboy/ssh-action@master
|
|
||||||
env:
|
|
||||||
REMOTE_DIR: ${{ needs.arm.outputs.remotepath }}
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SSH_ARM_HOST }}
|
|
||||||
username: ubuntu
|
|
||||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
|
||||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
|
||||||
script_stop: true
|
|
||||||
envs: GITHUB_COMMIT,DOCKER_REPO,DOCKER_USER,DOCKER_PASS,REMOTE_DIR,GITHUB_REF_NAME
|
|
||||||
script: bash ~/$REMOTE_DIR/docker-publish.sh "$GITHUB_COMMIT" "$DOCKER_REPO" "$DOCKER_USER" "$DOCKER_PASS" "$REMOTE_DIR" "$GITHUB_REF_NAME"
|
|
||||||
|
|
||||||
|
|
||||||
clean-arm:
|
|
||||||
name: Build / Cleanup
|
|
||||||
needs:
|
|
||||||
- arm
|
|
||||||
- docker-arm
|
|
||||||
if: ${{ always() && vars.SSH_ARM_ENABLED }}
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
env:
|
|
||||||
REMOTE_DIR: ${{ needs.arm.outputs.remotepath }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
|
||||||
- name: Remove uploaded files from server
|
|
||||||
uses: appleboy/ssh-action@master
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.SSH_ARM_HOST }}
|
|
||||||
username: ubuntu
|
|
||||||
key: ${{ secrets.SSH_ARM_PRIVATE_KEY }}
|
|
||||||
fingerprint: ${{ secrets.SSH_ARM_FINGERPRINT }}
|
|
||||||
envs: REMOTE_DIR
|
|
||||||
script: rm -rf $REMOTE_DIR
|
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -39,9 +39,9 @@ jobs:
|
|||||||
|
|
||||||
spellcheck:
|
spellcheck:
|
||||||
name: Spellcheck
|
name: Spellcheck
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -56,9 +56,9 @@ jobs:
|
|||||||
linkcheck:
|
linkcheck:
|
||||||
name: Linkcheck
|
name: Linkcheck
|
||||||
if: github.base_ref == 'main'
|
if: github.base_ref == 'main'
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -0,0 +1,202 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- devel
|
||||||
|
- v*
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
# Terminate all previous runs of the same workflow for the same tag.
|
||||||
|
group: release-${{ github.ref }}
|
||||||
|
# TODO: Enable this once https://github.com/orgs/community/discussions/13015 is solved
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
uses: ./.github/workflows/build.yaml
|
||||||
|
secrets:
|
||||||
|
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
prepare:
|
||||||
|
name: Prepare
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: Check the version to be released
|
||||||
|
run: |
|
||||||
|
cabal_version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||||
|
|
||||||
|
if [ "${GITHUB_REF_NAME}" != "devel" ] && [ "${GITHUB_REF_NAME}" != "v$cabal_version" ]; then
|
||||||
|
echo "Tagged version ($GITHUB_REF_NAME) does not match the one in postgrest.cabal (v$cabal_version). Aborting release..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: Identify changes from CHANGELOG.md
|
||||||
|
run: |
|
||||||
|
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
|
||||||
|
echo "Getting unreleased changes..."
|
||||||
|
sed -n "1,/## Unreleased/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
|
||||||
|
else
|
||||||
|
version="$(grep -oP '^version:\s*\K.*' postgrest.cabal)"
|
||||||
|
echo "Propper release, getting changes for version $version ..."
|
||||||
|
sed -n "1,/## \[$version\]/d;/## \[/q;p" CHANGELOG.md > CHANGES.md
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Relevant extract from CHANGELOG.md:"
|
||||||
|
cat CHANGES.md
|
||||||
|
- name: Save CHANGES.md as artifact
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
|
with:
|
||||||
|
name: release-changes
|
||||||
|
path: CHANGES.md
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
|
||||||
|
github:
|
||||||
|
name: GitHub
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
needs:
|
||||||
|
- prepare
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: Download all artifacts
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
- name: Create release bundle with archives for all builds
|
||||||
|
run: |
|
||||||
|
find artifacts -type f -iname postgrest -exec chmod +x {} \;
|
||||||
|
|
||||||
|
mkdir -p release-bundle
|
||||||
|
|
||||||
|
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-linux-static-x86-64.tar.xz" \
|
||||||
|
-C artifacts/postgrest-linux-static-x86-64 postgrest
|
||||||
|
|
||||||
|
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-macos-aarch64.tar.xz" \
|
||||||
|
-C artifacts/postgrest-macos-aarch64 postgrest
|
||||||
|
|
||||||
|
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-freebsd-x86-64.tar.xz" \
|
||||||
|
-C artifacts/postgrest-freebsd-x86-64 postgrest
|
||||||
|
|
||||||
|
tar cJvf "release-bundle/postgrest-${GITHUB_REF_NAME}-ubuntu-aarch64.tar.xz" \
|
||||||
|
-C artifacts/postgrest-ubuntu-aarch64 postgrest
|
||||||
|
|
||||||
|
zip --junk-paths "release-bundle/postgrest-${GITHUB_REF_NAME}-windows-x86-64.zip" \
|
||||||
|
artifacts/postgrest-windows-x86-64/postgrest.exe
|
||||||
|
|
||||||
|
- name: Save release bundle
|
||||||
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
|
with:
|
||||||
|
name: release-bundle
|
||||||
|
path: release-bundle
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
- name: Publish release on GitHub
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
echo "Releasing version ${GITHUB_REF_NAME} on GitHub..."
|
||||||
|
|
||||||
|
if [ "${GITHUB_REF_NAME}" == "devel" ]; then
|
||||||
|
# To replace the existing release, we must first delete the old assets,
|
||||||
|
# then modify the release, then add the new assets.
|
||||||
|
gh release view devel --json assets \
|
||||||
|
| jq -r '.assets[] | .name' \
|
||||||
|
| xargs -rn1 \
|
||||||
|
gh release delete-asset -y devel
|
||||||
|
gh release edit devel \
|
||||||
|
-t devel \
|
||||||
|
--verify-tag \
|
||||||
|
-F artifacts/release-changes/CHANGES.md \
|
||||||
|
--prerelease
|
||||||
|
gh release upload --clobber devel release-bundle/*
|
||||||
|
else
|
||||||
|
gh release create "${GITHUB_REF_NAME}" \
|
||||||
|
-t "${GITHUB_REF_NAME}" \
|
||||||
|
--verify-tag \
|
||||||
|
-F artifacts/release-changes/CHANGES.md \
|
||||||
|
release-bundle/*
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
docker:
|
||||||
|
name: Docker Hub
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
needs:
|
||||||
|
- prepare
|
||||||
|
if: |
|
||||||
|
vars.DOCKER_REPO && vars.DOCKER_USER
|
||||||
|
env:
|
||||||
|
DOCKER_REPO: ${{ vars.DOCKER_REPO }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: Download x86-64 Docker image
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
with:
|
||||||
|
name: postgrest-docker-x86-64
|
||||||
|
- name: Download aarch64 binary
|
||||||
|
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||||
|
with:
|
||||||
|
name: postgrest-ubuntu-aarch64
|
||||||
|
- uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
|
||||||
|
- uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||||
|
with:
|
||||||
|
username: ${{ vars.DOCKER_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_PASS }}
|
||||||
|
- name: Build aarch64 Docker image
|
||||||
|
run: |
|
||||||
|
# This only pushes the image via digest, not a tag. This will not appear
|
||||||
|
# in the image list on Docker Hub, yet. It will be later added to the main
|
||||||
|
# tag's manifest.
|
||||||
|
docker buildx build \
|
||||||
|
-t "$DOCKER_REPO/postgrest" \
|
||||||
|
--platform linux/arm64 \
|
||||||
|
--output push-by-digest=true,type=image,push=true \
|
||||||
|
--metadata-file metadata.json \
|
||||||
|
.
|
||||||
|
echo "SHA256_ARM=$(jq -r '."containerimage.digest"' metadata.json)" >> "$GITHUB_ENV"
|
||||||
|
- name: Publish images on Docker Hub
|
||||||
|
run: |
|
||||||
|
docker load -i postgrest-docker.tar.gz
|
||||||
|
|
||||||
|
docker tag postgrest:latest "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}"
|
||||||
|
docker push "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}"
|
||||||
|
docker buildx imagetools create --append \
|
||||||
|
-t "$DOCKER_REPO/postgrest:${GITHUB_REF_NAME}" \
|
||||||
|
"$DOCKER_REPO/postgrest@$SHA256_ARM"
|
||||||
|
|
||||||
|
# Only tag 'latest' for full releases
|
||||||
|
if [ "${GITHUB_REF_NAME}" != "devel" ]; then
|
||||||
|
echo "Pushing to 'latest' tag for full release of ${GITHUB_REF_NAME} ..."
|
||||||
|
docker tag postgrest:latest "$DOCKER_REPO"/postgrest:latest
|
||||||
|
docker push "$DOCKER_REPO"/postgrest:latest
|
||||||
|
docker buildx imagetools create --append \
|
||||||
|
-t "$DOCKER_REPO/postgrest:latest" \
|
||||||
|
"$DOCKER_REPO/postgrest@$SHA256_ARM"
|
||||||
|
else
|
||||||
|
echo "Skipping push to 'latest' tag for pre-release..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
docker-description:
|
||||||
|
name: Docker Hub Description
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
if: |
|
||||||
|
vars.DOCKER_REPO && vars.DOCKER_USER &&
|
||||||
|
github.ref == 'refs/tags/devel'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae # v4.0.0
|
||||||
|
with:
|
||||||
|
username: ${{ vars.DOCKER_USER }}
|
||||||
|
password: ${{ secrets.DOCKER_PASS }}
|
||||||
|
repository: ${{ vars.DOCKER_REPO }}/postgrest
|
||||||
|
short-description: ${{ github.event.repository.description }}
|
||||||
|
readme-filepath: ./docker-hub-readme.md
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
name: Upload Reports
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows:
|
|
||||||
- CI
|
|
||||||
- Test
|
|
||||||
branches-ignore:
|
|
||||||
- devel
|
|
||||||
- v[0-9]+
|
|
||||||
- v[0-9]+.[0-9]+.[0-9]+
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
loadtest:
|
|
||||||
name: Loadtest
|
|
||||||
permissions:
|
|
||||||
checks: write
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
if: github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure'
|
|
||||||
steps:
|
|
||||||
- name: Download from Artifacts
|
|
||||||
uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
|
||||||
with:
|
|
||||||
github-token: ${{ github.token }}
|
|
||||||
run-id: ${{ github.event.workflow_run.id }}
|
|
||||||
name: loadtest.md
|
|
||||||
path: artifacts
|
|
||||||
- name: Upload to GitHub Checks
|
|
||||||
uses: LouisBrunner/checks-action@6b626ffbad7cc56fd58627f774b9067e6118af23 # v2.0.0
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
sha: ${{ github.event.workflow_run.head_sha }}
|
|
||||||
name: Loadtest Results
|
|
||||||
conclusion: neutral
|
|
||||||
output: |
|
|
||||||
{"summary":""}
|
|
||||||
output_text_description_file: artifacts/loadtest.md
|
|
||||||
+10
-16
@@ -32,14 +32,14 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
coverage:
|
coverage:
|
||||||
name: Coverage
|
name: Coverage
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
# Hack for enabling color output, see:
|
# Hack for enabling color output, see:
|
||||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -49,7 +49,7 @@ jobs:
|
|||||||
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
|
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
|
||||||
run: postgrest-coverage
|
run: postgrest-coverage
|
||||||
- name: Upload coverage to codecov
|
- name: Upload coverage to codecov
|
||||||
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1
|
uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1
|
||||||
with:
|
with:
|
||||||
files: ./coverage/codecov.json
|
files: ./coverage/codecov.json
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
@@ -69,14 +69,14 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
pgVersion: ["9_6", 10, 11, 12, 13, 14, 15, 16]
|
pgVersion: ["9_6", 10, 11, 12, 13, 14, 15, 16]
|
||||||
name: PG ${{ matrix.pgVersion }}
|
name: PG ${{ matrix.pgVersion }}
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
# Hack for enabling color output, see:
|
# Hack for enabling color output, see:
|
||||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -98,9 +98,9 @@ jobs:
|
|||||||
|
|
||||||
memory:
|
memory:
|
||||||
name: Memory
|
name: Memory
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
uses: ./.github/actions/setup-nix
|
uses: ./.github/actions/setup-nix
|
||||||
with:
|
with:
|
||||||
@@ -112,9 +112,9 @@ jobs:
|
|||||||
|
|
||||||
loadtest:
|
loadtest:
|
||||||
name: Loadtest
|
name: Loadtest
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Nix Environment
|
- name: Setup Nix Environment
|
||||||
@@ -129,10 +129,4 @@ jobs:
|
|||||||
- name: Run loadtest
|
- name: Run loadtest
|
||||||
run: |
|
run: |
|
||||||
postgrest-loadtest-against main ${{ steps.get-latest-tag.outputs.tag }}
|
postgrest-loadtest-against main ${{ steps.get-latest-tag.outputs.tag }}
|
||||||
postgrest-loadtest-report > loadtest/loadtest.md
|
postgrest-loadtest-report >> "$GITHUB_STEP_SUMMARY"
|
||||||
- name: Upload report
|
|
||||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
|
||||||
with:
|
|
||||||
name: loadtest.md
|
|
||||||
path: loadtest/loadtest.md
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|||||||
@@ -5,6 +5,67 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## [12.2.8] - 2025-02-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3841, Log `503` client error to stderr - @taimoorzaeem
|
||||||
|
|
||||||
|
## [12.2.7] - 2025-02-03
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #2524, Fix schema reloading notice on windows - @diogob
|
||||||
|
|
||||||
|
## [12.2.6] - 2025-01-29
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3788, Fix jwt cache does not remove expired entries - @taimoorzaeem
|
||||||
|
|
||||||
|
## [12.2.5] - 2025-01-20
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3867, Fix startup for arm64 docker image - @wolfgangwalther
|
||||||
|
|
||||||
|
## [12.2.4] - 2025-01-18
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3779, Always log the schema cache load time - @steve-chavez
|
||||||
|
- #3706, Fix insert with `missing=default` uses default value of domain instead of column - @taimoorzaeem
|
||||||
|
|
||||||
|
## [12.2.3] - 2024-08-01
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3091, Broken link in OpenAPI description `externalDocs` - @salim-b
|
||||||
|
- #3659, Embed One-to-One relationship with different column order properly - @wolfgangwalther
|
||||||
|
- #3504, Remove `format` from `rowFilter` parameters in OpenAPI - @dantheman2865
|
||||||
|
- #3660, Fix regression that loaded the schema cache before the in-database configuration - @steve-chavez, @laurenceisla
|
||||||
|
|
||||||
|
## [12.2.2] - 2024-07-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3093, Nested empty embeds no longer show empty values and are correctly omitted - @laurenceisla
|
||||||
|
- #3644, Make --dump-schema work with in-database pgrst.db_schemas setting - @wolfgangwalther
|
||||||
|
- #3644, Show number of timezones in schema cache load report - @wolfgangwalther
|
||||||
|
- #3644, List correct enum options in OpenApi output when multiple types with same name are present - @wolfgangwalther
|
||||||
|
- #3523, Fix schema cache loading retry without backoff - @steve-chavez
|
||||||
|
|
||||||
|
## [12.2.1] - 2024-06-27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- #3147, Don't reload schema cache on every listener failure - @steve-chavez
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- #3592, Architecture diagram now supports dark mode and has links - @laurenceisla
|
||||||
|
- #3616, The schema isolation diagram now supports dark mode and uses well-known schemas - @laurenceisla
|
||||||
|
|
||||||
## [12.2.0] - 2024-06-11
|
## [12.2.0] - 2024-06-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# PostgREST docker hub image
|
# PostgREST Docker Hub image for aarch64.
|
||||||
|
# The x86-64 is a single-static-binary image built via Nix, see:
|
||||||
|
# nix/tools/docker/README.md
|
||||||
|
|
||||||
FROM ubuntu:noble@sha256:3f85b7caad41a95462cf5b787d8a04604c8262cdcdf9a472b8c52ef83375fe15 AS postgrest
|
FROM ubuntu:noble@sha256:80dd3c3b9c6cecb9f1667e9290b3bc61b78c2678c02cbdae5f0fea92cc6734ab AS postgrest
|
||||||
|
|
||||||
RUN apt-get update -y \
|
RUN apt-get update -y \
|
||||||
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
|
&& apt install -y --no-install-recommends libpq-dev zlib1g-dev jq gcc libnuma-dev \
|
||||||
@@ -8,6 +10,7 @@ RUN apt-get update -y \
|
|||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY postgrest /usr/bin/postgrest
|
COPY postgrest /usr/bin/postgrest
|
||||||
|
RUN chmod +x /usr/bin/postgrest
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||

|

|
||||||
|
|
||||||
[](https://www.patreon.com/postgrest)
|
[](https://www.patreon.com/postgrest)
|
||||||
[](https://www.paypal.me/postgrest)
|
|
||||||
[](https://gitter.im/begriffs/postgrest)
|
|
||||||
[](http://postgrest.org)
|
[](http://postgrest.org)
|
||||||
[](https://hub.docker.com/r/postgrest/postgrest/)
|
[](https://hub.docker.com/r/postgrest/postgrest/)
|
||||||
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
||||||
@@ -145,11 +143,7 @@ and the [API guide](http://postgrest.org/en/stable/api.html).
|
|||||||
|
|
||||||
## Supporting development
|
## Supporting development
|
||||||
|
|
||||||
You can help PostgREST ongoing maintenance and development by:
|
You can help PostgREST ongoing maintenance and development by making a regular donation through Patreon https://www.patreon.com/postgrest
|
||||||
|
|
||||||
- Making a regular donation through Patreon https://www.patreon.com/postgrest
|
|
||||||
|
|
||||||
- Alternatively, you can make a one-time donation via Paypal https://www.paypal.me/postgrest
|
|
||||||
|
|
||||||
Every donation will be spent on making PostgREST better for the whole community.
|
Every donation will be spent on making PostgREST better for the whole community.
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
index-state: hackage.haskell.org 2024-05-17T23:41:49Z
|
index-state: hackage.haskell.org 2025-02-01T14:59:33Z
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
# PostgREST
|
# PostgREST
|
||||||
|
|
||||||
[](https://gitter.im/begriffs/postgrest)
|
|
||||||
[](https://www.patreon.com/postgrest)
|
[](https://www.patreon.com/postgrest)
|
||||||
[](https://www.paypal.me/postgrest)
|
|
||||||
[](http://postgrest.org)
|
[](http://postgrest.org)
|
||||||
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
||||||
|
|
||||||
|
|||||||
@@ -5,35 +5,20 @@ The ER diagrams were created with https://github.com/BurntSushi/erd/.
|
|||||||
You can go download erd from https://github.com/BurntSushi/erd/releases and then do:
|
You can go download erd from https://github.com/BurntSushi/erd/releases and then do:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./erd_static-x86-64 -i film.er -o ../_static/film.png
|
./erd_static-x86-64 -i ./er/film.er -o ../_static/film.png
|
||||||
```
|
```
|
||||||
|
|
||||||
The fonts used belong to the GNU FreeFont family. You can download them here: http://ftp.gnu.org/gnu/freefont/
|
The fonts used belong to the GNU FreeFont family. You can download them here: http://ftp.gnu.org/gnu/freefont/
|
||||||
|
|
||||||
## LaTeX
|
## UML
|
||||||
|
|
||||||
The schema structure diagram is done with LaTeX. You can use a GUI like https://www.mathcha.io/editor to create the .tex file.
|
The UML diagrams are created with https://plantuml.com/.
|
||||||
|
|
||||||
Then use this command to generate the png file.
|
PlantUML only creates one diagram per file.
|
||||||
|
That's why we need to create another one for dark mode.
|
||||||
|
For example, for the file [uml/arch.uml](uml/arch.uml) there's [uml/dark/arch-dark.uml](uml/dark/arch-dark.uml) which includes the first one:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
postgrest-docs-render
|
plantuml -tsvg uml/arch.uml -o ../../_static
|
||||||
```
|
plantuml -tsvg -darkmode uml/dark/arch-dark.uml -o ../../../_static
|
||||||
|
|
||||||
LaTeX is used because it's a tweakable plain text format.
|
|
||||||
|
|
||||||
You can install the full latex suite with `nix`:
|
|
||||||
|
|
||||||
```
|
|
||||||
nix-env -iA texlive.combined.scheme-full
|
|
||||||
```
|
|
||||||
|
|
||||||
To tweak the file with a live reload environment use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# open the pdf(zathura used as an example)
|
|
||||||
zathura db.pdf &
|
|
||||||
|
|
||||||
# live reload with entr
|
|
||||||
echo db.tex | entr pdflatex --shell-escape -halt-on-error db.tex
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
\documentclass[convert]{standalone}
|
|
||||||
\usepackage{amsmath}
|
|
||||||
\usepackage{tikz}
|
|
||||||
\usepackage{mathdots}
|
|
||||||
\usepackage{yhmath}
|
|
||||||
\usepackage{cancel}
|
|
||||||
\usepackage{color}
|
|
||||||
\usepackage{siunitx}
|
|
||||||
\usepackage{array}
|
|
||||||
\usepackage{multirow}
|
|
||||||
\usepackage{amssymb}
|
|
||||||
\usepackage{gensymb}
|
|
||||||
\usepackage{tabularx}
|
|
||||||
\usepackage{booktabs}
|
|
||||||
\usetikzlibrary{fadings}
|
|
||||||
\usetikzlibrary{patterns}
|
|
||||||
\usetikzlibrary{shadows.blur}
|
|
||||||
\usetikzlibrary{shapes}
|
|
||||||
|
|
||||||
\begin{document}
|
|
||||||
|
|
||||||
\newcommand\customScale{0.35}
|
|
||||||
|
|
||||||
\begin{tikzpicture}[x=0.75pt,y=0.75pt,yscale=-1,xscale=1, scale=\customScale, every node/.style={scale=\customScale}]
|
|
||||||
|
|
||||||
%Shape: Can [id:dp7234864758664346]
|
|
||||||
\draw [fill={rgb, 255:red, 47; green, 97; blue, 144 } ,fill opacity=1 ] (497.5,51.5) -- (497.5,255.5) .. controls (497.5,275.66) and (423.18,292) .. (331.5,292) .. controls (239.82,292) and (165.5,275.66) .. (165.5,255.5) -- (165.5,51.5) .. controls (165.5,31.34) and (239.82,15) .. (331.5,15) .. controls (423.18,15) and (497.5,31.34) .. (497.5,51.5) .. controls (497.5,71.66) and (423.18,88) .. (331.5,88) .. controls (239.82,88) and (165.5,71.66) .. (165.5,51.5) ;
|
|
||||||
%Shape: Rectangle [id:dp7384065579958246]
|
|
||||||
\draw [fill={rgb, 255:red, 236; green, 227; blue, 227 } ,fill opacity=1 ] (189,115) -- (252.5,115) -- (252.5,155) -- (189,155) -- cycle ;
|
|
||||||
%Shape: Rectangle [id:dp24763906430298177]
|
|
||||||
\draw [fill={rgb, 255:red, 236; green, 227; blue, 227 } ,fill opacity=1 ] (292,118) -- (362,118) -- (362,158) -- (292,158) -- cycle ;
|
|
||||||
%Shape: Rectangle [id:dp3775601612537265]
|
|
||||||
\draw [fill={rgb, 255:red, 236; green, 227; blue, 227 } ,fill opacity=1 ] (397,114) -- (467,114) -- (467,154) -- (397,154) -- cycle ;
|
|
||||||
%Shape: Rectangle [id:dp7071457022893852]
|
|
||||||
\draw [fill={rgb, 255:red, 248; green, 231; blue, 28 } ,fill opacity=1 ] (269,199) -- (397.5,199) -- (397.5,273) -- (269,273) -- cycle ;
|
|
||||||
%Straight Lines [id:da8846759047437789]
|
|
||||||
\draw (268,234) -- (226.44,155.77) ;
|
|
||||||
\draw [shift={(225.5,154)}, rotate = 422.02] [color={rgb, 255:red, 0; green, 0; blue, 0 } ][line width=0.75] (10.93,-3.29) .. controls (6.95,-1.4) and (3.31,-0.3) .. (0,0) .. controls (3.31,0.3) and (6.95,1.4) .. (10.93,3.29) ;
|
|
||||||
%Straight Lines [id:da6908444738113828]
|
|
||||||
\draw (309.5,198) -- (307.6,161) ;
|
|
||||||
\draw [shift={(307.5,159)}, rotate = 447.06] [color={rgb, 255:red, 0; green, 0; blue, 0 } ][line width=0.75] (10.93,-3.29) .. controls (6.95,-1.4) and (3.31,-0.3) .. (0,0) .. controls (3.31,0.3) and (6.95,1.4) .. (10.93,3.29) ;
|
|
||||||
%Straight Lines [id:da7168757864413169]
|
|
||||||
\draw (398.5,233) -- (431.72,154.84) ;
|
|
||||||
\draw [shift={(432.5,153)}, rotate = 473.03] [color={rgb, 255:red, 0; green, 0; blue, 0 } ][line width=0.75] (10.93,-3.29) .. controls (6.95,-1.4) and (3.31,-0.3) .. (0,0) .. controls (3.31,0.3) and (6.95,1.4) .. (10.93,3.29) ;
|
|
||||||
%Up Down Arrow [id:dp14059754167108496]
|
|
||||||
\draw [fill={rgb, 255:red, 126; green, 211; blue, 33 } ,fill opacity=1 ] (312.5,288.5) -- (330,273) -- (347.5,288.5) -- (338.75,288.5) -- (338.75,319.5) -- (347.5,319.5) -- (330,335) -- (312.5,319.5) -- (321.25,319.5) -- (321.25,288.5) -- cycle ;
|
|
||||||
|
|
||||||
% Text Node
|
|
||||||
\draw (201,129) node [anchor=north west][inner sep=0.75pt] [align=left] {tables};
|
|
||||||
% Text Node
|
|
||||||
\draw (307,130) node [anchor=north west][inner sep=0.75pt] [align=left ] {tables};
|
|
||||||
% Text Node
|
|
||||||
\draw (414,127) node [anchor=north west][inner sep=0.75pt] [align=left] {tables};
|
|
||||||
% Text Node
|
|
||||||
\draw (300,203) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 0; green, 0; blue, 0 } ,opacity=1 ] [align=center] { \\ views \\ + \\ \ \ functions};
|
|
||||||
|
|
||||||
% Text Node
|
|
||||||
\draw (322,178) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{api}};
|
|
||||||
% Text Node
|
|
||||||
\draw (190,97) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{internal}};
|
|
||||||
% Text Node
|
|
||||||
\draw (300,99) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{private}};
|
|
||||||
% Text Node
|
|
||||||
\draw (417,101) node [anchor=north west][inner sep=0.75pt] [color={rgb, 255:red, 255; green, 255; blue, 255 } ,opacity=1 ] [align=left] {\large\textbf{core}};
|
|
||||||
% Text Node
|
|
||||||
\draw (358,306) node [anchor=north west][inner sep=0.75pt] [align=left] {REST};
|
|
||||||
|
|
||||||
\end{tikzpicture}
|
|
||||||
|
|
||||||
|
|
||||||
\end{document}
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
@startuml
|
@startuml
|
||||||
|
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
|
||||||
package "PostgREST" {
|
package "PostgREST" {
|
||||||
() HTTP as HTTPAPI
|
() HTTP as HTTPAPI
|
||||||
HTTPAPI - [Auth]
|
HTTPAPI - [Auth]
|
||||||
@@ -14,11 +16,9 @@ package "PostgREST" {
|
|||||||
[Admin] -r- () HTTPADMIN
|
[Admin] -r- () HTTPADMIN
|
||||||
[Config] -l- () CLI
|
[Config] -l- () CLI
|
||||||
|
|
||||||
[Config] <-l~ Listener
|
[Config] <-r~ Listener
|
||||||
|
|
||||||
HTTPADMIN -[hidden]r- CLI
|
HTTPADMIN -[hidden]r- CLI
|
||||||
Listener -[hidden]r- [Schema Cache]
|
|
||||||
Listener -[hidden]r- [Config]
|
|
||||||
[Schema Cache] -l[hidden]- [Config]
|
[Schema Cache] -l[hidden]- [Config]
|
||||||
[Schema Cache] -l[hidden]- [Admin]
|
[Schema Cache] -l[hidden]- [Admin]
|
||||||
[Schema Cache] -l[hidden]- CLI
|
[Schema Cache] -l[hidden]- CLI
|
||||||
@@ -26,16 +26,16 @@ package "PostgREST" {
|
|||||||
|
|
||||||
|
|
||||||
database "PostgreSQL" {
|
database "PostgreSQL" {
|
||||||
node "API schema" as API {
|
|
||||||
rectangle "Functions, Views"
|
|
||||||
}
|
|
||||||
node Authorization {
|
node Authorization {
|
||||||
rectangle "Roles, GRANT, RLS"
|
rectangle "Roles, GRANT, RLS"
|
||||||
}
|
}
|
||||||
|
node "API schema" as API {
|
||||||
|
rectangle "Functions, Views"
|
||||||
|
}
|
||||||
rectangle "Tables, extensions" as tbs
|
rectangle "Tables, extensions" as tbs
|
||||||
API -d- tbs
|
API -d- tbs
|
||||||
|
|
||||||
API -[hidden]l- Authorization
|
API -l[hidden]- Authorization
|
||||||
}
|
}
|
||||||
|
|
||||||
:user:
|
:user:
|
||||||
@@ -67,4 +67,18 @@ end note
|
|||||||
note top of Listener
|
note top of Listener
|
||||||
LISTEN session
|
LISTEN session
|
||||||
end note
|
end note
|
||||||
|
|
||||||
|
url of Admin is [[../references/admin_server.html#admin-server]]
|
||||||
|
url of API is [[../explanations/schema_isolation.html]]
|
||||||
|
url of Auth is [[../references/auth.html#authn]]
|
||||||
|
url of Authorization is [[../explanations/db_authz.html]]
|
||||||
|
url of CLI is [[../references/cli.html#cli]]
|
||||||
|
url of "Connection Pool" is [[../references/connection_pool.html]]
|
||||||
|
url of Config is [[../references/configuration.html#configuration]]
|
||||||
|
url of HTTPADMIN is [[https://aosabook.org/en/posa/warp.html]]
|
||||||
|
url of HTTPAPI is [[https://aosabook.org/en/posa/warp.html]]
|
||||||
|
url of Listener is [[../references/listener.html#listener]]
|
||||||
|
url of Proxy is [[../explanations/nginx.html]]
|
||||||
|
url of "Schema Cache" is [[../references/schema_cache.html#schema-cache]]
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@startuml
|
||||||
|
!include ../arch.uml
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@startuml
|
||||||
|
!include ../sch-iso.uml
|
||||||
|
@enduml
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
@startuml
|
||||||
|
|
||||||
|
skinparam backgroundColor transparent
|
||||||
|
skinparam linetype ortho
|
||||||
|
|
||||||
|
skinparam node {
|
||||||
|
backgroundColor transparent
|
||||||
|
borderThickness 1
|
||||||
|
}
|
||||||
|
|
||||||
|
database "PostgreSQL" {
|
||||||
|
node public {
|
||||||
|
rectangle tables_public as "tables"
|
||||||
|
}
|
||||||
|
|
||||||
|
node extensions as "**extensions**" {
|
||||||
|
}
|
||||||
|
|
||||||
|
node API as "<size:20>api" {
|
||||||
|
rectangle vf_api as "views + functions"
|
||||||
|
}
|
||||||
|
|
||||||
|
tables_public <-- vf_api
|
||||||
|
extensions <-- vf_api
|
||||||
|
}
|
||||||
|
|
||||||
|
vf_api <-[thickness=3]-> () PostgREST
|
||||||
|
|
||||||
|
@enduml
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB |
Vendored
+1
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 24 KiB |
Vendored
+4
@@ -149,3 +149,7 @@ html[data-theme="dark"] .img-translucent img {
|
|||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.svg-container-md {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB |
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="us-ascii" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="411px" preserveAspectRatio="none" style="width:315px;height:411px;" version="1.1" viewBox="0 0 315 411" width="315px" zoomAndPan="magnify"><defs/><g><!--cluster PostgreSQL--><g id="cluster_PostgreSQL"><path d="M6,16 C6,6 152,6 152,6 C152,6 298,6 298,16 L298,309.14 C298,319.14 152,319.14 152,319.14 C152,319.14 6,319.14 6,309.14 L6,16 " fill="none" style="stroke:#E7E7E7;stroke-width:1.0;"/><path d="M6,16 C6,26 152,26 152,26 C152,26 298,26 298,16 " fill="none" style="stroke:#E7E7E7;stroke-width:1.0;"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="81.375" x="111.3125" y="42.9659">PostgreSQL</text></g><!--cluster public--><g id="cluster_public"><polygon fill="none" points="30,77,40,67,150,67,150,155.07,140,165.07,30,165.07,30,77" style="stroke:#E7E7E7;stroke-width:1.0;"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="140" x2="150" y1="77" y2="67"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="30" x2="140" y1="77" y2="77"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="140" x2="140" y1="77" y2="165.07"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="44.625" x="63.6875" y="94.9659">public</text></g><!--cluster API--><g id="cluster_API"><polygon fill="none" points="68,199.07,78,189.07,236,189.07,236,285.14,226,295.14,68,295.14,68,199.07" style="stroke:#E7E7E7;stroke-width:1.0;"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="226" x2="236" y1="199.07" y2="189.07"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="68" x2="226" y1="199.07" y2="199.07"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="226" x2="226" y1="199.07" y2="295.14"/><text fill="#FFFFFF" font-family="sans-serif" font-size="20" font-weight="bold" lengthAdjust="spacing" textLength="29.875" x="133.0625" y="223.45">api</text></g><!--entity tables_public--><g id="elem_tables_public"><rect fill="#313139" height="39.0679" rx="2.5" ry="2.5" style="stroke:#E7E7E7;stroke-width:0.5;" width="61" x="69.5" y="110"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="41" x="79.5" y="134.9659">tables</text></g><!--entity extensions--><g id="elem_extensions"><polygon fill="none" points="165.81,115,175.81,105,282.185,105,282.185,144.0679,272.185,154.0679,165.81,154.0679,165.81,115" style="stroke:#E7E7E7;stroke-width:1.0;"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="272.185" x2="282.185" y1="115" y2="105"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="165.81" x2="272.185" y1="115" y2="115"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="272.185" x2="272.185" y1="115" y2="154.0679"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="76.375" x="180.81" y="139.9659">extensions</text></g><!--entity vf_api--><g id="elem_vf_api"><rect fill="#313139" height="39.0679" rx="2.5" ry="2.5" style="stroke:#E7E7E7;stroke-width:0.5;" width="136" x="84" y="240.07"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="116" x="94" y="265.0359">views + functions</text></g><!--entity PostgREST--><g id="elem_PostgREST"><ellipse cx="152" cy="371.14" fill="#313139" rx="8" ry="8" style="stroke:#E7E7E7;stroke-width:0.5;"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="70" x="117" y="403.1059">PostgREST</text></g><!--reverse link tables_public to vf_api--><g id="link_tables_public_vf_api"><path d="M107.25,155.29 C107.25,179.74 107.25,215.13 107.25,239.67 " fill="none" id="tables_public-backto-vf_api" style="stroke:#E7E7E7;stroke-width:1.0;"/><polygon fill="#E7E7E7" points="107.25,149.29,103.25,158.29,107.25,154.29,111.25,158.29,107.25,149.29" style="stroke:#E7E7E7;stroke-width:1.0;"/></g><!--reverse link extensions to vf_api--><g id="link_extensions_vf_api"><path d="M192.91,160.31 C192.91,185.07 192.91,216.68 192.91,239.61 " fill="none" id="extensions-backto-vf_api" style="stroke:#E7E7E7;stroke-width:1.0;"/><polygon fill="#E7E7E7" points="192.91,154.31,188.91,163.31,192.91,159.31,196.91,163.31,192.91,154.31" style="stroke:#E7E7E7;stroke-width:1.0;"/></g><!--link vf_api to PostgREST--><g id="link_vf_api_PostgREST"><path d="M152,285.55 C152,309.98 152,338.34 152,356.18 " fill="none" id="vf_api-PostgREST" style="stroke:#E7E7E7;stroke-width:3.0;"/><polygon fill="#E7E7E7" points="152,279.55,148,288.55,152,284.55,156,288.55,152,279.55" style="stroke:#E7E7E7;stroke-width:3.0;"/><polygon fill="#E7E7E7" points="152,362.18,156,353.18,152,357.18,148,353.18,152,362.18" style="stroke:#E7E7E7;stroke-width:3.0;"/></g><!--SRC=[KypCIyufJKbLqDFJBqxEqCqipjShpSq10000]--></g></svg>
|
||||||
|
After Width: | Height: | Size: 4.8 KiB |
Vendored
+1
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.1 KiB |
+1
-4
@@ -50,7 +50,7 @@ source_suffix = ".rst"
|
|||||||
master_doc = "index"
|
master_doc = "index"
|
||||||
|
|
||||||
# This is overriden by readthedocs with the version tag anyway
|
# This is overriden by readthedocs with the version tag anyway
|
||||||
version = "devel"
|
version = "12.2"
|
||||||
# To avoid repetition in <title> we set this to an empty string.
|
# To avoid repetition in <title> we set this to an empty string.
|
||||||
release = ""
|
release = ""
|
||||||
|
|
||||||
@@ -295,10 +295,7 @@ def setup(app):
|
|||||||
# taken from https://github.com/sphinx-doc/sphinx/blob/82dad44e5bd3776ecb6fd8ded656bc8151d0e63d/sphinx/util/requests.py#L42
|
# taken from https://github.com/sphinx-doc/sphinx/blob/82dad44e5bd3776ecb6fd8ded656bc8151d0e63d/sphinx/util/requests.py#L42
|
||||||
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0"
|
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:25.0) Gecko/20100101 Firefox/25.0"
|
||||||
|
|
||||||
# TODO: these pages are returning 403 right now. Verify if they can be fixed by addressing the issue:
|
|
||||||
# https://github.com/PostgREST/postgrest/issues/3544
|
|
||||||
linkcheck_ignore = [
|
linkcheck_ignore = [
|
||||||
r"https://stackoverflow.com/",
|
|
||||||
r"https://www.patreon.com/postgrest",
|
r"https://www.patreon.com/postgrest",
|
||||||
r"https://blog.frankel.ch/poor-man-api",
|
r"https://blog.frankel.ch/poor-man-api",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ External Notification
|
|||||||
|
|
||||||
These are PostgreSQL bridges that propagate LISTEN/NOTIFY to external queues for further processing. This allows functions to initiate actions outside the database such as sending emails.
|
These are PostgreSQL bridges that propagate LISTEN/NOTIFY to external queues for further processing. This allows functions to initiate actions outside the database such as sending emails.
|
||||||
|
|
||||||
|
* `pg-notify-stdout <https://github.com/mkleczek/pg-notify-stdout>`_ - writes notifications to standard output (use in shell scripts etc.)
|
||||||
* `pg-notify-webhook <https://github.com/vbalasu/pg-notify-webhook>`_ - trigger webhooks from PostgreSQL's LISTEN/NOTIFY
|
* `pg-notify-webhook <https://github.com/vbalasu/pg-notify-webhook>`_ - trigger webhooks from PostgreSQL's LISTEN/NOTIFY
|
||||||
* `pgsql-listen-exchange <https://github.com/gmr/pgsql-listen-exchange>`_ - RabbitMQ
|
* `pgsql-listen-exchange <https://github.com/gmr/pgsql-listen-exchange>`_ - RabbitMQ
|
||||||
* `postgres-websockets <https://github.com/diogob/postgres-websockets>`_ - expose web sockets for PostgreSQL's LISTEN/NOTIFY
|
* `postgres-websockets <https://github.com/diogob/postgres-websockets>`_ - expose web sockets for PostgreSQL's LISTEN/NOTIFY
|
||||||
|
|||||||
@@ -6,7 +6,22 @@ This page describes the architecture of PostgREST.
|
|||||||
Bird's Eye View
|
Bird's Eye View
|
||||||
===============
|
===============
|
||||||
|
|
||||||
.. image:: ../_static/arch.png
|
You can click on the components to navigate to their respective documentation.
|
||||||
|
|
||||||
|
.. container:: img-dark
|
||||||
|
|
||||||
|
.. See https://github.com/sphinx-doc/sphinx/issues/2240#issuecomment-187366626
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<object width="100%" data="../_static/arch-dark.svg" type="image/svg+xml"></object>
|
||||||
|
|
||||||
|
.. container:: img-light
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<object width="100%" data="../_static/arch.svg" type="image/svg+xml"></object>
|
||||||
|
|
||||||
|
|
||||||
Code Map
|
Code Map
|
||||||
========
|
========
|
||||||
@@ -77,5 +92,4 @@ The HTTP server is provided by `Warp <https://aosabook.org/en/posa/warp.html>`_.
|
|||||||
Listener
|
Listener
|
||||||
--------
|
--------
|
||||||
|
|
||||||
`Listener.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Listener.hs>`_ is in charge of maintaining a `LISTEN session <https://www.postgresql.org/docs/current/sql-listen.html>`_
|
`Listener.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Listener.hs>`_ is in charge of the :ref:`listener`.
|
||||||
that keeps the :ref:`schema_cache` and the :ref:`in_db_config` up to date.
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Installation
|
Installation
|
||||||
############
|
############
|
||||||
|
|
||||||
The release page has `pre-compiled binaries for macOS, Windows, Linux and FreeBSD <https://github.com/PostgREST/postgrest/releases/latest>`_ .
|
The release page has `pre-compiled binaries for macOS, Windows, Linux and FreeBSD <https://github.com/PostgREST/postgrest/releases/latest>`_.
|
||||||
The Linux binary is a static executable that can be run on any Linux distribution.
|
The Linux binary is a static executable that can be run on any Linux distribution.
|
||||||
|
|
||||||
You can also use your OS package manager.
|
You can also use your OS package manager.
|
||||||
@@ -190,7 +190,7 @@ When a pre-built binary does not exist for your system you can build the project
|
|||||||
|
|
||||||
You can build PostgREST from source with `Stack <https://github.com/commercialhaskell/stack>`_. It will install any necessary Haskell dependencies on your system.
|
You can build PostgREST from source with `Stack <https://github.com/commercialhaskell/stack>`_. It will install any necessary Haskell dependencies on your system.
|
||||||
|
|
||||||
* `Install Stack <https://docs.haskellstack.org/en/stable/README/#how-to-install-stack>`_ for your platform
|
* `Install Stack <https://docs.haskellstack.org/en/stable/#how-to-install-stack>`_ for your platform
|
||||||
* Install Library Dependencies
|
* Install Library Dependencies
|
||||||
|
|
||||||
===================== =======================================
|
===================== =======================================
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
.. note::
|
|
||||||
|
|
||||||
This page is a work in progress.
|
|
||||||
|
|
||||||
.. _schema_isolation:
|
.. _schema_isolation:
|
||||||
|
|
||||||
Schema Isolation
|
Schema Isolation
|
||||||
@@ -12,6 +8,18 @@ A PostgREST instance exposes all the tables, views, and functions of a single `P
|
|||||||
It is recommended that you don't expose tables on your API schema. Instead expose views and functions which insulate the internal details from the outside world.
|
It is recommended that you don't expose tables on your API schema. Instead expose views and functions which insulate the internal details from the outside world.
|
||||||
This allows you to change the internals of your schema and maintain backwards compatibility. It also keeps your code easier to refactor, and provides a natural way to do API versioning.
|
This allows you to change the internals of your schema and maintain backwards compatibility. It also keeps your code easier to refactor, and provides a natural way to do API versioning.
|
||||||
|
|
||||||
.. container:: img-translucent
|
.. container:: svg-container-md
|
||||||
|
|
||||||
.. image:: ../_static/db.png
|
.. container:: img-dark
|
||||||
|
|
||||||
|
.. See https://github.com/sphinx-doc/sphinx/issues/2240#issuecomment-187366626
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<object width="100%" data="../_static/sch-iso-dark.svg" type="image/svg+xml"></object>
|
||||||
|
|
||||||
|
.. container:: img-light
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<object width="100%" data="../_static/sch-iso.svg" type="image/svg+xml"></object>
|
||||||
|
|||||||
+2
-9
@@ -16,15 +16,9 @@ PostgREST Documentation
|
|||||||
.. image:: https://img.shields.io/docker/pulls/postgrest/postgrest.svg
|
.. image:: https://img.shields.io/docker/pulls/postgrest/postgrest.svg
|
||||||
:target: https://hub.docker.com/r/postgrest/postgrest/
|
:target: https://hub.docker.com/r/postgrest/postgrest/
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg
|
|
||||||
:target: https://gitter.im/begriffs/postgrest
|
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/Donate-Patreon-orange.svg?colorB=F96854
|
.. image:: https://img.shields.io/badge/Donate-Patreon-orange.svg?colorB=F96854
|
||||||
:target: https://www.patreon.com/postgrest
|
:target: https://www.patreon.com/postgrest
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/Donate-PayPal-green.svg
|
|
||||||
:target: https://www.paypal.com/paypalme/postgrest
|
|
||||||
|
|
||||||
|
|
|
|
||||||
|
|
||||||
PostgREST is a standalone web server that turns your PostgreSQL database directly into a RESTful API. The structural constraints and permissions in the database determine the API endpoints and operations.
|
PostgREST is a standalone web server that turns your PostgreSQL database directly into a RESTful API. The structural constraints and permissions in the database determine the API endpoints and operations.
|
||||||
@@ -113,7 +107,7 @@ PostgREST has a focused scope. It works well with other tools like Nginx. This f
|
|||||||
Getting Support
|
Getting Support
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
The project has a friendly and growing community. For discussions, use the Github `discussions page <https://github.com/PostgREST/postgrest/discussions>`_ or join our `chat room <https://gitter.im/begriffs/postgrest>`_. You can also report or search for bugs/features on the Github `issues <https://github.com/PostgREST/postgrest/issues>`_ page.
|
The project has a friendly and growing community. For discussions, use the Github `discussions page <https://github.com/PostgREST/postgrest/discussions>`_. You can also report or search for bugs/features on the Github `issues <https://github.com/PostgREST/postgrest/issues>`_ page.
|
||||||
|
|
||||||
Release Notes
|
Release Notes
|
||||||
-------------
|
-------------
|
||||||
@@ -218,13 +212,12 @@ Here are some companies that use PostgREST in production.
|
|||||||
* `Datrium <https://www.datrium.com>`_
|
* `Datrium <https://www.datrium.com>`_
|
||||||
* `Drip Depot <https://www.dripdepot.com>`_
|
* `Drip Depot <https://www.dripdepot.com>`_
|
||||||
* `Image-charts <https://www.image-charts.com>`_
|
* `Image-charts <https://www.image-charts.com>`_
|
||||||
* `Moat <https://www.oracle.com/advertising/measurement/>`_
|
* `Moat <https://www.oracle.com/advertising/>`_
|
||||||
* `Netwo <https://www.netwo.io>`_
|
* `Netwo <https://www.netwo.io>`_
|
||||||
* `Nimbus <https://www.nimbusfacility.com/sg/home>`_
|
* `Nimbus <https://www.nimbusfacility.com/sg/home>`_
|
||||||
- See how Nimbus uses PostgREST in `Paul Copplestone's blog post <https://paul.copplest.one/blog/nimbus-tech-2019-04.html>`_.
|
- See how Nimbus uses PostgREST in `Paul Copplestone's blog post <https://paul.copplest.one/blog/nimbus-tech-2019-04.html>`_.
|
||||||
* `OpenBooking <https://openbooking.ch>`_
|
* `OpenBooking <https://openbooking.ch>`_
|
||||||
* `Redsmin <https://www.redsmin.com>`_
|
* `Redsmin <https://www.redsmin.com>`_
|
||||||
* `Sompani <https://www.sompani.com>`_
|
|
||||||
* `Supabase <https://supabase.com>`_
|
* `Supabase <https://supabase.com>`_
|
||||||
|
|
||||||
.. Failing links
|
.. Failing links
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
Functions as RPC
|
Functions as RPC
|
||||||
================
|
================
|
||||||
|
|
||||||
*"A single resource can be the equivalent of a database function, with the power to abstract state changes over any number of storage items"* -- `Roy T. Fielding <http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven#comment-743>`_
|
*"A single resource can be the equivalent of a database function, with the power to abstract state changes over any number of storage items"* -- `Roy T. Fielding <https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven#comment-743>`_
|
||||||
|
|
||||||
Functions can perform any operation allowed by PostgreSQL (read data, modify data, :ref:`raise errors <raise_error>`, and even DDL operations). Every function in the :ref:`exposed schema <schemas>` and accessible by the :ref:`active database role <roles>` is executable under the :code:`/rpc` prefix.
|
Functions can perform any operation allowed by PostgreSQL (read data, modify data, :ref:`raise errors <raise_error>`, and even DDL operations). Every function in the :ref:`exposed schema <schemas>` and accessible by the :ref:`active database role <roles>` is executable under the :code:`/rpc` prefix.
|
||||||
|
|
||||||
@@ -71,6 +71,40 @@ The function parameter names match the JSON object keys in the POST case, for th
|
|||||||
|
|
||||||
.. _function_single_json:
|
.. _function_single_json:
|
||||||
|
|
||||||
|
Functions with an array of JSON objects
|
||||||
|
----------------------------------------------
|
||||||
|
|
||||||
|
If you want to pass multiple JSON objects to a Postgres function (an array of objects), you can create a function with a parameter of type ``json`` or ``jsonb``.
|
||||||
|
|
||||||
|
Within the curl request, this JSON must be embedded in an object where they key matches the same name as the function's ``json`` or ``jsonb`` parameter.
|
||||||
|
This will allow you to loop over the array of JSON objects within the Postgres function.
|
||||||
|
|
||||||
|
This practice may allow you to reduce the number of ``curl`` requests required to accomplish a task.
|
||||||
|
|
||||||
|
For instance, assume we have created this function in the database.
|
||||||
|
|
||||||
|
.. code-block:: postgres
|
||||||
|
|
||||||
|
CREATE FUNCTION update_data(p_json jsonb)
|
||||||
|
RETURNS void AS $$
|
||||||
|
DECLARE
|
||||||
|
json_item json;
|
||||||
|
BEGIN
|
||||||
|
FOR json_item IN SELECT jsonb_array_elements(p_json) LOOP
|
||||||
|
UPDATE data_table SET data_text_column = (json_item->>'data_text')::text
|
||||||
|
WHERE data_int_column = (json_item->>'data_int')::integer;
|
||||||
|
END LOOP;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE SQL IMMUTABLE;
|
||||||
|
|
||||||
|
A ``curl`` request using the POST method would look like the following:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
curl "http://localhost:3000/rpc/update_data" \
|
||||||
|
-X POST -H "Content-Type: application/json" \
|
||||||
|
-d '{ "p_json": [ { "data_text": "one", "data_int": "1" }, { "data_text": "two", "data_int": "2" } ] }'
|
||||||
|
|
||||||
Functions with a single unnamed JSON parameter
|
Functions with a single unnamed JSON parameter
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ cs :code:`@>` contains e.g. :code:`?tags=cs.{example,
|
|||||||
cd :code:`<@` contained in e.g. :code:`?values=cd.{1,2,3}`
|
cd :code:`<@` contained in e.g. :code:`?values=cd.{1,2,3}`
|
||||||
ov :code:`&&` overlap (have points in common), e.g. :code:`?period=ov.[2017-01-01,2017-06-30]` –
|
ov :code:`&&` overlap (have points in common), e.g. :code:`?period=ov.[2017-01-01,2017-06-30]` –
|
||||||
also supports array types, use curly braces instead of square brackets e.g.
|
also supports array types, use curly braces instead of square brackets e.g.
|
||||||
:code: `?arr=ov.{1,3}`
|
:code:`?arr=ov.{1,3}`
|
||||||
sl :code:`<<` strictly left of, e.g. :code:`?range=sl.(1,10)`
|
sl :code:`<<` strictly left of, e.g. :code:`?range=sl.(1,10)`
|
||||||
sr :code:`>>` strictly right of
|
sr :code:`>>` strictly right of
|
||||||
nxr :code:`&<` does not extend to the right of, e.g. :code:`?range=nxr.(1,10)`
|
nxr :code:`&<` does not extend to the right of, e.g. :code:`?range=nxr.(1,10)`
|
||||||
@@ -623,18 +623,34 @@ You can make an upsert with :code:`POST` and the :code:`Prefer: resolution=merge
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
curl "http://localhost:3000/employees" \
|
curl "http://localhost:3000/products" \
|
||||||
-X POST -H "Content-Type: application/json" \
|
-X POST -H "Content-Type: application/json" \
|
||||||
-H "Prefer: resolution=merge-duplicates" \
|
-H "Prefer: resolution=merge-duplicates" \
|
||||||
-d @- << EOF
|
-d @- << EOF
|
||||||
[
|
[
|
||||||
{ "id": 1, "name": "Old employee 1", "salary": 30000 },
|
{ "sku": "CL2031", "name": "Existing T-shirt", "price": 35 },
|
||||||
{ "id": 2, "name": "Old employee 2", "salary": 42000 },
|
{ "sku": "CL2040", "name": "Existing Hoodie", "price": 60 },
|
||||||
{ "id": 3, "name": "New employee 3", "salary": 50000 }
|
{ "sku": "AC1022", "name": "New Cap", "price": 30 }
|
||||||
]
|
]
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
By default, upsert operates based on the primary key columns, you must specify all of them. You can also choose to ignore the duplicates with :code:`Prefer: resolution=ignore-duplicates`. This works best when the primary key is natural, but it's also possible to use it if the primary key is surrogate (example: "id serial primary key"). For more details read `this issue <https://github.com/PostgREST/postgrest/issues/1118>`_.
|
By default, upsert operates based on the primary key columns, so you must specify all of them.
|
||||||
|
You can also choose to ignore the duplicates with :code:`Prefer: resolution=ignore-duplicates`.
|
||||||
|
Upsert works best when the primary key is natural (e.g. ``sku``).
|
||||||
|
However, it can work with surrogate primary keys (e.g. ``id serial primary key``), if you also do a :ref:`bulk_insert_default`:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
curl "http://localhost:3000/employees?colums=id,name,salary" \
|
||||||
|
-X POST -H "Content-Type: application/json" \
|
||||||
|
-H "Prefer: resolution=merge-duplicates, missing=default" \
|
||||||
|
-d @- << EOF
|
||||||
|
[
|
||||||
|
{ "id": 1, "name": "Existing employee 1", "salary": 30000 },
|
||||||
|
{ "id": 2, "name": "Existing employee 2", "salary": 42000 },
|
||||||
|
{ "name": "New employee 3", "salary": 50000 }
|
||||||
|
]
|
||||||
|
EOF
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
After creating a table or changing its primary key, you must refresh PostgREST schema cache for upsert to work properly. To learn how to refresh the cache see :ref:`schema_reloading`.
|
After creating a table or changing its primary key, you must refresh PostgREST schema cache for upsert to work properly. To learn how to refresh the cache see :ref:`schema_reloading`.
|
||||||
|
|||||||
@@ -135,14 +135,12 @@ To reload the configuration via signal, send a SIGUSR2 signal to the server proc
|
|||||||
Configuration Reload with NOTIFY
|
Configuration Reload with NOTIFY
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
To reload the configuration from within the database, you can use a NOTIFY command.
|
To reload the configuration from within the database, you can use the ``NOTIFY`` command. See :ref:`listener`.
|
||||||
|
|
||||||
.. code:: postgresql
|
.. code:: postgresql
|
||||||
|
|
||||||
NOTIFY pgrst, 'reload config'
|
NOTIFY pgrst, 'reload config'
|
||||||
|
|
||||||
The ``"pgrst"`` notification channel is enabled by default. You can name the channel with :ref:`db-channel` and enable or disable it with :ref:`db-channel-enabled`.
|
|
||||||
|
|
||||||
.. _config_full_list:
|
.. _config_full_list:
|
||||||
|
|
||||||
List of parameters
|
List of parameters
|
||||||
@@ -696,6 +694,7 @@ log-level
|
|||||||
log-level = "info"
|
log-level = "info"
|
||||||
|
|
||||||
# All the above plus events for development purposes are logged
|
# All the above plus events for development purposes are logged
|
||||||
|
# Logs connection pool events and the schema cache parsing time
|
||||||
log-level = "debug"
|
log-level = "debug"
|
||||||
|
|
||||||
Because currently there's no buffering for logging, the levels with minimal logging(``crit/error``) will increase throughput.
|
Because currently there's no buffering for logging, the levels with minimal logging(``crit/error``) will increase throughput.
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
.. _listener:
|
||||||
|
|
||||||
|
Listener
|
||||||
|
########
|
||||||
|
|
||||||
|
PostgREST uses `LISTEN <https://www.postgresql.org/docs/current/sql-listen.html>`_ to reload its :ref:`Schema Cache <schema_reloading_notify>` and :ref:`Configuration <config_reloading_notify>` via `NOTIFY <https://www.postgresql.org/docs/current/sql-notify.html>`_.
|
||||||
|
This is useful in environments where you can’t send SIGUSR1 or SIGUSR2 Unix Signals.
|
||||||
|
Like on cloud managed containers or on Windows systems.
|
||||||
|
|
||||||
|
.. code:: postgresql
|
||||||
|
|
||||||
|
NOTIFY pgrst, 'reload schema'; -- reload schema cache
|
||||||
|
NOTIFY pgrst, 'reload config'; -- reload config
|
||||||
|
NOTIFY pgrst; -- reload both
|
||||||
|
|
||||||
|
By default, the LISTEN channel is enabled (:ref:`db-channel-enabled`) and named ``pgrst`` (:ref:`db-channel`).
|
||||||
|
|
||||||
|
Listener on Read Replicas
|
||||||
|
=========================
|
||||||
|
|
||||||
|
|
||||||
|
The ``LISTEN`` and ``NOTIFY`` commands do not work on PostgreSQL read replicas.
|
||||||
|
Thus, if you connect PostgREST to a read replica the Listener will fail to start.
|
||||||
|
|
||||||
|
.. code:: psql
|
||||||
|
|
||||||
|
-- check if the instance is a replica
|
||||||
|
postgres=# select pg_is_in_recovery();
|
||||||
|
pg_is_in_recovery
|
||||||
|
-------------------
|
||||||
|
t
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
postgres=# LISTEN pgrst;
|
||||||
|
ERROR: cannot execute LISTEN during recovery
|
||||||
|
|
||||||
|
To work around this, you can connect the Listener to the primary while still using the :ref:`connection_pool` on the replica.
|
||||||
|
|
||||||
|
This can be done by using the standard `libpq multiple hosts <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS>`_ and `target_session_attrs <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-TARGET-SESSION-ATTRS>`_ in your :ref:`connection string <db-uri>`.
|
||||||
|
|
||||||
|
.. code:: bash
|
||||||
|
|
||||||
|
db-uri = "postgres://read_replica.host,primary.host/mydb?target_session_attrs=read-only"
|
||||||
|
|
||||||
|
This will cause the :ref:`connection_pool` to connect to the read replica host and ``LISTEN`` on the fallback primary host.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Under the hood, PostgREST forces `target_session_attrs=read-write <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-TARGET-SESSION-ATTRS>`_ for the ``LISTEN`` session.
|
||||||
|
|
||||||
|
.. _listener_automatic_recovery:
|
||||||
|
|
||||||
|
Automatic Recovery
|
||||||
|
==================
|
||||||
|
|
||||||
|
The listener will retry reconnecting to the database if connection loss happens.
|
||||||
|
|
||||||
|
- It will retry forever with exponential backoff, with a maximum backoff time of 32 seconds between retries. Each of these attempts are :ref:`logged <pgrst_logging>`.
|
||||||
|
- Automatic recovery can be disabled by setting :ref:`db-pool-automatic-recovery` to ``false``.
|
||||||
|
- To ensure a valid state, the listener reloads the :ref:`schema_cache` and :ref:`configuration` when recovering.
|
||||||
@@ -22,7 +22,11 @@ PostgREST logs basic request information to ``stdout``, including the authentica
|
|||||||
127.0.0.1 - user [26/Jul/2021:01:56:38 -0500] "GET /clients HTTP/1.1" 200 - "" "curl/7.64.0"
|
127.0.0.1 - user [26/Jul/2021:01:56:38 -0500] "GET /clients HTTP/1.1" 200 - "" "curl/7.64.0"
|
||||||
127.0.0.1 - anonymous [26/Jul/2021:01:56:48 -0500] "GET /unexistent HTTP/1.1" 404 - "" "curl/7.64.0"
|
127.0.0.1 - anonymous [26/Jul/2021:01:56:48 -0500] "GET /unexistent HTTP/1.1" 404 - "" "curl/7.64.0"
|
||||||
|
|
||||||
For diagnostic information about the server itself, PostgREST logs to ``stderr``. It includes the server version and also the version of the connected PostgreSQL.
|
For diagnostic information about the server itself, PostgREST logs to ``stderr``:
|
||||||
|
|
||||||
|
- The full version of the connected PostgreSQL database.
|
||||||
|
- :ref:`schema_cache` statistics.
|
||||||
|
- The messages received by the :ref:`listener`.
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
@@ -30,6 +34,12 @@ For diagnostic information about the server itself, PostgREST logs to ``stderr``
|
|||||||
06/May/2024:08:16:11 -0500: Attempting to connect to the database...
|
06/May/2024:08:16:11 -0500: Attempting to connect to the database...
|
||||||
06/May/2024:08:16:11 -0500: Successfully connected to PostgreSQL 14.10 (Ubuntu 14.10-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
|
06/May/2024:08:16:11 -0500: Successfully connected to PostgreSQL 14.10 (Ubuntu 14.10-0ubuntu0.22.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, 64-bit
|
||||||
06/May/2024:08:16:11 -0500: Listening on port 3000
|
06/May/2024:08:16:11 -0500: Listening on port 3000
|
||||||
|
06/May/2024:08:16:11 -0500: Listening for notifications on the "pgrst" channel
|
||||||
|
06/May/2024:08:16:11 -0500: Config reloaded
|
||||||
|
06/May/2024:08:16:11 -0500: Schema cache queried in 3.8 milliseconds
|
||||||
|
06/May/2024:08:16:11 -0500: Schema cache loaded 15 Relations, 8 Relationships, 8 Functions, 0 Domain Representations, 4 Media Type Handlers
|
||||||
|
06/May/2024:14:11:27 -0500: Received a config reload message on the "pgrst" channel
|
||||||
|
06/May/2024:14:11:27 -0500: Config reloaded
|
||||||
|
|
||||||
Database Logs
|
Database Logs
|
||||||
-------------
|
-------------
|
||||||
|
|||||||
@@ -47,16 +47,12 @@ For docker you can do:
|
|||||||
Schema Cache Reloading with NOTIFY
|
Schema Cache Reloading with NOTIFY
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
PostgREST also allows you to reload its schema cache through PostgreSQL `NOTIFY <https://www.postgresql.org/docs/current/sql-notify.html>`_.
|
To reload the schema cache from within the database, you can use the ``NOTIFY`` command. See :ref:`listener`.
|
||||||
|
|
||||||
.. code-block:: postgres
|
.. code-block:: postgres
|
||||||
|
|
||||||
NOTIFY pgrst, 'reload schema'
|
NOTIFY pgrst, 'reload schema'
|
||||||
|
|
||||||
This is useful in environments where you can’t send the SIGUSR1 Unix Signal. Like on cloud managed containers or on Windows systems.
|
|
||||||
|
|
||||||
The ``pgrst`` notification channel is enabled by default. For configuring the channel, see :ref:`db-channel` and :ref:`db-channel-enabled`.
|
|
||||||
|
|
||||||
.. _auto_schema_reloading:
|
.. _auto_schema_reloading:
|
||||||
|
|
||||||
Automatic Schema Cache Reloading
|
Automatic Schema Cache Reloading
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ sphinx-rtd-dark-mode>=1.3.0
|
|||||||
sphinx-tabs>=3.2.0
|
sphinx-tabs>=3.2.0
|
||||||
sphinx>=5.0.2
|
sphinx>=5.0.2
|
||||||
sphinxext-opengraph==0.9.1
|
sphinxext-opengraph==0.9.1
|
||||||
urllib3==2.2.1
|
urllib3==2.2.3
|
||||||
|
|||||||
@@ -17,12 +17,7 @@ To make an API we'll simply be building a database. All the endpoints and permis
|
|||||||
|
|
||||||
By the end of this tutorial you'll have a working database, PostgREST server, and a simple single-user todo list API.
|
By the end of this tutorial you'll have a working database, PostgREST server, and a simple single-user todo list API.
|
||||||
|
|
||||||
Step 1. Relax, we'll help
|
Step 1. Install PostgreSQL
|
||||||
-------------------------
|
|
||||||
|
|
||||||
As you begin the tutorial, pop open the project `chat room <https://gitter.im/begriffs/postgrest>`_ in another tab. There are a nice group of people active in the project and we'll help you out if you get stuck.
|
|
||||||
|
|
||||||
Step 2. Install PostgreSQL
|
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
If you're already familiar with using PostgreSQL and have it installed on your system you can use the existing installation (see :ref:`pg-dependency` for minimum requirements). For this tutorial we'll describe how to use the database in Docker because database configuration is otherwise too complicated for a simple tutorial.
|
If you're already familiar with using PostgreSQL and have it installed on your system you can use the existing installation (see :ref:`pg-dependency` for minimum requirements). For this tutorial we'll describe how to use the database in Docker because database configuration is otherwise too complicated for a simple tutorial.
|
||||||
@@ -48,7 +43,7 @@ This will run the Docker instance as a daemon and expose port 5432 to the host s
|
|||||||
In this case, you will need to change the **first** of the two 5432 to something else, for example to :code:`5433:5432`. Remember to also adjust the port in your config file in Step 5!
|
In this case, you will need to change the **first** of the two 5432 to something else, for example to :code:`5433:5432`. Remember to also adjust the port in your config file in Step 5!
|
||||||
|
|
||||||
|
|
||||||
Step 3. Install PostgREST
|
Step 2. Install PostgREST
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
Using a Package Manager
|
Using a Package Manager
|
||||||
@@ -122,7 +117,7 @@ If everything is working correctly it will print out its version and the availab
|
|||||||
</details>
|
</details>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
Step 4. Create Database for API
|
Step 3. Create Database for API
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
Connect to the SQL console (psql) inside the container. To do so, run this from your command line:
|
Connect to the SQL console (psql) inside the container. To do so, run this from your command line:
|
||||||
@@ -185,7 +180,7 @@ Now quit out of psql; it's time to start the API!
|
|||||||
|
|
||||||
\q
|
\q
|
||||||
|
|
||||||
Step 5. Run PostgREST
|
Step 4. Run PostgREST
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
PostgREST can use a configuration file to tell it how to connect to the database. Create a file :code:`tutorial.conf` with this inside:
|
PostgREST can use a configuration file to tell it how to connect to the database. Create a file :code:`tutorial.conf` with this inside:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
owner = "NixOS";
|
owner = "NixOS";
|
||||||
repo = "nixpkgs";
|
repo = "nixpkgs";
|
||||||
date = "2024-04-20";
|
date = "2024-05-29";
|
||||||
rev = "92d295f588631b0db2da509f381b4fb1e74173c5";
|
rev = "a15e8d1b3d9e6496c4a3214e2104f6d28dfa7df7";
|
||||||
tarballHash = "162w28y4i5c8g5qhjvs827qxphf2a8n4c8fwhcywzl1j1a35h2im";
|
tarballHash = "sha256:0r4a4165f4n1zlpnyjrdrwrg86n1b6g3axsdh3j5iizpmjrlxmd7";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,14 @@ let
|
|||||||
}
|
}
|
||||||
{ });
|
{ });
|
||||||
|
|
||||||
hasql-pool = lib.dontCheck prev.hasql-pool_1_0_1;
|
hasql-pool = lib.dontCheck (prev.callHackageDirect
|
||||||
|
{
|
||||||
|
pkg = "hasql-pool";
|
||||||
|
ver = "1.0.1";
|
||||||
|
sha256 = "sha256-Hf1f7lX0LWkjrb25SDBovCYPRdmUP1H6pAxzi7kT4Gg=";
|
||||||
|
}
|
||||||
|
{ }
|
||||||
|
);
|
||||||
|
|
||||||
postgresql-libpq = lib.dontCheck
|
postgresql-libpq = lib.dontCheck
|
||||||
(prev.postgresql-libpq.override {
|
(prev.postgresql-libpq.override {
|
||||||
@@ -67,8 +74,8 @@ let
|
|||||||
hasql-notifications = lib.dontCheck (prev.callHackageDirect
|
hasql-notifications = lib.dontCheck (prev.callHackageDirect
|
||||||
{
|
{
|
||||||
pkg = "hasql-notifications";
|
pkg = "hasql-notifications";
|
||||||
ver = "0.2.2.0";
|
ver = "0.2.2.2";
|
||||||
sha256 = "sha256-73OQ9/su2qvO7HavF3xuuNWLXSXyB9reBUQDaHys06I=";
|
sha256 = "sha256-myKwlug7OgTa/qP6mHfCD+5Q8IhM17JvpJBfSo+M01k=";
|
||||||
}
|
}
|
||||||
{ }
|
{ }
|
||||||
);
|
);
|
||||||
|
|||||||
+2
-25
@@ -2,10 +2,8 @@
|
|||||||
, aspellDicts
|
, aspellDicts
|
||||||
, buildToolbox
|
, buildToolbox
|
||||||
, checkedShellScript
|
, checkedShellScript
|
||||||
, imagemagick
|
|
||||||
, python3
|
, python3
|
||||||
, python3Packages
|
, python3Packages
|
||||||
, texlive
|
|
||||||
, writers
|
, writers
|
||||||
, plantuml
|
, plantuml
|
||||||
}:
|
}:
|
||||||
@@ -61,36 +59,15 @@ let
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
render =
|
render =
|
||||||
let
|
|
||||||
pdflatex = texlive.combine {
|
|
||||||
inherit (texlive)
|
|
||||||
amsmath
|
|
||||||
booktabs
|
|
||||||
cancel
|
|
||||||
gensymb
|
|
||||||
mathdots
|
|
||||||
multirow
|
|
||||||
pgf
|
|
||||||
pgf-blur
|
|
||||||
scheme-basic
|
|
||||||
siunitx
|
|
||||||
standalone
|
|
||||||
yhmath
|
|
||||||
;
|
|
||||||
};
|
|
||||||
in
|
|
||||||
checkedShellScript
|
checkedShellScript
|
||||||
{
|
{
|
||||||
name = "postgrest-docs-render";
|
name = "postgrest-docs-render";
|
||||||
docs = "Render the diagrams.";
|
docs = "Render the diagrams.";
|
||||||
workingDir = "/docs/_diagrams";
|
workingDir = "/docs/_diagrams";
|
||||||
withTmpDir = true;
|
|
||||||
}
|
}
|
||||||
''
|
''
|
||||||
${pdflatex}/bin/pdflatex -halt-on-error -output-directory="$tmpdir" db.tex
|
${plantuml}/bin/plantuml -tsvg uml/*.uml -o ../../_static
|
||||||
${imagemagick}/bin/convert -density 300 "$tmpdir/db.pdf" ../_static/db.png
|
${plantuml}/bin/plantuml -tsvg -darkmode uml/dark/*.uml -o ../../../_static
|
||||||
|
|
||||||
${plantuml}/bin/plantuml arch.uml -o ../_static
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
server =
|
server =
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
name: postgrest
|
name: postgrest
|
||||||
version: 12.2.0
|
version: 12.2.8
|
||||||
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
|
||||||
@@ -110,7 +110,7 @@ library
|
|||||||
, gitrev >= 1.2 && < 1.4
|
, gitrev >= 1.2 && < 1.4
|
||||||
, 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.2.2.0 && < 0.3
|
, hasql-notifications >= 0.2.2.2 && < 0.2.3
|
||||||
, hasql-pool >= 1.0.1 && < 1.1
|
, hasql-pool >= 1.0.1 && < 1.1
|
||||||
, hasql-transaction >= 1.0.1 && < 1.1
|
, hasql-transaction >= 1.0.1 && < 1.1
|
||||||
, heredoc >= 0.2 && < 0.3
|
, heredoc >= 0.2 && < 0.3
|
||||||
|
|||||||
@@ -68,14 +68,14 @@ run appState = do
|
|||||||
|
|
||||||
observer $ AppStartObs prettyVersion
|
observer $ AppStartObs prettyVersion
|
||||||
|
|
||||||
AppState.connectionWorker appState
|
AppState.schemaCacheLoader appState -- Loads the initial SchemaCache
|
||||||
Unix.installSignalHandlers (AppState.getMainThreadId appState) (AppState.connectionWorker appState) (AppState.reReadConfig False appState)
|
Unix.installSignalHandlers (AppState.getMainThreadId appState) (AppState.schemaCacheLoader appState) (AppState.readInDbConfig False appState)
|
||||||
|
|
||||||
Listener.runListener appState
|
Listener.runListener appState
|
||||||
|
|
||||||
Admin.runAdmin appState (serverSettings conf)
|
Admin.runAdmin appState (serverSettings conf)
|
||||||
|
|
||||||
let app = postgrest configLogLevel appState (AppState.connectionWorker appState)
|
let app = postgrest configLogLevel appState (AppState.schemaCacheLoader appState)
|
||||||
|
|
||||||
case configServerUnixSocket of
|
case configServerUnixSocket of
|
||||||
Just path -> do
|
Just path -> do
|
||||||
|
|||||||
+79
-165
@@ -24,12 +24,11 @@ module PostgREST.AppState
|
|||||||
, putPgVersion
|
, putPgVersion
|
||||||
, putIsListenerOn
|
, putIsListenerOn
|
||||||
, usePool
|
, usePool
|
||||||
, reReadConfig
|
, readInDbConfig
|
||||||
, connectionWorker
|
, schemaCacheLoader
|
||||||
, getObserver
|
, getObserver
|
||||||
, isLoaded
|
, isLoaded
|
||||||
, isPending
|
, isPending
|
||||||
, waitForListenerCanStart
|
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.Aeson as JSON
|
import qualified Data.Aeson as JSON
|
||||||
@@ -87,20 +86,16 @@ data AuthResult = AuthResult
|
|||||||
data AppState = AppState
|
data AppState = AppState
|
||||||
-- | Database connection pool
|
-- | Database connection pool
|
||||||
{ statePool :: SQL.Pool
|
{ statePool :: SQL.Pool
|
||||||
-- | Database server version, will be updated by the connectionWorker
|
-- | Database server version
|
||||||
, statePgVersion :: IORef PgVersion
|
, statePgVersion :: IORef PgVersion
|
||||||
-- | No schema cache at the start. Will be filled in by the connectionWorker
|
-- | Schema cache
|
||||||
, stateSchemaCache :: IORef (Maybe SchemaCache)
|
, stateSchemaCache :: IORef (Maybe SchemaCache)
|
||||||
-- | The schema cache status
|
-- | The schema cache status
|
||||||
, stateSCacheStatus :: IORef SchemaCacheStatus
|
, stateSCacheStatus :: IORef SchemaCacheStatus
|
||||||
-- | The connection status
|
|
||||||
, stateConnStatus :: IORef ConnectionStatus
|
|
||||||
-- | State of the LISTEN channel
|
-- | State of the LISTEN channel
|
||||||
, stateIsListenerOn :: IORef Bool
|
, stateIsListenerOn :: IORef Bool
|
||||||
-- | starts the connection worker with a debounce
|
-- | starts the connection worker with a debounce
|
||||||
, debouncedConnectionWorker :: IO ()
|
, debouncedSCacheLoader :: IO ()
|
||||||
-- | Binary semaphore used to sync the listener with the connectionWorker.
|
|
||||||
, stateListenerCanStart :: MVar ()
|
|
||||||
-- | Config that can change at runtime
|
-- | Config that can change at runtime
|
||||||
, stateConf :: IORef AppConfig
|
, stateConf :: IORef AppConfig
|
||||||
-- | Time used for verifying JWT expiration
|
-- | Time used for verifying JWT expiration
|
||||||
@@ -129,15 +124,8 @@ data SchemaCacheStatus
|
|||||||
| SCPending
|
| SCPending
|
||||||
deriving Eq
|
deriving Eq
|
||||||
|
|
||||||
-- | Current database connection status
|
|
||||||
data ConnectionStatus
|
|
||||||
= ConnEstablished
|
|
||||||
| ConnPending
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
type AppSockets = (NS.Socket, Maybe NS.Socket)
|
type AppSockets = (NS.Socket, Maybe NS.Socket)
|
||||||
|
|
||||||
|
|
||||||
init :: AppConfig -> IO AppState
|
init :: AppConfig -> IO AppState
|
||||||
init conf@AppConfig{configLogLevel, configDbPoolSize} = do
|
init conf@AppConfig{configLogLevel, configDbPoolSize} = do
|
||||||
loggerState <- Logger.init
|
loggerState <- Logger.init
|
||||||
@@ -156,10 +144,8 @@ initWithPool (sock, adminSock) pool conf loggerState metricsState observer = do
|
|||||||
<$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
|
<$> newIORef minimumPgVersion -- assume we're in a supported version when starting, this will be corrected on a later step
|
||||||
<*> newIORef Nothing
|
<*> newIORef Nothing
|
||||||
<*> newIORef SCPending
|
<*> newIORef SCPending
|
||||||
<*> newIORef ConnPending
|
|
||||||
<*> newIORef False
|
<*> newIORef False
|
||||||
<*> pure (pure ())
|
<*> pure (pure ())
|
||||||
<*> newEmptyMVar
|
|
||||||
<*> newIORef conf
|
<*> newIORef conf
|
||||||
<*> mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentTime }
|
<*> mkAutoUpdate defaultUpdateSettings { updateAction = getCurrentTime }
|
||||||
<*> myThreadId
|
<*> myThreadId
|
||||||
@@ -172,15 +158,15 @@ initWithPool (sock, adminSock) pool conf loggerState metricsState observer = do
|
|||||||
<*> pure loggerState
|
<*> pure loggerState
|
||||||
<*> pure metricsState
|
<*> pure metricsState
|
||||||
|
|
||||||
debWorker <-
|
deb <-
|
||||||
let decisecond = 100000 in
|
let decisecond = 100000 in
|
||||||
mkDebounce defaultDebounceSettings
|
mkDebounce defaultDebounceSettings
|
||||||
{ debounceAction = internalConnectionWorker appState
|
{ debounceAction = retryingSchemaCacheLoad appState
|
||||||
, debounceFreq = decisecond
|
, debounceFreq = decisecond
|
||||||
, debounceEdge = leadingEdge -- runs the worker at the start and the end
|
, debounceEdge = leadingEdge -- runs the worker at the start and the end
|
||||||
}
|
}
|
||||||
|
|
||||||
return appState { debouncedConnectionWorker = debWorker}
|
return appState { debouncedSCacheLoader = deb}
|
||||||
|
|
||||||
destroy :: AppState -> IO ()
|
destroy :: AppState -> IO ()
|
||||||
destroy = destroyPool
|
destroy = destroyPool
|
||||||
@@ -279,8 +265,9 @@ usePool AppState{stateObserver=observer, stateMainThreadId=mainThreadId, ..} ses
|
|||||||
SQL.ServerError{} ->
|
SQL.ServerError{} ->
|
||||||
when (Error.status (Error.PgError False err) >= HTTP.status500) $
|
when (Error.status (Error.PgError False err) >= HTTP.status500) $
|
||||||
observer $ QueryErrorCodeHighObs err
|
observer $ QueryErrorCodeHighObs err
|
||||||
SQL.SessionUsageError (SQL.QueryError _ _ (SQL.ClientError _)) ->
|
err@(SQL.SessionUsageError (SQL.QueryError _ _ (SQL.ClientError _))) ->
|
||||||
pure ()
|
-- An error on the client-side, usually indicates problems wth connection
|
||||||
|
observer $ QueryErrorCodeHighObs err
|
||||||
)
|
)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@@ -306,41 +293,18 @@ getSchemaCache = readIORef . stateSchemaCache
|
|||||||
putSchemaCache :: AppState -> Maybe SchemaCache -> IO ()
|
putSchemaCache :: AppState -> Maybe SchemaCache -> IO ()
|
||||||
putSchemaCache appState = atomicWriteIORef (stateSchemaCache appState)
|
putSchemaCache appState = atomicWriteIORef (stateSchemaCache appState)
|
||||||
|
|
||||||
connectionWorker :: AppState -> IO ()
|
schemaCacheLoader :: AppState -> IO ()
|
||||||
connectionWorker = debouncedConnectionWorker
|
schemaCacheLoader = debouncedSCacheLoader
|
||||||
|
|
||||||
getNextDelay :: AppState -> IO Int
|
getNextDelay :: AppState -> IO Int
|
||||||
getNextDelay = readIORef . stateNextDelay
|
getNextDelay = readIORef . stateNextDelay
|
||||||
|
|
||||||
putNextDelay :: AppState -> Int -> IO ()
|
|
||||||
putNextDelay = atomicWriteIORef . stateNextDelay
|
|
||||||
|
|
||||||
getNextListenerDelay :: AppState -> IO Int
|
getNextListenerDelay :: AppState -> IO Int
|
||||||
getNextListenerDelay = readIORef . stateNextListenerDelay
|
getNextListenerDelay = readIORef . stateNextListenerDelay
|
||||||
|
|
||||||
putNextListenerDelay :: AppState -> Int -> IO ()
|
putNextListenerDelay :: AppState -> Int -> IO ()
|
||||||
putNextListenerDelay = atomicWriteIORef . stateNextListenerDelay
|
putNextListenerDelay = atomicWriteIORef . stateNextListenerDelay
|
||||||
|
|
||||||
--------------------------------------------------------------------------------------
|
|
||||||
-------------------------------------------IMPORTANT----------------------------------
|
|
||||||
--------------------------------------------------------------------------------------
|
|
||||||
-- Both of these function ensure there's no parallel connection attempts between the listener and the connection pool.
|
|
||||||
-- Doing that raised an error with GSSAPI as discussed on https://github.com/PostgREST/postgrest/issues/3569.
|
|
||||||
-- Until the root cause is found and solved, we need to prevent parallel connection attempts.
|
|
||||||
|
|
||||||
-- tryPutMVar doesn't lock the thread. It should always succeed since
|
|
||||||
-- the connectionWorker is the only mvar producer.
|
|
||||||
setListenerCanStart :: AppState -> IO ()
|
|
||||||
setListenerCanStart appState = void $ tryPutMVar (stateListenerCanStart appState) ()
|
|
||||||
|
|
||||||
-- | As this IO action uses `takeMVar` internally, it will only return once
|
|
||||||
-- `stateListenerCanStart` has been set using `setListenerCanStart`.
|
|
||||||
waitForListenerCanStart :: AppState -> IO ()
|
|
||||||
waitForListenerCanStart = takeMVar . stateListenerCanStart
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------------
|
|
||||||
--------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
getConfig :: AppState -> IO AppConfig
|
getConfig :: AppState -> IO AppConfig
|
||||||
getConfig = readIORef . stateConf
|
getConfig = readIORef . stateConf
|
||||||
|
|
||||||
@@ -362,53 +326,81 @@ getSocketAdmin = stateSocketAdmin
|
|||||||
getMainThreadId :: AppState -> ThreadId
|
getMainThreadId :: AppState -> ThreadId
|
||||||
getMainThreadId = stateMainThreadId
|
getMainThreadId = stateMainThreadId
|
||||||
|
|
||||||
getIsListenerOn :: AppState -> IO Bool
|
isConnEstablished :: AppState -> IO Bool
|
||||||
getIsListenerOn appState = do
|
isConnEstablished appState = do
|
||||||
AppConfig{..} <- getConfig appState
|
AppConfig{..} <- getConfig appState
|
||||||
if configDbChannelEnabled then
|
if configDbChannelEnabled then -- if the listener is enabled, we can be sure the connection is up
|
||||||
readIORef $ stateIsListenerOn appState
|
readIORef $ stateIsListenerOn appState
|
||||||
else
|
else -- otherwise the only way to check the connection is to make a query
|
||||||
pure True
|
isRight <$> usePool appState (SQL.sql "SELECT 1")
|
||||||
|
|
||||||
putIsListenerOn :: AppState -> Bool -> IO ()
|
putIsListenerOn :: AppState -> Bool -> IO ()
|
||||||
putIsListenerOn = atomicWriteIORef . stateIsListenerOn
|
putIsListenerOn = atomicWriteIORef . stateIsListenerOn
|
||||||
|
|
||||||
isConnEstablished :: AppState -> IO Bool
|
|
||||||
isConnEstablished x = do
|
|
||||||
conf <- getConfig x
|
|
||||||
if configDbChannelEnabled conf
|
|
||||||
then do -- if the listener is enabled, we can be sure the connection status is always up to date
|
|
||||||
st <- readIORef $ stateConnStatus x
|
|
||||||
return $ st == ConnEstablished
|
|
||||||
else -- otherwise the only way to check the connection is to make a query
|
|
||||||
isRight <$> usePool x (SQL.sql "SELECT 1")
|
|
||||||
|
|
||||||
isLoaded :: AppState -> IO Bool
|
isLoaded :: AppState -> IO Bool
|
||||||
isLoaded x = do
|
isLoaded x = do
|
||||||
scacheStatus <- readIORef $ stateSCacheStatus x
|
scacheStatus <- readIORef $ stateSCacheStatus x
|
||||||
connEstablished <- isConnEstablished x
|
connEstablished <- isConnEstablished x
|
||||||
listenerOn <- getIsListenerOn x
|
return $ scacheStatus == SCLoaded && connEstablished
|
||||||
return $ scacheStatus == SCLoaded && connEstablished && listenerOn
|
|
||||||
|
|
||||||
isPending :: AppState -> IO Bool
|
isPending :: AppState -> IO Bool
|
||||||
isPending x = do
|
isPending x = do
|
||||||
scacheStatus <- readIORef $ stateSCacheStatus x
|
scacheStatus <- readIORef $ stateSCacheStatus x
|
||||||
connStatus <- readIORef $ stateConnStatus x
|
connEstablished <- isConnEstablished x
|
||||||
listenerOn <- getIsListenerOn x
|
return $ scacheStatus == SCPending || not connEstablished
|
||||||
return $ scacheStatus == SCPending || connStatus == ConnPending || not listenerOn
|
|
||||||
|
|
||||||
putSCacheStatus :: AppState -> SchemaCacheStatus -> IO ()
|
putSCacheStatus :: AppState -> SchemaCacheStatus -> IO ()
|
||||||
putSCacheStatus = atomicWriteIORef . stateSCacheStatus
|
putSCacheStatus = atomicWriteIORef . stateSCacheStatus
|
||||||
|
|
||||||
putConnStatus :: AppState -> ConnectionStatus -> IO ()
|
|
||||||
putConnStatus = atomicWriteIORef . stateConnStatus
|
|
||||||
|
|
||||||
getObserver :: AppState -> ObservationHandler
|
getObserver :: AppState -> ObservationHandler
|
||||||
getObserver = stateObserver
|
getObserver = stateObserver
|
||||||
|
|
||||||
-- | Load the SchemaCache by using a connection from the pool.
|
-- | Try to load the schema cache and retry if it fails.
|
||||||
loadSchemaCache :: AppState -> IO SchemaCacheStatus
|
--
|
||||||
loadSchemaCache appState@AppState{stateObserver=observer} = do
|
-- This is done by repeatedly: 1) flushing the pool, 2) querying the version and validating that the postgres version is supported by us, and 3) loading the schema cache.
|
||||||
|
-- It's necessary to flush the pool:
|
||||||
|
--
|
||||||
|
-- + Because connections cache the pg catalog(see #2620)
|
||||||
|
-- + For rapid recovery. Otherwise, the pool idle or lifetime timeout would have to be reached for new healthy connections to be acquired.
|
||||||
|
retryingSchemaCacheLoad :: AppState -> IO ()
|
||||||
|
retryingSchemaCacheLoad appState@AppState{stateObserver=observer, stateMainThreadId=mainThreadId} =
|
||||||
|
void $ retrying retryPolicy shouldRetry (\RetryStatus{rsIterNumber, rsPreviousDelay} -> do
|
||||||
|
when (rsIterNumber > 0) $ do
|
||||||
|
let delay = fromMaybe 0 rsPreviousDelay `div` oneSecondInUs
|
||||||
|
observer $ ConnectionRetryObs delay
|
||||||
|
putNextListenerDelay appState delay
|
||||||
|
|
||||||
|
flushPool appState
|
||||||
|
|
||||||
|
(,) <$> qPgVersion <*> (qInDbConfig *> qSchemaCache)
|
||||||
|
)
|
||||||
|
where
|
||||||
|
qPgVersion :: IO (Maybe PgVersion)
|
||||||
|
qPgVersion = do
|
||||||
|
AppConfig{..} <- getConfig appState
|
||||||
|
pgVersion <- usePool appState (queryPgVersion False) -- No need to prepare the query here, as the connection might not be established
|
||||||
|
case pgVersion of
|
||||||
|
Left e -> do
|
||||||
|
observer $ QueryPgVersionError e
|
||||||
|
unless configDbPoolAutomaticRecovery $ do
|
||||||
|
observer ExitDBNoRecoveryObs
|
||||||
|
killThread mainThreadId
|
||||||
|
return Nothing
|
||||||
|
Right actualPgVersion -> do
|
||||||
|
when (actualPgVersion < minimumPgVersion) $ do
|
||||||
|
observer $ ExitUnsupportedPgVersion actualPgVersion minimumPgVersion
|
||||||
|
killThread mainThreadId
|
||||||
|
observer $ DBConnectedObs $ pgvFullName actualPgVersion
|
||||||
|
putPgVersion appState actualPgVersion
|
||||||
|
return $ Just actualPgVersion
|
||||||
|
|
||||||
|
qInDbConfig :: IO ()
|
||||||
|
qInDbConfig = do
|
||||||
|
AppConfig{..} <- getConfig appState
|
||||||
|
when configDbConfig $ readInDbConfig False appState
|
||||||
|
|
||||||
|
qSchemaCache :: IO (Maybe SchemaCache)
|
||||||
|
qSchemaCache = do
|
||||||
conf@AppConfig{..} <- getConfig appState
|
conf@AppConfig{..} <- getConfig appState
|
||||||
(resultTime, result) <-
|
(resultTime, result) <-
|
||||||
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
let transaction = if configDbPreparedStatements then SQL.transaction else SQL.unpreparedTransaction in
|
||||||
@@ -418,7 +410,7 @@ loadSchemaCache appState@AppState{stateObserver=observer} = do
|
|||||||
putSCacheStatus appState SCPending
|
putSCacheStatus appState SCPending
|
||||||
putSchemaCache appState Nothing
|
putSchemaCache appState Nothing
|
||||||
observer $ SchemaCacheErrorObs e
|
observer $ SchemaCacheErrorObs e
|
||||||
return SCPending
|
return Nothing
|
||||||
|
|
||||||
Right sCache -> do
|
Right sCache -> do
|
||||||
-- IMPORTANT: While the pending schema cache state starts from running the above querySchemaCache, only at this stage we block API requests due to the usage of an
|
-- IMPORTANT: While the pending schema cache state starts from running the above querySchemaCache, only at this stage we block API requests due to the usage of an
|
||||||
@@ -429,103 +421,25 @@ loadSchemaCache appState@AppState{stateObserver=observer} = do
|
|||||||
(t, _) <- timeItT $ observer $ SchemaCacheSummaryObs $ showSummary sCache
|
(t, _) <- timeItT $ observer $ SchemaCacheSummaryObs $ showSummary sCache
|
||||||
observer $ SchemaCacheLoadedObs t
|
observer $ SchemaCacheLoadedObs t
|
||||||
putSCacheStatus appState SCLoaded
|
putSCacheStatus appState SCLoaded
|
||||||
return SCLoaded
|
return $ Just sCache
|
||||||
|
|
||||||
-- | The purpose of this worker is to obtain a healthy connection to pg and an
|
shouldRetry :: RetryStatus -> (Maybe PgVersion, Maybe SchemaCache) -> IO Bool
|
||||||
-- up-to-date schema cache(SchemaCache). This method is meant to be called
|
shouldRetry _ (pgVer, sCache) = do
|
||||||
-- multiple times by the same thread, but does nothing if the previous
|
|
||||||
-- invocation has not terminated. In all cases this method does not halt the
|
|
||||||
-- calling thread, the work is performed in a separate thread.
|
|
||||||
--
|
|
||||||
-- Background thread that does the following :
|
|
||||||
-- 1. Tries to connect to pg server and will keep trying until success.
|
|
||||||
-- 2. Checks if the pg version is supported and if it's not it kills the main
|
|
||||||
-- program.
|
|
||||||
-- 3. Obtains the sCache. If this fails, it goes back to 1.
|
|
||||||
internalConnectionWorker :: AppState -> IO ()
|
|
||||||
internalConnectionWorker appState@AppState{stateObserver=observer, stateMainThreadId=mainThreadId} = work
|
|
||||||
where
|
|
||||||
work = do
|
|
||||||
AppConfig{..} <- getConfig appState
|
AppConfig{..} <- getConfig appState
|
||||||
observer DBConnectAttemptObs
|
let itShould = configDbPoolAutomaticRecovery && (isNothing pgVer || isNothing sCache)
|
||||||
connStatus <- establishConnection appState
|
|
||||||
case connStatus of
|
|
||||||
ConnPending ->
|
|
||||||
unless configDbPoolAutomaticRecovery $ do
|
|
||||||
observer ExitDBNoRecoveryObs
|
|
||||||
killThread mainThreadId
|
|
||||||
ConnEstablished -> do
|
|
||||||
actualPgVersion <- getPgVersion appState
|
|
||||||
when (actualPgVersion < minimumPgVersion) $ do
|
|
||||||
observer $ ExitUnsupportedPgVersion actualPgVersion minimumPgVersion
|
|
||||||
killThread mainThreadId
|
|
||||||
observer (DBConnectedObs $ pgvFullName actualPgVersion)
|
|
||||||
-- Wake up the Listener
|
|
||||||
when configDbChannelEnabled $
|
|
||||||
setListenerCanStart appState
|
|
||||||
-- this could be fail because the connection drops, but the loadSchemaCache will pick the error and retry again
|
|
||||||
-- We cannot retry after it fails immediately, because db-pre-config could have user errors. We just log the error and continue.
|
|
||||||
when configDbConfig $ reReadConfig False appState
|
|
||||||
scStatus <- loadSchemaCache appState
|
|
||||||
case scStatus of
|
|
||||||
SCLoaded ->
|
|
||||||
-- do nothing and proceed if the load was successful
|
|
||||||
return ()
|
|
||||||
SCPending ->
|
|
||||||
-- retry reloading the schema cache
|
|
||||||
work
|
|
||||||
|
|
||||||
-- | Repeatedly flush the pool, and check if a connection from the
|
|
||||||
-- pool allows access to the PostgreSQL database.
|
|
||||||
--
|
|
||||||
-- Releasing the pool is key for rapid recovery. Otherwise, the pool
|
|
||||||
-- timeout would have to be reached for new healthy connections to be acquired.
|
|
||||||
-- Which might not happen if the server is busy with requests. No idle
|
|
||||||
-- connection, no pool timeout.
|
|
||||||
--
|
|
||||||
-- It's also necessary to release the pool connections because they cache the pg catalog(see #2620)
|
|
||||||
--
|
|
||||||
-- The connection tries are capped, but if the connection times out no error is
|
|
||||||
-- thrown, just 'False' is returned.
|
|
||||||
establishConnection :: AppState -> IO ConnectionStatus
|
|
||||||
establishConnection appState@AppState{stateObserver=observer} =
|
|
||||||
retrying retryPolicy shouldRetry $
|
|
||||||
const $ flushPool appState >> getConnectionStatus
|
|
||||||
where
|
|
||||||
getConnectionStatus :: IO ConnectionStatus
|
|
||||||
getConnectionStatus = do
|
|
||||||
pgVersion <- usePool appState (queryPgVersion False) -- No need to prepare the query here, as the connection might not be established
|
|
||||||
case pgVersion of
|
|
||||||
Left e -> do
|
|
||||||
observer $ ConnectionPgVersionErrorObs e
|
|
||||||
putConnStatus appState ConnPending
|
|
||||||
return ConnPending
|
|
||||||
Right version -> do
|
|
||||||
putConnStatus appState ConnEstablished
|
|
||||||
putPgVersion appState version
|
|
||||||
return ConnEstablished
|
|
||||||
|
|
||||||
shouldRetry :: RetryStatus -> ConnectionStatus -> IO Bool
|
|
||||||
shouldRetry rs isConnSucc = do
|
|
||||||
AppConfig{..} <- getConfig appState
|
|
||||||
let
|
|
||||||
delay = fromMaybe 0 (rsPreviousDelay rs) `div` oneSecondInUs
|
|
||||||
itShould = ConnPending == isConnSucc && configDbPoolAutomaticRecovery
|
|
||||||
when itShould $ observer $ ConnectionRetryObs delay
|
|
||||||
when itShould $ putNextDelay appState delay
|
|
||||||
return itShould
|
return itShould
|
||||||
|
|
||||||
retryPolicy :: RetryPolicy
|
retryPolicy :: RetryPolicy
|
||||||
retryPolicy =
|
retryPolicy =
|
||||||
let
|
let delayMicroseconds = 32*oneSecondInUs {-32 seconds-} in
|
||||||
delayMicroseconds = 32000000 -- 32 seconds
|
|
||||||
in
|
|
||||||
capDelay delayMicroseconds $ exponentialBackoff oneSecondInUs
|
capDelay delayMicroseconds $ exponentialBackoff oneSecondInUs
|
||||||
oneSecondInUs = 1000000 -- | One second in microseconds
|
|
||||||
|
|
||||||
-- | Re-reads the config plus config options from the db
|
oneSecondInUs = 1000000 -- one second in microseconds
|
||||||
reReadConfig :: Bool -> AppState -> IO ()
|
|
||||||
reReadConfig startingUp appState@AppState{stateObserver=observer} = do
|
-- | Reads the in-db config and reads the config file again
|
||||||
|
-- | We don't retry reading the in-db config after it fails immediately, because it could have user errors. We just report the error and continue.
|
||||||
|
readInDbConfig :: Bool -> AppState -> IO ()
|
||||||
|
readInDbConfig startingUp appState@AppState{stateObserver=observer} = do
|
||||||
AppConfig{..} <- getConfig appState
|
AppConfig{..} <- getConfig appState
|
||||||
pgVer <- getPgVersion appState
|
pgVer <- getPgVersion appState
|
||||||
dbSettings <-
|
dbSettings <-
|
||||||
|
|||||||
+24
-1
@@ -135,10 +135,33 @@ getJWTFromCache appState token maxLifetime parseJwt utc = do
|
|||||||
authResult <- maybe parseJwt (pure . Right) checkCache
|
authResult <- maybe parseJwt (pure . Right) checkCache
|
||||||
|
|
||||||
case (authResult,checkCache) of
|
case (authResult,checkCache) of
|
||||||
(Right res, Nothing) -> C.insert' (getJwtCache appState) (getTimeSpec res maxLifetime utc) token res
|
-- From comment:
|
||||||
|
-- https://github.com/PostgREST/postgrest/pull/3801#discussion_r1857987914
|
||||||
|
--
|
||||||
|
-- We purge expired cache entries on a cache miss
|
||||||
|
-- The reasoning is that:
|
||||||
|
--
|
||||||
|
-- 1. We expect it to be rare (otherwise there is no point of the cache)
|
||||||
|
-- 2. It makes sure the cache is not growing (as inserting new entries
|
||||||
|
-- does garbage collection)
|
||||||
|
-- 3. Since this is time expiration based cache there is no real risk of
|
||||||
|
-- starvation - sooner or later we are going to have a cache miss.
|
||||||
|
|
||||||
|
(Right res, Nothing) -> do -- cache miss
|
||||||
|
|
||||||
|
let timeSpec = getTimeSpec res maxLifetime utc
|
||||||
|
|
||||||
|
-- purge expired cache entries
|
||||||
|
C.purgeExpired jwtCache
|
||||||
|
|
||||||
|
-- insert new cache entry
|
||||||
|
C.insert' jwtCache timeSpec token res
|
||||||
|
|
||||||
_ -> pure ()
|
_ -> pure ()
|
||||||
|
|
||||||
return authResult
|
return authResult
|
||||||
|
where
|
||||||
|
jwtCache = getJwtCache appState
|
||||||
|
|
||||||
-- Used to extract JWT exp claim and add to JWT Cache
|
-- Used to extract JWT exp claim and add to JWT Cache
|
||||||
getTimeSpec :: AuthResult -> Int -> UTCTime -> Maybe TimeSpec
|
getTimeSpec :: AuthResult -> Int -> UTCTime -> Maybe TimeSpec
|
||||||
|
|||||||
@@ -42,9 +42,11 @@ main CLI{cliCommand, cliPath} = do
|
|||||||
AppState.destroy
|
AppState.destroy
|
||||||
(\appState -> case cliCommand of
|
(\appState -> case cliCommand of
|
||||||
CmdDumpConfig -> do
|
CmdDumpConfig -> do
|
||||||
when configDbConfig $ AppState.reReadConfig True appState
|
when configDbConfig $ AppState.readInDbConfig True appState
|
||||||
putStr . Config.toText =<< AppState.getConfig appState
|
putStr . Config.toText =<< AppState.getConfig appState
|
||||||
CmdDumpSchema -> putStrLn =<< dumpSchema appState
|
CmdDumpSchema -> do
|
||||||
|
when configDbConfig $ AppState.readInDbConfig True appState
|
||||||
|
putStrLn =<< dumpSchema appState
|
||||||
CmdRun -> App.run appState)
|
CmdRun -> App.run appState)
|
||||||
|
|
||||||
-- | Dump SchemaCache schema to JSON
|
-- | Dump SchemaCache schema to JSON
|
||||||
|
|||||||
@@ -27,16 +27,10 @@ runListener appState = do
|
|||||||
-- | Starts a LISTEN connection and handles notifications. It recovers with exponential backoff with a cap of 32 seconds, if the LISTEN connection is lost.
|
-- | Starts a LISTEN connection and handles notifications. It recovers with exponential backoff with a cap of 32 seconds, if the LISTEN connection is lost.
|
||||||
retryingListen :: AppState -> IO ()
|
retryingListen :: AppState -> IO ()
|
||||||
retryingListen appState = do
|
retryingListen appState = do
|
||||||
AppState.waitForListenerCanStart appState
|
|
||||||
AppConfig{..} <- AppState.getConfig appState
|
AppConfig{..} <- AppState.getConfig appState
|
||||||
let
|
let
|
||||||
dbChannel = toS configDbChannel
|
dbChannel = toS configDbChannel
|
||||||
handleFinally err = do
|
handleFinally err = do
|
||||||
-- assume we lost notifications, call the connection worker which will also reload the schema cache
|
|
||||||
-- and will setListenerCanStart again
|
|
||||||
-- TODO: When the connection error is only on the Listener, it's wasteful to call the connectionWorker everytime.
|
|
||||||
AppState.connectionWorker appState
|
|
||||||
|
|
||||||
AppState.putIsListenerOn appState False
|
AppState.putIsListenerOn appState False
|
||||||
observer $ DBListenFail dbChannel (Right err)
|
observer $ DBListenFail dbChannel (Right err)
|
||||||
unless configDbPoolAutomaticRecovery $
|
unless configDbPoolAutomaticRecovery $
|
||||||
@@ -60,6 +54,8 @@ retryingListen appState = do
|
|||||||
|
|
||||||
delay <- AppState.getNextListenerDelay appState
|
delay <- AppState.getNextListenerDelay appState
|
||||||
when (delay > 1) $ do -- if we did a retry
|
when (delay > 1) $ do -- if we did a retry
|
||||||
|
-- assume we lost notifications, refresh the schema cache
|
||||||
|
AppState.schemaCacheLoader appState
|
||||||
-- reset the delay
|
-- reset the delay
|
||||||
AppState.putNextListenerDelay appState 1
|
AppState.putNextListenerDelay appState 1
|
||||||
|
|
||||||
@@ -78,8 +74,8 @@ retryingListen appState = do
|
|||||||
handleNotification channel msg =
|
handleNotification channel msg =
|
||||||
if | BS.null msg -> observer (DBListenerGotSCacheMsg channel) >> cacheReloader
|
if | BS.null msg -> observer (DBListenerGotSCacheMsg channel) >> cacheReloader
|
||||||
| msg == "reload schema" -> observer (DBListenerGotSCacheMsg channel) >> cacheReloader
|
| msg == "reload schema" -> observer (DBListenerGotSCacheMsg channel) >> cacheReloader
|
||||||
| msg == "reload config" -> observer (DBListenerGotConfigMsg channel) >> AppState.reReadConfig False appState
|
| msg == "reload config" -> observer (DBListenerGotConfigMsg channel) >> AppState.readInDbConfig False appState
|
||||||
| otherwise -> pure () -- Do nothing if anything else than an empty message is sent
|
| otherwise -> pure () -- Do nothing if anything else than an empty message is sent
|
||||||
|
|
||||||
cacheReloader =
|
cacheReloader =
|
||||||
AppState.connectionWorker appState
|
AppState.schemaCacheLoader appState
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ observationLogger loggerState logLevel obs = case obs of
|
|||||||
o@(HasqlPoolObs _) -> do
|
o@(HasqlPoolObs _) -> do
|
||||||
when (logLevel >= LogDebug) $ do
|
when (logLevel >= LogDebug) $ do
|
||||||
logWithZTime loggerState $ observationMessage o
|
logWithZTime loggerState $ observationMessage o
|
||||||
o@(SchemaCacheLoadedObs _) -> do
|
|
||||||
when (logLevel >= LogDebug) $ do
|
|
||||||
logWithZTime loggerState $ observationMessage o
|
|
||||||
PoolRequest ->
|
PoolRequest ->
|
||||||
pure ()
|
pure ()
|
||||||
PoolRequestFullfilled ->
|
PoolRequestFullfilled ->
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ data Observation
|
|||||||
| AppStartObs ByteString
|
| AppStartObs ByteString
|
||||||
| AppServerPortObs NS.PortNumber
|
| AppServerPortObs NS.PortNumber
|
||||||
| AppServerUnixObs FilePath
|
| AppServerUnixObs FilePath
|
||||||
| DBConnectAttemptObs
|
|
||||||
| ExitUnsupportedPgVersion PgVersion PgVersion
|
| ExitUnsupportedPgVersion PgVersion PgVersion
|
||||||
| ExitDBNoRecoveryObs
|
| ExitDBNoRecoveryObs
|
||||||
| ExitDBFatalError ObsFatalError SQL.UsageError
|
| ExitDBFatalError ObsFatalError SQL.UsageError
|
||||||
@@ -39,7 +38,6 @@ data Observation
|
|||||||
| SchemaCacheSummaryObs Text
|
| SchemaCacheSummaryObs Text
|
||||||
| SchemaCacheLoadedObs Double
|
| SchemaCacheLoadedObs Double
|
||||||
| ConnectionRetryObs Int
|
| ConnectionRetryObs Int
|
||||||
| ConnectionPgVersionErrorObs SQL.UsageError
|
|
||||||
| DBListenStart Text
|
| DBListenStart Text
|
||||||
| DBListenFail Text (Either SQL.ConnectionError (Either SomeException ()))
|
| DBListenFail Text (Either SQL.ConnectionError (Either SomeException ()))
|
||||||
| DBListenRetry Int
|
| DBListenRetry Int
|
||||||
@@ -50,6 +48,7 @@ data Observation
|
|||||||
| ConfigSucceededObs
|
| ConfigSucceededObs
|
||||||
| QueryRoleSettingsErrorObs SQL.UsageError
|
| QueryRoleSettingsErrorObs SQL.UsageError
|
||||||
| QueryErrorCodeHighObs SQL.UsageError
|
| QueryErrorCodeHighObs SQL.UsageError
|
||||||
|
| QueryPgVersionError SQL.UsageError
|
||||||
| PoolAcqTimeoutObs SQL.UsageError
|
| PoolAcqTimeoutObs SQL.UsageError
|
||||||
| HasqlPoolObs SQL.Observation
|
| HasqlPoolObs SQL.Observation
|
||||||
| PoolRequest
|
| PoolRequest
|
||||||
@@ -69,8 +68,6 @@ observationMessage = \case
|
|||||||
"Listening on port " <> show port
|
"Listening on port " <> show port
|
||||||
AppServerUnixObs sock ->
|
AppServerUnixObs sock ->
|
||||||
"Listening on unix socket " <> show sock
|
"Listening on unix socket " <> show sock
|
||||||
DBConnectAttemptObs ->
|
|
||||||
"Attempting to connect to the database..."
|
|
||||||
DBConnectedObs ver ->
|
DBConnectedObs ver ->
|
||||||
"Successfully connected to " <> ver
|
"Successfully connected to " <> ver
|
||||||
ExitUnsupportedPgVersion pgVer minPgVer ->
|
ExitUnsupportedPgVersion pgVer minPgVer ->
|
||||||
@@ -78,7 +75,7 @@ observationMessage = \case
|
|||||||
ExitDBNoRecoveryObs ->
|
ExitDBNoRecoveryObs ->
|
||||||
"Automatic recovery disabled, exiting."
|
"Automatic recovery disabled, exiting."
|
||||||
ExitDBFatalError ServerAuthError usageErr ->
|
ExitDBFatalError ServerAuthError usageErr ->
|
||||||
jsonMessage usageErr
|
"Failed to establish a connection. " <> jsonMessage usageErr
|
||||||
ExitDBFatalError ServerPgrstBug usageErr ->
|
ExitDBFatalError ServerPgrstBug usageErr ->
|
||||||
"This is probably a bug in PostgREST, please report it at https://github.com/PostgREST/postgrest/issues. " <> jsonMessage usageErr
|
"This is probably a bug in PostgREST, please report it at https://github.com/PostgREST/postgrest/issues. " <> jsonMessage usageErr
|
||||||
ExitDBFatalError ServerError42P05 usageErr ->
|
ExitDBFatalError ServerError42P05 usageErr ->
|
||||||
@@ -95,8 +92,8 @@ observationMessage = \case
|
|||||||
"Schema cache loaded in " <> showMillis resultTime <> " milliseconds"
|
"Schema cache loaded in " <> showMillis resultTime <> " milliseconds"
|
||||||
ConnectionRetryObs delay ->
|
ConnectionRetryObs delay ->
|
||||||
"Attempting to reconnect to the database in " <> (show delay::Text) <> " seconds..."
|
"Attempting to reconnect to the database in " <> (show delay::Text) <> " seconds..."
|
||||||
ConnectionPgVersionErrorObs usageErr ->
|
QueryPgVersionError usageErr ->
|
||||||
jsonMessage usageErr
|
"Failed to query the PostgreSQL version. " <> jsonMessage usageErr
|
||||||
DBListenStart channel -> do
|
DBListenStart channel -> do
|
||||||
"Listening for notifications on the " <> show channel <> " channel"
|
"Listening for notifications on the " <> show channel <> " channel"
|
||||||
DBListenFail channel listenErr ->
|
DBListenFail channel listenErr ->
|
||||||
|
|||||||
@@ -622,7 +622,11 @@ generateRelSelectField (Node ReadPlan{relToParent=Just rel, select, relName, rel
|
|||||||
where
|
where
|
||||||
rsSelName = fromMaybe relName relAlias
|
rsSelName = fromMaybe relName relAlias
|
||||||
rsEmbedMode = if relIsToOne rel then JsonObject else JsonArray
|
rsEmbedMode = if relIsToOne rel then JsonObject else JsonArray
|
||||||
rsEmptyEmbed = null select && null forest
|
rsEmptyEmbed = hasOnlyNullEmbed (null select) forest
|
||||||
|
hasOnlyNullEmbed = foldr checkIfNullEmbed
|
||||||
|
checkIfNullEmbed :: ReadPlanTree -> Bool -> Bool
|
||||||
|
checkIfNullEmbed (Node ReadPlan{select=s} f) isNullEmbed =
|
||||||
|
isNullEmbed && hasOnlyNullEmbed (null s) f
|
||||||
generateRelSelectField _ = Nothing
|
generateRelSelectField _ = Nothing
|
||||||
|
|
||||||
generateSpreadSelectFields :: ReadPlan -> [SpreadSelectField]
|
generateSpreadSelectFields :: ReadPlan -> [SpreadSelectField]
|
||||||
|
|||||||
@@ -296,8 +296,7 @@ makeRowFilter tn c =
|
|||||||
& required ?~ False
|
& required ?~ False
|
||||||
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
& schema .~ ParamOther ((mempty :: ParamOtherSchema)
|
||||||
& in_ .~ ParamQuery
|
& in_ .~ ParamQuery
|
||||||
& type_ ?~ SwaggerString
|
& type_ ?~ SwaggerString))
|
||||||
& format ?~ colType c))
|
|
||||||
|
|
||||||
makeRowFilters :: Text -> [Column] -> [(Text, Param)]
|
makeRowFilters :: Text -> [Column] -> [(Text, Param)]
|
||||||
makeRowFilters tn = fmap (makeRowFilter tn)
|
makeRowFilters tn = fmap (makeRowFilter tn)
|
||||||
@@ -402,7 +401,7 @@ postgrestSpec (prettyVersion, docsVersion) rels pds ti (s, h, p, b) sd allowSecu
|
|||||||
& description ?~ fromMaybe "This is a dynamic API generated by PostgREST" dDesc)
|
& description ?~ fromMaybe "This is a dynamic API generated by PostgREST" dDesc)
|
||||||
& externalDocs ?~ ((mempty :: ExternalDocs)
|
& externalDocs ?~ ((mempty :: ExternalDocs)
|
||||||
& description ?~ "PostgREST Documentation"
|
& description ?~ "PostgREST Documentation"
|
||||||
& url .~ URL ("https://postgrest.org/en/" <> docsVersion <> "/api.html"))
|
& url .~ URL ("https://postgrest.org/en/" <> docsVersion <> "/references/api.html"))
|
||||||
& host .~ h'
|
& host .~ h'
|
||||||
& definitions .~ fromList (makeTableDef rels <$> ti)
|
& definitions .~ fromList (makeTableDef rels <$> ti)
|
||||||
& parameters .~ fromList (makeParamDefs ti)
|
& parameters .~ fromList (makeParamDefs ti)
|
||||||
|
|||||||
@@ -95,13 +95,14 @@ instance JSON.ToJSON SchemaCache where
|
|||||||
]
|
]
|
||||||
|
|
||||||
showSummary :: SchemaCache -> Text
|
showSummary :: SchemaCache -> Text
|
||||||
showSummary (SchemaCache tbls rels routs reps mediaHdlrs _) =
|
showSummary (SchemaCache tbls rels routs reps mediaHdlrs tzs) =
|
||||||
T.intercalate ", "
|
T.intercalate ", "
|
||||||
[ show (HM.size tbls) <> " Relations"
|
[ show (HM.size tbls) <> " Relations"
|
||||||
, show (HM.size rels) <> " Relationships"
|
, show (HM.size rels) <> " Relationships"
|
||||||
, show (HM.size routs) <> " Functions"
|
, show (HM.size routs) <> " Functions"
|
||||||
, show (HM.size reps) <> " Domain Representations"
|
, show (HM.size reps) <> " Domain Representations"
|
||||||
, show (HM.size mediaHdlrs) <> " Media Type Handlers"
|
, show (HM.size mediaHdlrs) <> " Media Type Handlers"
|
||||||
|
, show (S.size tzs) <> " Timezones"
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | A view foreign key or primary key dependency detected on its source table
|
-- | A view foreign key or primary key dependency detected on its source table
|
||||||
@@ -646,7 +647,7 @@ tablesSqlQuery pgVer =
|
|||||||
information_schema._pg_truetypid(a.*, t.*),
|
information_schema._pg_truetypid(a.*, t.*),
|
||||||
information_schema._pg_truetypmod(a.*, t.*)
|
information_schema._pg_truetypmod(a.*, t.*)
|
||||||
)::integer AS character_maximum_length,
|
)::integer AS character_maximum_length,
|
||||||
COALESCE(bt.typname, t.typname)::name AS udt_name,
|
COALESCE(bt.oid, t.oid) AS base_type,
|
||||||
a.attnum::integer AS position
|
a.attnum::integer AS position
|
||||||
FROM pg_attribute a
|
FROM pg_attribute a
|
||||||
LEFT JOIN pg_description AS d
|
LEFT JOIN pg_description AS d
|
||||||
@@ -690,14 +691,13 @@ tablesSqlQuery pgVer =
|
|||||||
FROM columns info
|
FROM columns info
|
||||||
LEFT OUTER JOIN (
|
LEFT OUTER JOIN (
|
||||||
SELECT
|
SELECT
|
||||||
n.nspname AS s,
|
e.enumtypid,
|
||||||
t.typname AS n,
|
|
||||||
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS vals
|
array_agg(e.enumlabel ORDER BY e.enumsortorder) AS vals
|
||||||
FROM pg_type t
|
FROM pg_type t
|
||||||
JOIN pg_enum e ON t.oid = e.enumtypid
|
JOIN pg_enum e ON t.oid = e.enumtypid
|
||||||
JOIN pg_namespace n ON n.oid = t.typnamespace
|
JOIN pg_namespace n ON n.oid = t.typnamespace
|
||||||
GROUP BY s,n
|
GROUP BY enumtypid
|
||||||
) AS enum_info ON info.udt_name = enum_info.n
|
) AS enum_info ON info.base_type = enum_info.enumtypid
|
||||||
WHERE info.table_schema NOT IN ('pg_catalog', 'information_schema')
|
WHERE info.table_schema NOT IN ('pg_catalog', 'information_schema')
|
||||||
GROUP BY info.table_schema, info.table_name
|
GROUP BY info.table_schema, info.table_name
|
||||||
),
|
),
|
||||||
@@ -818,20 +818,20 @@ tablesSqlQuery pgVer =
|
|||||||
columnDefault -- typbasetype and typdefaultbin handles `CREATE DOMAIN .. DEFAULT val`, attidentity/attgenerated handles generated columns, pg_get_expr gets the default of a column
|
columnDefault -- typbasetype and typdefaultbin handles `CREATE DOMAIN .. DEFAULT val`, attidentity/attgenerated handles generated columns, pg_get_expr gets the default of a column
|
||||||
| pgVer >= pgVersion120 = [q|
|
| pgVer >= pgVersion120 = [q|
|
||||||
CASE
|
CASE
|
||||||
WHEN t.typbasetype != 0 THEN pg_get_expr(t.typdefaultbin, 0)
|
WHEN (t.typbasetype != 0) AND (ad.adbin IS NULL) THEN pg_get_expr(t.typdefaultbin, 0)
|
||||||
WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname))
|
WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname))
|
||||||
WHEN a.attgenerated = 's' THEN null
|
WHEN a.attgenerated = 's' THEN null
|
||||||
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
|
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
|
||||||
END|]
|
END|]
|
||||||
| pgVer >= pgVersion100 = [q|
|
| pgVer >= pgVersion100 = [q|
|
||||||
CASE
|
CASE
|
||||||
WHEN t.typbasetype != 0 THEN pg_get_expr(t.typdefaultbin, 0)
|
WHEN (t.typbasetype != 0) AND (ad.adbin IS NULL) THEN pg_get_expr(t.typdefaultbin, 0)
|
||||||
WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname))
|
WHEN a.attidentity = 'd' THEN format('nextval(%s)', quote_literal(seqsch.nspname || '.' || seqclass.relname))
|
||||||
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
|
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
|
||||||
END|]
|
END|]
|
||||||
| otherwise = [q|
|
| otherwise = [q|
|
||||||
CASE
|
CASE
|
||||||
WHEN t.typbasetype != 0 THEN pg_get_expr(t.typdefaultbin, 0)
|
WHEN (t.typbasetype != 0) AND (ad.adbin IS NULL) THEN pg_get_expr(t.typdefaultbin, 0)
|
||||||
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
|
ELSE pg_get_expr(ad.adbin, ad.adrelid)::text
|
||||||
END|]
|
END|]
|
||||||
|
|
||||||
@@ -872,7 +872,7 @@ allM2OandO2ORels pgVer =
|
|||||||
JOIN LATERAL (
|
JOIN LATERAL (
|
||||||
SELECT
|
SELECT
|
||||||
array_agg(row(cols.attname, refs.attname) order by ord) AS cols_and_fcols,
|
array_agg(row(cols.attname, refs.attname) order by ord) AS cols_and_fcols,
|
||||||
jsonb_agg(cols.attname order by ord) AS cols
|
jsonb_agg(cols.attname order by cols.attnum) AS cols
|
||||||
FROM unnest(traint.conkey, traint.confkey) WITH ORDINALITY AS _(col, ref, ord)
|
FROM unnest(traint.conkey, traint.confkey) WITH ORDINALITY AS _(col, ref, ord)
|
||||||
JOIN pg_attribute cols ON cols.attrelid = traint.conrelid AND cols.attnum = col
|
JOIN pg_attribute cols ON cols.attrelid = traint.conrelid AND cols.attnum = col
|
||||||
JOIN pg_attribute refs ON refs.attrelid = traint.confrelid AND refs.attnum = ref
|
JOIN pg_attribute refs ON refs.attrelid = traint.confrelid AND refs.attnum = ref
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import Protolude
|
|||||||
|
|
||||||
|
|
||||||
-- | User friendly version number such as '1.1.1'.
|
-- | User friendly version number such as '1.1.1'.
|
||||||
-- Pre-release versions are tagged as such, e.g., '1.1.1.1 (pre-release)'.
|
-- Pre-release versions are tagged as such, e.g., '1.1 (pre-release)'.
|
||||||
-- If a git hash is available, it's added to the version, e.g., '1.1.1 (abcdef0)'.
|
-- If a git hash is available, it's added to the version, e.g., '1.1.1 (abcdef0)'.
|
||||||
prettyVersion :: ByteString
|
prettyVersion :: ByteString
|
||||||
prettyVersion =
|
prettyVersion =
|
||||||
@@ -31,14 +31,14 @@ prettyVersion =
|
|||||||
|
|
||||||
-- | Version number used in docs.
|
-- | Version number used in docs.
|
||||||
-- Pre-release versions link to the latest docs
|
-- Pre-release versions link to the latest docs
|
||||||
-- Uses only the two first components of the version. Example: 'v1.1'
|
-- Uses only the first component of the version. Example: 'v1'
|
||||||
docsVersion :: Text
|
docsVersion :: Text
|
||||||
docsVersion
|
docsVersion
|
||||||
| isPreRelease = "latest"
|
| isPreRelease = "latest"
|
||||||
| otherwise = "v" <> (T.intercalate "." . map show . take 2 $ versionBranch version)
|
| otherwise = "v" <> (T.intercalate "." . map show . take 1 $ versionBranch version)
|
||||||
|
|
||||||
|
|
||||||
-- | Versions with four components (e.g., '1.1.1.1') are treated as pre-releases.
|
-- | Versions with two components (e.g., '1.1') are treated as pre-releases.
|
||||||
isPreRelease :: Bool
|
isPreRelease :: Bool
|
||||||
isPreRelease =
|
isPreRelease =
|
||||||
length (versionBranch version) == 4
|
length (versionBranch version) == 2
|
||||||
|
|||||||
+1
-1
@@ -19,6 +19,6 @@ nix:
|
|||||||
extra-deps:
|
extra-deps:
|
||||||
- configurator-pg-0.2.10
|
- configurator-pg-0.2.10
|
||||||
- fuzzyset-0.2.4
|
- fuzzyset-0.2.4
|
||||||
- hasql-notifications-0.2.2.0
|
- hasql-notifications-0.2.2.2
|
||||||
- hasql-pool-1.0.1
|
- hasql-pool-1.0.1
|
||||||
- postgresql-libpq-0.10.1.0
|
- postgresql-libpq-0.10.1.0
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ packages:
|
|||||||
original:
|
original:
|
||||||
hackage: fuzzyset-0.2.4
|
hackage: fuzzyset-0.2.4
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-notifications-0.2.2.0@sha256:a4e591ef3f06647b056567d3b66948c4a85371f05deb5434edb6ce190f7c845d,2021
|
hackage: hasql-notifications-0.2.2.2@sha256:d1d6bc0d3ee5e418fc12ea023b78739e0decba6c34e2b43bec55b89e18bd4412,2025
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
sha256: bd7192a5e82ef6dbac711c3433408a0330c8db1cd3482be1ccd4fbd0a63bc2f6
|
sha256: 83a9cbb179b1efd0b2acd6509583c7afcdbe63469ab033d8581d48d675a80b44
|
||||||
size: 452
|
size: 452
|
||||||
original:
|
original:
|
||||||
hackage: hasql-notifications-0.2.2.0
|
hackage: hasql-notifications-0.2.2.2
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-pool-1.0.1@sha256:3cfb4c7153a6c536ac7e126c17723e6d26ee03794954deed2d72bcc826d05a40,2302
|
hackage: hasql-pool-1.0.1@sha256:3cfb4c7153a6c536ac7e126c17723e6d26ee03794954deed2d72bcc826d05a40,2302
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
|
|||||||
+1
-1
@@ -11,5 +11,5 @@ nix:
|
|||||||
|
|
||||||
extra-deps:
|
extra-deps:
|
||||||
- fuzzyset-0.2.4
|
- fuzzyset-0.2.4
|
||||||
- hasql-notifications-0.2.2.0
|
- hasql-notifications-0.2.2.2
|
||||||
- hasql-pool-1.0.1
|
- hasql-pool-1.0.1
|
||||||
|
|||||||
+3
-3
@@ -12,12 +12,12 @@ packages:
|
|||||||
original:
|
original:
|
||||||
hackage: fuzzyset-0.2.4
|
hackage: fuzzyset-0.2.4
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-notifications-0.2.2.0@sha256:a4e591ef3f06647b056567d3b66948c4a85371f05deb5434edb6ce190f7c845d,2021
|
hackage: hasql-notifications-0.2.2.2@sha256:d1d6bc0d3ee5e418fc12ea023b78739e0decba6c34e2b43bec55b89e18bd4412,2025
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
sha256: bd7192a5e82ef6dbac711c3433408a0330c8db1cd3482be1ccd4fbd0a63bc2f6
|
sha256: 83a9cbb179b1efd0b2acd6509583c7afcdbe63469ab033d8581d48d675a80b44
|
||||||
size: 452
|
size: 452
|
||||||
original:
|
original:
|
||||||
hackage: hasql-notifications-0.2.2.0
|
hackage: hasql-notifications-0.2.2.2
|
||||||
- completed:
|
- completed:
|
||||||
hackage: hasql-pool-1.0.1@sha256:3cfb4c7153a6c536ac7e126c17723e6d26ee03794954deed2d72bcc826d05a40,2302
|
hackage: hasql-pool-1.0.1@sha256:3cfb4c7153a6c536ac7e126c17723e6d26ee03794954deed2d72bcc826d05a40,2302
|
||||||
pantry-tree:
|
pantry-tree:
|
||||||
|
|||||||
@@ -218,6 +218,40 @@
|
|||||||
pdSchema: public
|
pdSchema: public
|
||||||
pdVolatility: Volatile
|
pdVolatility: Volatile
|
||||||
|
|
||||||
|
- - qiName: reset_db_schemas_config
|
||||||
|
qiSchema: public
|
||||||
|
- - pdDescription: null
|
||||||
|
pdFuncSettings: []
|
||||||
|
pdHasVariadic: false
|
||||||
|
pdName: reset_db_schemas_config
|
||||||
|
pdParams: []
|
||||||
|
pdReturnType:
|
||||||
|
contents:
|
||||||
|
contents:
|
||||||
|
qiName: void
|
||||||
|
qiSchema: pg_catalog
|
||||||
|
tag: Scalar
|
||||||
|
tag: Single
|
||||||
|
pdSchema: public
|
||||||
|
pdVolatility: Volatile
|
||||||
|
|
||||||
|
- - qiName: change_db_schemas_config
|
||||||
|
qiSchema: public
|
||||||
|
- - pdDescription: null
|
||||||
|
pdFuncSettings: []
|
||||||
|
pdHasVariadic: false
|
||||||
|
pdName: change_db_schemas_config
|
||||||
|
pdParams: []
|
||||||
|
pdReturnType:
|
||||||
|
contents:
|
||||||
|
contents:
|
||||||
|
qiName: void
|
||||||
|
qiSchema: pg_catalog
|
||||||
|
tag: Scalar
|
||||||
|
tag: Single
|
||||||
|
pdSchema: public
|
||||||
|
pdVolatility: Volatile
|
||||||
|
|
||||||
- - qiName: change_db_schema_and_full_reload
|
- - qiName: change_db_schema_and_full_reload
|
||||||
qiSchema: public
|
qiSchema: public
|
||||||
- - pdDescription: null
|
- - pdDescription: null
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ GRANT
|
|||||||
CREATE SCHEMA v1;
|
CREATE SCHEMA v1;
|
||||||
GRANT USAGE ON SCHEMA v1 TO postgrest_test_anonymous;
|
GRANT USAGE ON SCHEMA v1 TO postgrest_test_anonymous;
|
||||||
|
|
||||||
|
CREATE SCHEMA test;
|
||||||
|
GRANT USAGE ON SCHEMA test TO postgrest_test_anonymous;
|
||||||
|
|
||||||
CREATE TABLE authors_only ();
|
CREATE TABLE authors_only ();
|
||||||
GRANT SELECT ON authors_only TO postgrest_test_author;
|
GRANT SELECT ON authors_only TO postgrest_test_author;
|
||||||
|
|
||||||
@@ -226,3 +229,17 @@ $$ language sql;
|
|||||||
create function get_statement_timeout(items) returns text as $$
|
create function get_statement_timeout(items) returns text as $$
|
||||||
select current_setting('statement_timeout', true) as statement_timeout
|
select current_setting('statement_timeout', true) as statement_timeout
|
||||||
$$ language sql;
|
$$ language sql;
|
||||||
|
|
||||||
|
create function change_db_schemas_config() returns void as $_$
|
||||||
|
begin
|
||||||
|
alter role postgrest_test_authenticator set pgrst.db_schemas = 'test';
|
||||||
|
end $_$ volatile security definer language plpgsql;
|
||||||
|
|
||||||
|
create function reset_db_schemas_config() returns void as $_$
|
||||||
|
begin
|
||||||
|
alter role postgrest_test_authenticator reset pgrst.db_schemas;
|
||||||
|
end $_$ volatile security definer language plpgsql ;
|
||||||
|
|
||||||
|
create function test.get_current_schema() returns text as $$
|
||||||
|
select current_schema()::text;
|
||||||
|
$$ language sql;
|
||||||
|
|||||||
+85
-1
@@ -778,7 +778,7 @@ def test_metrics_include_schema_cache_fails(defaultenv, metapostgrest):
|
|||||||
r'pgrst_schema_cache_loads_total{status="FAIL"} (\d+)', response.text
|
r'pgrst_schema_cache_loads_total{status="FAIL"} (\d+)', response.text
|
||||||
).group(1)
|
).group(1)
|
||||||
)
|
)
|
||||||
assert metrics > 3.0
|
assert metrics == 1.0
|
||||||
|
|
||||||
reset_statement_timeout(metapostgrest, role)
|
reset_statement_timeout(metapostgrest, role)
|
||||||
|
|
||||||
@@ -1579,3 +1579,87 @@ def test_admin_metrics(defaultenv):
|
|||||||
assert "pgrst_db_pool_waiting" in response.text
|
assert "pgrst_db_pool_waiting" in response.text
|
||||||
assert "pgrst_db_pool_available" in response.text
|
assert "pgrst_db_pool_available" in response.text
|
||||||
assert "pgrst_db_pool_timeouts_total" in response.text
|
assert "pgrst_db_pool_timeouts_total" in response.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_schema_cache_startup_load_with_in_db_config(defaultenv, metapostgrest):
|
||||||
|
"verify that the Schema Cache loads correctly at startup, using the in-db `pgrst.db_schemas` config"
|
||||||
|
|
||||||
|
response = metapostgrest.session.post("/rpc/change_db_schemas_config")
|
||||||
|
assert response.text == ""
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
with run(env=defaultenv) as postgrest:
|
||||||
|
response = postgrest.session.get("/rpc/get_current_schema")
|
||||||
|
assert response.text == '"test"'
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
response = metapostgrest.session.post("/rpc/reset_db_schemas_config")
|
||||||
|
assert response.text == ""
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
|
||||||
|
def test_jwt_cache_purges_expired_entries(defaultenv):
|
||||||
|
"test expired cache entries are purged on cache miss"
|
||||||
|
|
||||||
|
# The verification of actual cache size reduction is done manually, see https://github.com/PostgREST/postgrest/pull/3801#issuecomment-2620776041
|
||||||
|
# This test is written for code coverage of purgeExpired function
|
||||||
|
|
||||||
|
relativeSeconds = lambda sec: int(
|
||||||
|
(datetime.now(timezone.utc) + timedelta(seconds=sec)).timestamp()
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = lambda sec: jwtauthheader(
|
||||||
|
{"role": "postgrest_test_author", "exp": relativeSeconds(sec)},
|
||||||
|
SECRET,
|
||||||
|
)
|
||||||
|
|
||||||
|
env = {
|
||||||
|
**defaultenv,
|
||||||
|
"PGRST_JWT_CACHE_MAX_LIFETIME": "86400",
|
||||||
|
"PGRST_JWT_SECRET": SECRET,
|
||||||
|
"PGRST_DB_CONFIG": "false",
|
||||||
|
}
|
||||||
|
|
||||||
|
with run(env=env) as postgrest:
|
||||||
|
|
||||||
|
# Generate two unique JWT tokens
|
||||||
|
# The 1 second sleep is needed for it generate a unique token
|
||||||
|
hdrs1 = headers(5)
|
||||||
|
postgrest.session.get("/authors_only", headers=hdrs1)
|
||||||
|
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
hdrs2 = headers(5)
|
||||||
|
postgrest.session.get("/authors_only", headers=hdrs2)
|
||||||
|
|
||||||
|
# Wait 5 seconds for the tokens to expire
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
|
hdrs3 = headers(5)
|
||||||
|
|
||||||
|
# Make another request which should cause a cache miss and so
|
||||||
|
# the purgeExpired function will be triggered.
|
||||||
|
#
|
||||||
|
# This should remove the 2 expired tokens but adds another to cache
|
||||||
|
response = postgrest.session.get("/authors_only", headers=hdrs3)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_pgrst_log_503_client_error_to_stderr(defaultenv):
|
||||||
|
"PostgREST should log 503 errors to stderr"
|
||||||
|
|
||||||
|
env = {
|
||||||
|
**defaultenv,
|
||||||
|
"PGAPPNAME": "test-io",
|
||||||
|
}
|
||||||
|
|
||||||
|
with run(env=env) as postgrest:
|
||||||
|
|
||||||
|
postgrest.session.get("/rpc/terminate_pgrst?appname=test-io")
|
||||||
|
|
||||||
|
output = postgrest.read_stdout(nlines=6)
|
||||||
|
|
||||||
|
log_message = '{"code":"PGRST001","details":"no connection to the server\\n","hint":null,"message":"Database client error. Retrying the connection."}\n'
|
||||||
|
|
||||||
|
assert any(log_message in line for line in output)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ spec actualPgVersion = describe "OpenAPI" $ do
|
|||||||
|
|
||||||
let docsUrl = r ^? key "externalDocs" . key "url"
|
let docsUrl = r ^? key "externalDocs" . key "url"
|
||||||
|
|
||||||
liftIO $ docsUrl `shouldBe` Just (String ("https://postgrest.org/en/" <> docsVersion <> "/api.html"))
|
liftIO $ docsUrl `shouldBe` Just (String ("https://postgrest.org/en/" <> docsVersion <> "/references/api.html"))
|
||||||
|
|
||||||
describe "schema" $ do
|
describe "schema" $ do
|
||||||
|
|
||||||
|
|||||||
@@ -579,6 +579,15 @@ spec actualPgVersion = do
|
|||||||
, matchHeaders = ["Preference-Applied" <:> "missing=default, return=representation"]
|
, matchHeaders = ["Preference-Applied" <:> "missing=default, return=representation"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it "inserts a COLUMN default before a DOMAIN default with missing=default" $
|
||||||
|
request methodPost "/evil_friends_with_column_default?columns=id,name" [("Prefer", "return=representation"), ("Prefer", "missing=default")]
|
||||||
|
[json| { "name": "Demon" } |]
|
||||||
|
`shouldRespondWith`
|
||||||
|
[json| [{"id": 420, "name": "Demon"}] |]
|
||||||
|
{ matchStatus = 201
|
||||||
|
, matchHeaders = ["Preference-Applied" <:> "missing=default, return=representation"]
|
||||||
|
}
|
||||||
|
|
||||||
it "inserts json that has duplicate keys" $ do
|
it "inserts json that has duplicate keys" $ do
|
||||||
request methodPost "/tbl_w_json" [("Prefer", "return=representation")]
|
request methodPost "/tbl_w_json" [("Prefer", "return=representation")]
|
||||||
[json| { "data": { "a": 1, "a": 2 }, "id": 3 } |]
|
[json| { "data": { "a": 1, "a": 2 }, "id": 3 } |]
|
||||||
|
|||||||
@@ -1218,6 +1218,20 @@ spec actualPgVersion = do
|
|||||||
[json|[{"id":1,"name":"Angela Martin"}]|]
|
[json|[{"id":1,"name":"Angela Martin"}]|]
|
||||||
{ matchHeaders = [matchContentTypeJson] }
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
|
it "works on nested relationships" $ do
|
||||||
|
get "/users?select=*,users_tasks(tasks(projects()))" `shouldRespondWith`
|
||||||
|
[json| [{"id":1,"name":"Angela Martin"}, {"id":2,"name":"Michael Scott"}, {"id":3,"name":"Dwight Schrute"}]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
get "/users?select=*,users_tasks!inner(tasks!inner(projects()))&users_tasks.tasks.id=eq.3" `shouldRespondWith`
|
||||||
|
[json| [{"id":1,"name":"Angela Martin"}]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
get "/users?select=*,tasks(projects(clients()),users_tasks())" `shouldRespondWith`
|
||||||
|
[json| [{"id":1,"name":"Angela Martin"}, {"id":2,"name":"Michael Scott"}, {"id":3,"name":"Dwight Schrute"}]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
get "/users?select=*,tasks!inner(projects(clients()),users_tasks(),name)&tasks.id=eq.3" `shouldRespondWith`
|
||||||
|
[json| [{"id":1,"name":"Angela Martin","tasks":[{"name": "Design w10"}]}]|]
|
||||||
|
{ matchHeaders = [matchContentTypeJson] }
|
||||||
|
|
||||||
context "empty root select" $
|
context "empty root select" $
|
||||||
it "gives all columns" $ do
|
it "gives all columns" $ do
|
||||||
get "/projects?select=" `shouldRespondWith`
|
get "/projects?select=" `shouldRespondWith`
|
||||||
@@ -1413,4 +1427,3 @@ spec actualPgVersion = do
|
|||||||
get "/infinite_recursion?select=*" `shouldRespondWith`
|
get "/infinite_recursion?select=*" `shouldRespondWith`
|
||||||
[json|{"code":"42P17","message":"infinite recursion detected in rules for relation \"infinite_recursion\"","details":null,"hint":null}|]
|
[json|{"code":"42P17","message":"infinite recursion detected in rules for relation \"infinite_recursion\"","details":null,"hint":null}|]
|
||||||
{ matchStatus = 500 }
|
{ matchStatus = 500 }
|
||||||
|
|
||||||
|
|||||||
Vendored
+6
-1
@@ -2872,7 +2872,7 @@ CREATE TABLE test.students_info(
|
|||||||
, code text
|
, code text
|
||||||
, address text
|
, address text
|
||||||
, primary key(id, code)
|
, primary key(id, code)
|
||||||
, foreign key (id, code) references test.students(id, code) on delete cascade
|
, foreign key (code, id) references test.students(code, id) on delete cascade
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE test.country(
|
CREATE TABLE test.country(
|
||||||
@@ -3319,6 +3319,11 @@ create table evil_friends(
|
|||||||
, name text
|
, name text
|
||||||
);
|
);
|
||||||
|
|
||||||
|
create table evil_friends_with_column_default(
|
||||||
|
id devil_int default 420
|
||||||
|
, name text
|
||||||
|
);
|
||||||
|
|
||||||
create table bets (
|
create table bets (
|
||||||
id int
|
id int
|
||||||
, data_json json
|
, data_json json
|
||||||
|
|||||||
Reference in New Issue
Block a user