Compare commits
@@ -1,5 +1,5 @@
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-0
|
||||
image_family: freebsd-14-3
|
||||
|
||||
build_task:
|
||||
# Don't change this name without adjusting .github/workflows/build.yaml
|
||||
@@ -35,7 +35,7 @@ build_task:
|
||||
- find main src -type f -iname '*.hs' -exec md5sum "{}" +
|
||||
|
||||
build_script: |
|
||||
stack build -j 1 --local-bin-path . --copy-bins --stack-yaml stack-21.7.yaml
|
||||
stack build -j 1 --local-bin-path . --copy-bins
|
||||
strip postgrest
|
||||
|
||||
bin_artifacts:
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
@@ -1,17 +0,0 @@
|
||||
<!--
|
||||
Before reporting a bug:
|
||||
If your database schema has changed while the PostgREST server is running,
|
||||
send the server a SIGUSR1 signal or restart it(http://postgrest.org/en/stable/admin.html#schema-reloading)
|
||||
to ensure the schema cache is not stale. This sometimes fixes apparent bugs.
|
||||
-->
|
||||
### Environment
|
||||
|
||||
* PostgreSQL version: (if using docker, specify the image)
|
||||
* PostgREST version: (if using docker, specify the image)
|
||||
* Operating system:
|
||||
|
||||
### Description of issue
|
||||
|
||||
(Expected behavior vs actual behavior)
|
||||
|
||||
(Steps to reproduce: Include a minimal SQL definition plus how you make the request to PostgREST and the response body)
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report to help us improve
|
||||
type: Bug
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Before reporting a bug:
|
||||
If your database schema has changed while the PostgREST server is running,
|
||||
send the server a SIGUSR1 signal or restart it (http://postgrest.org/en/stable/admin.html#schema-reloading) to ensure the schema cache is not stale. This sometimes fixes apparent bugs.
|
||||
-->
|
||||
### Environment
|
||||
|
||||
* PostgreSQL version: (if using docker, specify the image)
|
||||
* PostgREST version: (if using docker, specify the image)
|
||||
* Operating system:
|
||||
|
||||
### Description of issue
|
||||
|
||||
Describe the behavior you expected vs the actual behavior. Include:
|
||||
|
||||
- A minimal SQL definition.
|
||||
- How you make the request to PostgREST (curl command preferred).
|
||||
- The PostgREST response.
|
||||
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an enhancement for this project
|
||||
type: Feature
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
A clear and concise description of what the problem is.
|
||||
|
||||
## Solution
|
||||
|
||||
A clear and concise description of what you want to happen.
|
||||
@@ -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"
|
||||
- name: Save artifact to GitHub Actions
|
||||
if: steps.find-task.outputs.task_found
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: ${{ inputs.upload }}
|
||||
path: ${{ steps.download.outputs.artifacts }}
|
||||
|
||||
@@ -19,14 +19,14 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
- uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
if: ${{ startsWith(github.ref, 'refs/heads/') || (inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
key: ${{ runner.os }}-${{ inputs.prefix }}-${{ inputs.suffix }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ inputs.prefix }}-
|
||||
- uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
|
||||
- uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
if: ${{ !startsWith(github.ref, 'refs/heads/') && !(inputs.save-prs && startsWith(github.ref, 'refs/pull/')) }}
|
||||
with:
|
||||
path: ${{ inputs.path }}
|
||||
|
||||
@@ -11,15 +11,16 @@ inputs:
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: nixbuild/nix-quick-install-action@60e9c39264d4714139af3cdf15f691b19eec3530 # v28
|
||||
- uses: nixbuild/nix-quick-install-action@1f095fee853b33114486cfdeae62fa099cda35a9 # v33
|
||||
with:
|
||||
nix_conf: |-
|
||||
always-allow-substitutes = true
|
||||
max-jobs = auto
|
||||
- uses: cachix/cachix-action@ad2ddac53f961de1989924296a1f236fcfbaa4fc # v15
|
||||
- uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
|
||||
with:
|
||||
name: postgrest
|
||||
authToken: ${{ inputs.authToken }}
|
||||
skipPush: ${{ inputs.authToken == '' }}
|
||||
- if: ${{ inputs.tools }}
|
||||
run: nix-env -f default.nix -iA ${{ inputs.tools }}
|
||||
shell: bash
|
||||
|
||||
@@ -8,18 +8,33 @@
|
||||
"/^v[0-9]+/"
|
||||
],
|
||||
"rebaseWhen": "conflicted",
|
||||
"pip_requirements": {
|
||||
"enabled": false
|
||||
},
|
||||
"packageRules": [
|
||||
{
|
||||
"matchBaseBranches": [ "/^v[0-9]+/" ],
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchBaseBranches": [ "/^v[0-9]+/" ],
|
||||
"groupName": "all dependencies"
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["docutils"],
|
||||
"allowedVersions": "<0.21.0"
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"matchPackageNames": ["base", "bytestring", "containers", "directory", "mtl", "parsec", "process", "text"],
|
||||
"groupName": "GHC dependencies"
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["macos"],
|
||||
"allowedVersions": "<13"
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"matchPackageNames": ["hasql", "hasql-dynamic-statements", "hasql-notifications", "hasql-transaction", "hasql-pool"],
|
||||
"groupName": "hasql"
|
||||
},
|
||||
{
|
||||
"matchManagers": ["haskell-cabal"],
|
||||
"matchPackageNames": ["fuzzyset"],
|
||||
"allowedVersions": "<0.3"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
static:
|
||||
name: Nix - Linux static
|
||||
runs-on: ubuntu-22.04
|
||||
name: Nix - Linux x86-64 static
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -42,27 +42,27 @@ jobs:
|
||||
- name: Build static executable
|
||||
run: nix-build -A postgrestStatic
|
||||
- name: Save built executable as artifact
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: postgrest-linux-static-x64
|
||||
name: postgrest-linux-static-x86-64
|
||||
path: result/bin/postgrest
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Build Docker image
|
||||
run: nix-build -A docker.image --out-link postgrest-docker.tar.gz
|
||||
- name: Save built Docker image as artifact
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: postgrest-docker-x64
|
||||
name: postgrest-docker-x86-64
|
||||
path: postgrest-docker.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
|
||||
macos:
|
||||
name: Nix - MacOS
|
||||
runs-on: macos-12
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -87,42 +87,50 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Linux
|
||||
runs-on: ubuntu-22.04
|
||||
- name: Linux aarch64
|
||||
runs-on: ubuntu-24.04-arm
|
||||
cache: |
|
||||
~/.stack/pantry
|
||||
~/.stack/snapshots
|
||||
~/.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
|
||||
runs-on: macos-12
|
||||
- name: MacOS aarch64
|
||||
runs-on: macos-14
|
||||
cache: |
|
||||
~/.stack/pantry
|
||||
~/.stack/snapshots
|
||||
~/.stack/stack.sqlite3
|
||||
artifact: postgrest-macos-x64
|
||||
artifact: postgrest-macos-aarch64
|
||||
deps: brew link --force libpq
|
||||
|
||||
- name: MacOS x86-64
|
||||
runs-on: macos-13
|
||||
cache: |
|
||||
~/.stack/pantry
|
||||
~/.stack/snapshots
|
||||
~/.stack/stack.sqlite3
|
||||
artifact: postgrest-macos-x86-64
|
||||
deps: brew link --force libpq
|
||||
|
||||
- name: Windows
|
||||
runs-on: windows-2022
|
||||
cache: |
|
||||
~\AppData\Roaming\stack\pantry
|
||||
~\AppData\Local\Programs\stack\pantry
|
||||
~\AppData\Roaming\stack\snapshots
|
||||
~\AppData\Local\Programs\stack\snapshots
|
||||
~\AppData\Roaming\stack\stack.sqlite3
|
||||
~\AppData\Local\Programs\stack\stack.sqlite3
|
||||
C:\sr\pantry
|
||||
C:\sr\snapshots
|
||||
C:\sr\stack.sqlite3
|
||||
deps: Add-Content $env:GITHUB_PATH $env:PGBIN
|
||||
artifact: postgrest-windows-x64
|
||||
artifact: postgrest-windows-x86-64
|
||||
|
||||
name: Stack - ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: haskell-actions/setup@33585e1a16afa5875e124b0ebc89dd0c2f872c21 # v2.7.3
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: haskell-actions/setup@7909071ceec0344debcc968c6c7a96a52e8dd0d7 # v2.8.1
|
||||
with:
|
||||
# This must match the version in stack.yaml's resolver
|
||||
ghc-version: 9.6.5
|
||||
ghc-version: 9.6.7
|
||||
enable-stack: true
|
||||
stack-no-global: true
|
||||
stack-setup-ghc: true
|
||||
@@ -147,8 +155,7 @@ jobs:
|
||||
- name: Strip Executable
|
||||
run: strip result/postgrest*
|
||||
- name: Save built executable as artifact
|
||||
if: matrix.artifact
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: ${{ matrix.artifact }}
|
||||
path: |
|
||||
@@ -159,27 +166,27 @@ jobs:
|
||||
|
||||
freebsd:
|
||||
name: Stack - FreeBSD from CirrusCI
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: ./.github/actions/artifact-from-cirrus
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
task: Build FreeBSD (Stack)
|
||||
download: bin
|
||||
upload: postgrest-freebsd-x64
|
||||
upload: postgrest-freebsd-x86-64
|
||||
|
||||
|
||||
cabal:
|
||||
strategy:
|
||||
matrix:
|
||||
ghc: ['9.6.4', '9.8.2']
|
||||
ghc: ['9.6.7', '9.8.4']
|
||||
fail-fast: false
|
||||
name: Cabal - Linux GHC ${{ matrix.ghc }}
|
||||
runs-on: ubuntu-22.04
|
||||
name: Cabal - Linux x86-64 - GHC ${{ matrix.ghc }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: haskell-actions/setup@33585e1a16afa5875e124b0ebc89dd0c2f872c21 # v2.7.3
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: haskell-actions/setup@7909071ceec0344debcc968c6c7a96a52e8dd0d7 # v2.8.1
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
- name: Cache .cabal
|
||||
@@ -188,8 +195,8 @@ jobs:
|
||||
path: |
|
||||
~/.cabal/packages
|
||||
~/.cabal/store
|
||||
prefix: cabal-${{ matrix.ghc }}
|
||||
suffix: ${{ hashFiles('postgrest.cabal', 'cabal.project', 'cabal.project.freeze') }}
|
||||
prefix: cabal-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }}
|
||||
suffix: ${{ hashFiles('postgrest.cabal', 'cabal.project') }}
|
||||
- name: Cache dist-newstyle
|
||||
uses: ./.github/actions/cache-on-main
|
||||
with:
|
||||
|
||||
@@ -18,9 +18,9 @@ concurrency:
|
||||
jobs:
|
||||
lint-style:
|
||||
name: Lint & Style
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
|
||||
@@ -5,21 +5,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- 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:
|
||||
check:
|
||||
name: Check
|
||||
if: startsWith(github.ref, 'refs/heads/')
|
||||
uses: ./.github/workflows/check.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -27,7 +16,6 @@ jobs:
|
||||
|
||||
docs:
|
||||
name: Docs
|
||||
if: startsWith(github.ref, 'refs/heads/')
|
||||
uses: ./.github/workflows/docs.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -35,7 +23,6 @@ jobs:
|
||||
|
||||
test:
|
||||
name: Test
|
||||
if: startsWith(github.ref, 'refs/heads/')
|
||||
uses: ./.github/workflows/test.yaml
|
||||
secrets:
|
||||
CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }}
|
||||
@@ -49,87 +36,21 @@ jobs:
|
||||
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:
|
||||
name: Release / Tag
|
||||
name: Tag
|
||||
concurrency:
|
||||
# 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 }}
|
||||
# TODO: Enable this once https://github.com/orgs/community/discussions/13015 is solved
|
||||
cancel-in-progress: false
|
||||
if: |
|
||||
vars.RELEASE_ENABLED &&
|
||||
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
|
||||
if: vars.RELEASE_ENABLED
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- docs
|
||||
- test
|
||||
- build
|
||||
- arm
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
ssh-key: ${{ secrets.POSTGREST_SSH_KEY }}
|
||||
- name: Tag latest commit
|
||||
@@ -147,216 +68,3 @@ jobs:
|
||||
git tag -f "devel"
|
||||
git push -f origin "devel"
|
||||
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:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -39,9 +39,9 @@ jobs:
|
||||
|
||||
spellcheck:
|
||||
name: Spellcheck
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -51,17 +51,3 @@ jobs:
|
||||
run: postgrest-docs-spellcheck
|
||||
- name: Run dictcheck
|
||||
run: postgrest-docs-dictcheck
|
||||
|
||||
|
||||
linkcheck:
|
||||
name: Linkcheck
|
||||
if: github.base_ref == 'main'
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: docs.linkcheck.bin
|
||||
- run: postgrest-docs-linkcheck
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Linkcheck
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '1 2 * * 3'
|
||||
|
||||
jobs:
|
||||
linkcheck:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: docs.linkcheck.bin
|
||||
- run: postgrest-docs-linkcheck
|
||||
@@ -0,0 +1,205 @@
|
||||
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
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}-macos-x86-64.tar.xz" \
|
||||
-C artifacts/postgrest-macos-x86-64 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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Download x86-64 Docker image
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
name: postgrest-docker-x86-64
|
||||
- name: Download aarch64 binary
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
name: postgrest-ubuntu-aarch64
|
||||
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
- uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
with:
|
||||
username: ${{ vars.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASS }}
|
||||
- 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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: peter-evans/dockerhub-description@432a30c9e07499fd01da9f8a49f0faf9e0ca5b77 # v4.0.2
|
||||
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
|
||||
@@ -32,14 +32,14 @@ concurrency:
|
||||
jobs:
|
||||
coverage:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
# Hack for enabling color output, see:
|
||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
- name: Run coverage (IO tests and Spec tests against PostgreSQL 15)
|
||||
run: postgrest-coverage
|
||||
- name: Upload coverage to codecov
|
||||
uses: codecov/codecov-action@125fc84a9a348dbcf27191600683ec096ec9021c # v4.4.1
|
||||
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
|
||||
with:
|
||||
files: ./coverage/codecov.json
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
@@ -67,16 +67,16 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
pgVersion: ["9_6", 10, 11, 12, 13, 14, 15, 16]
|
||||
pgVersion: [12, 13, 14, 15, 16, 17]
|
||||
name: PG ${{ matrix.pgVersion }}
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
# Hack for enabling color output, see:
|
||||
# https://github.com/actions/runner/issues/241#issuecomment-842566950
|
||||
shell: script -qec "bash --noprofile --norc -eo pipefail {0}"
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
@@ -98,23 +98,26 @@ jobs:
|
||||
|
||||
memory:
|
||||
name: Memory
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: memory.test.bin
|
||||
tools: tests.testMemory.bin
|
||||
- name: Run memory tests
|
||||
run: postgrest-test-memory
|
||||
|
||||
|
||||
loadtest:
|
||||
strategy:
|
||||
matrix:
|
||||
kind: ['mixed', 'jwt']
|
||||
name: Loadtest
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Nix Environment
|
||||
@@ -122,17 +125,37 @@ jobs:
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
tools: loadtest.loadtestAgainst.bin loadtest.report.bin
|
||||
- uses: WyriHaximus/github-action-get-previous-tag@04e8485ecb6487243907e330d522ff60f02283ce # v1.4.0
|
||||
id: get-latest-tag
|
||||
with:
|
||||
prefix: v
|
||||
- name: Run loadtest
|
||||
env:
|
||||
TARGET_BRANCH: ${{ github.base_ref || github.ref_name }}
|
||||
run: |
|
||||
postgrest-loadtest-against main ${{ steps.get-latest-tag.outputs.tag }}
|
||||
postgrest-loadtest-report > loadtest/loadtest.md
|
||||
- name: Upload report
|
||||
uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
|
||||
if [ "$TARGET_BRANCH" = "main" ]; then
|
||||
latest_tag=$(git tag --sort=-creatordate --list "v*" | head -n1)
|
||||
else
|
||||
latest_tag=$(git tag --merged HEAD --sort=-creatordate "v*" | head -n1)
|
||||
fi
|
||||
postgrest-loadtest-against -k ${{ matrix.kind }} "$TARGET_BRANCH" "$latest_tag"
|
||||
postgrest-loadtest-report >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
flake:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runs-on:
|
||||
- macos-13 # x86_64-darwin
|
||||
- macos-14 # aarch64-darwin
|
||||
- ubuntu-24.04 # x86_64-linux
|
||||
- ubuntu-24.04-arm # aarch64-linux
|
||||
name: Flake Check
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
name: loadtest.md
|
||||
path: loadtest/loadtest.md
|
||||
if-no-files-found: error
|
||||
fetch-depth: 0
|
||||
- name: Setup Nix Environment
|
||||
uses: ./.github/actions/setup-nix
|
||||
with:
|
||||
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||
- name: Run flake check
|
||||
run: |
|
||||
nix flake check
|
||||
|
||||
@@ -14,7 +14,7 @@ site
|
||||
.#*
|
||||
*.swp
|
||||
result*
|
||||
dist-newstyle
|
||||
dist-*
|
||||
postgrest.hp
|
||||
postgrest.prof
|
||||
__pycache__
|
||||
@@ -24,3 +24,4 @@ coverage
|
||||
loadtest
|
||||
.history
|
||||
.docs-build
|
||||
gen_targets.http
|
||||
|
||||
@@ -5,6 +5,6 @@ python:
|
||||
install:
|
||||
- requirements: docs/requirements.txt
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: "3.11"
|
||||
|
||||
@@ -13,21 +13,21 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/gnuhost.png">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/code-build.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
@@ -35,11 +35,6 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
<img width="296px" src="static/supabase.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -93,6 +88,11 @@ PostgREST ongoing development is only possible thanks to our Sponsors and Backer
|
||||
<img width="222px" src="static/oblivious.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="222px" src="static/code-build.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -5,6 +5,198 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [13.0.6] - 2025-08-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix logging the Haskell type instead of the listener error message directly by @laurenceisla in #3588
|
||||
- Fix format of `IPv6` address logged at PostgREST startup by @taimoorzaeem in #4291
|
||||
- Fix empty enum in `preferParams` OpenAPI parameter by @laurenceisla in #4292
|
||||
|
||||
## [13.0.5] - 2025-08-24
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix OpenAPI broken docs link by @taimoorzaeem in #4048
|
||||
- Fix OpenAPI specification incorrectly exposing GET methods for volatile functions by @joelonsql in #4174
|
||||
- Fix empty spread embeddings return unexpected SQL error by @taimoorzaeem in #3887
|
||||
- Fix `/metrics` endpoint not responding with `Content-Type` header by @taimoorzaeem in #4271
|
||||
|
||||
## [13.0.4] - 2025-06-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix regression that makes full-text search not work on domain types based on `tsvector` by @laurenceisla in #4135
|
||||
- Fix `jwt-aud` config not failing when set to an invalid URI by @taimoorzaeem in #4132
|
||||
|
||||
## [13.0.3] - 2025-06-16
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `max-affected` preference not failing with RPC when `handling=strict` by @taimoorzaeem in #4100
|
||||
- Fix a property definition's type in OpenAPI not showing the correct base type of a recursive domain by @laurenceisla in #4136
|
||||
|
||||
## [13.0.2] - 2025-06-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix regression that makes `ORDER BY` with nulls-order not work alongside limits by @laurenceisla in #4109
|
||||
|
||||
## [13.0.1] - 2025-06-01
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix jwt error returning HTTP status `400` for invalid role by @taimoorzaeem in #3601
|
||||
- Fix `db-extra-search-path` cannot be set to nothing by @taimoorzaeem in #4074
|
||||
+ It can now be disabled by setting it to empty string.
|
||||
+ Schema Cache load error is now logged including `db-schemas` and `db-extra-search-path` config values.
|
||||
|
||||
## [13.0.0] - 2025-05-08
|
||||
|
||||
### Added
|
||||
|
||||
- #3558, Add the `admin-server-host` config to set the host for the admin server - @develop7
|
||||
- #3607, Log to stderr when the JWT secret is less than 32 characters long - @laurenceisla
|
||||
- #2858, Performance improvements when calling RPCs via GET using indexes in more cases - @wolfgangwalther
|
||||
- #3560, Log resolved host in "Listening on ..." messages - @develop7
|
||||
- #3727, Log maximum pool size - @steve-chavez
|
||||
- #1536, Add string comparison feature for jwt-role-claim-key - @taimoorzaeem
|
||||
- #3747, Allow `not_null` value for the `is` operator - @taimoorzaeem
|
||||
- #2255, Apply `to_tsvector()` explicitly to the full-text search filtered column (excluding `tsvector` types) - @laurenceisla
|
||||
- #1578, Log the main SQL query to stderr at the current `log-level` when `log-query=main-query` - @laurenceisla
|
||||
- #3903, Log connection pool borrows on `log-level=debug` - @taimoorzaeem
|
||||
- #3041, Allow spreading one-to-many and many-to-many embedded resources - @laurenceisla
|
||||
+ The selected columns in the embedded resources are aggregated into arrays
|
||||
+ Aggregates are not supported
|
||||
- #2967, Add `Proxy-Status` header for better error response - @taimoorzaeem
|
||||
- #4016, Add `Content-Length` response header - @laurenceisla
|
||||
|
||||
### Fixed
|
||||
|
||||
- #3693, Prevent spread embedding to allow aggregates when they are disabled - @laurenceisla
|
||||
- #3693, A nested spread embedding now correctly groups by the fields of its top parent relationship - @laurenceisla
|
||||
- #3693, Fix spread embedding errors when using the `count()` aggregate without a field - @laurenceisla
|
||||
+ Fixed `"column reference <col> is ambiguous"` error when selecting `?select=...table(col,count())`
|
||||
+ Fixed `"column <json_aggregate>.<alias> does not exist"` error when selecting `?select=...table(aias:count())`
|
||||
- #3727, Clarify "listening" logs - @steve-chavez
|
||||
- #3795, Clarify `Accept: vnd.pgrst.object` error message - @steve-chavez
|
||||
- #3697, #3602, Handle queries on non-existing table gracefully - @taimoorzaeem
|
||||
- #3600, #3926, Improve JWT errors - @taimoorzaeem
|
||||
- #3013, Fix `order=` with POST, PATCH, PUT and DELETE requests - @taimoorzaeem
|
||||
- #3965, Fix filter on unselected columns in a table-valued function - @taimoorzaeem
|
||||
- #4052, Fix schema cache load duplicate objects with different object type but same oid - @taimoorzaeem
|
||||
|
||||
### Changed
|
||||
|
||||
- #2052, Dropped support for PostgreSQL 9.6 - @wolfgangwalther
|
||||
- #2052, Dropped support for PostgreSQL 10 - @wolfgangwalther
|
||||
- #2052, Dropped support for PostgreSQL 11 - @wolfgangwalther
|
||||
- #3508, PostgREST now fails to start when `server-port` and `admin-server-port` config options are the same - @develop7
|
||||
- #3607, PostgREST now fails to start when the JWT secret is less than 32 characters long - @laurenceisla
|
||||
- #3644, Fail schema cache lookup with invalid `db-schemas` or `db-extra-search-path` config - @wolfgangwalther
|
||||
- Previously, this would silently return 200 - OK on the root endpoint, but don't provide any usable endpoints.
|
||||
- Note: This also applies when deleting the `public` schema - both config options default to that.
|
||||
- #3757, Remove support for `Prefer: params=single-object` - @joelonsql
|
||||
+ This preference was deprecated in favor of Functions with an array of JSON objects
|
||||
- #3013, Drop support for Limited updates/deletes
|
||||
+ The feature was complicated and largely unused.
|
||||
- #3956, Drop `/config` endpoint of admin server - @steve-chavez
|
||||
+ The endpoint was at risk of being left unprotected when exposing it.
|
||||
+ The accompanying `admin-server-config-enabled` config was also dropped.
|
||||
- #3697, #3602, Querying non-existent table now returns `PGRST205` error instead of empty json - @taimoorzaeem
|
||||
- #3600, #3926, Improve JWT errors - @taimoorzaeem
|
||||
+ Return `PGRST301` error when `Bearer` in auth header is sent empty
|
||||
+ Diagnostic error messages instead of exposed internals
|
||||
+ Return new `PGRST303` error when jwt claims decoding fails
|
||||
- #3906, Return `PGRST125` and `PGRST126` errors instead of empty json - @taimoorzaeem
|
||||
|
||||
## [12.2.12] - 2025-05-01
|
||||
|
||||
### Fixed
|
||||
|
||||
- #3956, Fix exposing admin server `/config` by default - @steve-chavez
|
||||
+ The above endpoint is now disabled unless the `admin-server-config-enabled` config is set to `true`
|
||||
|
||||
## [12.2.11] - 2025-04-22
|
||||
|
||||
### Fixed
|
||||
|
||||
- #4030, Fix regression with parameter `charset=utf-8` in mediatype - @taimoorzaeem
|
||||
|
||||
## [12.2.10] - 2025-04-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- #3889, Fix: JWT cache purging on every request decreases performance - @mkleczek
|
||||
|
||||
## [12.2.9] - 2025-04-16
|
||||
|
||||
### Fixed
|
||||
|
||||
- #3498, Fix incorrect parsing of the `for` parameter of the `application/vnd.pgrst.plan` media type - @taimoorzaeem
|
||||
- #4014, Fix JWT cache allows old tokens after the jwt-secret is changed in a config reload - @taimoorzaeem
|
||||
|
||||
## [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
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
This repository follows the same contribution guidelines as the main PostgREST repository contribution guidelines:
|
||||
|
||||
https://github.com/PostgREST/postgrest/blob/main/.github/CONTRIBUTING.md
|
||||
@@ -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:7c06e91f61fa88c08cc74f7e1b7c69ae24910d745357e0dfe1d2c0322aaf20f9 AS postgrest
|
||||
|
||||
RUN apt-get update -y \
|
||||
&& 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/*
|
||||
|
||||
COPY postgrest /usr/bin/postgrest
|
||||
RUN chmod +x /usr/bin/postgrest
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||

|
||||
|
||||
[](https://www.patreon.com/postgrest)
|
||||
[](https://www.paypal.me/postgrest)
|
||||
[](https://gitter.im/begriffs/postgrest)
|
||||
[](http://postgrest.org)
|
||||
[](https://hub.docker.com/r/postgrest/postgrest/)
|
||||
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
||||
@@ -24,21 +22,21 @@ API than you are likely to write from scratch.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/gnuhost.png">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/neon.jpg">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/code-build.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
@@ -46,11 +44,6 @@ API than you are likely to write from scratch.
|
||||
<img width="296px" src="static/supabase.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -145,11 +138,7 @@ and the [API guide](http://postgrest.org/en/stable/api.html).
|
||||
|
||||
## Supporting development
|
||||
|
||||
You can help PostgREST ongoing maintenance and development by:
|
||||
|
||||
- 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
|
||||
You can help PostgREST ongoing maintenance and development by making a regular donation through Patreon https://www.patreon.com/postgrest
|
||||
|
||||
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-07-08T15:50:10Z
|
||||
|
||||
@@ -3,7 +3,15 @@
|
||||
, compiler ? "ghc948"
|
||||
|
||||
, # Commit of the Nixpkgs repository that we want to use.
|
||||
nixpkgsVersion ? import nix/nixpkgs-version.nix
|
||||
# It defaults to reading the inputs from flake.lock, which serves
|
||||
# as a compatibility layer for non-flake builds / default.nix / shell.nix.
|
||||
nixpkgsVersion ? let
|
||||
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||
in
|
||||
{
|
||||
inherit (lock.nodes.nixpkgs.locked) owner repo rev;
|
||||
tarballHash = lock.nodes.nixpkgs.locked.narHash;
|
||||
}
|
||||
|
||||
, # Nix files that describe the Nixpkgs repository. We evaluate the expression
|
||||
# using `import` below.
|
||||
@@ -19,7 +27,7 @@ let
|
||||
"postgrest";
|
||||
|
||||
# PostgREST source files, filtered based on the rules in the .gitignore files
|
||||
# and file extensions. We want to include as litte as possible, as the files
|
||||
# and file extensions. We want to include as little as possible, as the files
|
||||
# added here will increase the space used in the Nix store and trigger the
|
||||
# build of new Nix derivations when changed.
|
||||
src =
|
||||
@@ -36,9 +44,6 @@ let
|
||||
allOverlays.checked-shell-script
|
||||
allOverlays.gitignore
|
||||
allOverlays.postgresql-libpq
|
||||
allOverlays.postgresql-legacy
|
||||
allOverlays.postgresql-future
|
||||
allOverlays.postgis
|
||||
(allOverlays.haskell-packages { inherit compiler; })
|
||||
allOverlays.slocat
|
||||
];
|
||||
@@ -49,34 +54,28 @@ let
|
||||
|
||||
postgresqlVersions =
|
||||
[
|
||||
{ name = "postgresql-17"; postgresql = pkgs.postgresql_17.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-16"; postgresql = pkgs.postgresql_16.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-15"; postgresql = pkgs.postgresql_15.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-14"; postgresql = pkgs.postgresql_14.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-13"; postgresql = pkgs.postgresql_13.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-12"; postgresql = pkgs.postgresql_12.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-11"; postgresql = pkgs.postgresql_11.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-10"; postgresql = pkgs.postgresql_10.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
{ name = "postgresql-9_6"; postgresql = pkgs.postgresql_9_6.withPackages (p: [ p.postgis p.pg_safeupdate ]); }
|
||||
];
|
||||
|
||||
# Dynamic derivation for PostgREST
|
||||
postgrest =
|
||||
pkgs.haskell.packages."${compiler}".callCabal2nix name src { };
|
||||
postgrest = pkgs.lib.pipe (pkgs.haskell.packages."${compiler}".callCabal2nix name src { }) [
|
||||
# To allow ghc-datasize to be used.
|
||||
lib.disableLibraryProfiling
|
||||
# We are never going to use dynamic haskell libraries anyway. "Dynamic" refers to how
|
||||
# non-haskell deps are linked. All haskell dependencies are always statically linked.
|
||||
lib.disableSharedLibraries
|
||||
];
|
||||
|
||||
staticHaskellPackage = import nix/static.nix { inherit compiler name pkgs src; };
|
||||
|
||||
# Options passed to cabal in dev tools and tests
|
||||
devCabalOptions =
|
||||
"-f dev --test-show-detail=direct";
|
||||
|
||||
profiledHaskellPackages =
|
||||
pkgs.haskell.packages."${compiler}".extend (_: super:
|
||||
{
|
||||
mkDerivation =
|
||||
args:
|
||||
super.mkDerivation (args // { enableLibraryProfiling = true; });
|
||||
}
|
||||
);
|
||||
"-f dev --test-show-detail=direct --disable-shared";
|
||||
|
||||
inherit (pkgs.haskell) lib;
|
||||
in
|
||||
@@ -90,12 +89,11 @@ rec {
|
||||
lib.dontCheck postgrest;
|
||||
|
||||
# Profiled dynamic executable.
|
||||
postgrestProfiled =
|
||||
lib.enableExecutableProfiling (
|
||||
lib.dontHaddock (
|
||||
lib.dontCheck (profiledHaskellPackages.callCabal2nix name src { })
|
||||
)
|
||||
);
|
||||
postgrestProfiled = pkgs.lib.pipe postgrestPackage [
|
||||
lib.enableExecutableProfiling
|
||||
lib.enableLibraryProfiling
|
||||
lib.dontHaddock
|
||||
];
|
||||
|
||||
inherit (postgrest) env;
|
||||
|
||||
@@ -125,17 +123,9 @@ rec {
|
||||
loadtest =
|
||||
pkgs.callPackage nix/tools/loadtest.nix { inherit withTools; };
|
||||
|
||||
# Script for running memory tests.
|
||||
memory =
|
||||
pkgs.callPackage nix/tools/memory.nix { inherit postgrestProfiled withTools; };
|
||||
|
||||
# Utility for updating the pinned version of Nixpkgs.
|
||||
nixpkgsTools =
|
||||
pkgs.callPackage nix/tools/nixpkgsTools.nix { };
|
||||
|
||||
# Scripts for publishing new releases.
|
||||
release =
|
||||
pkgs.callPackage nix/tools/release { };
|
||||
pkgs.callPackage nix/tools/release.nix { };
|
||||
|
||||
# Linting and styling tools.
|
||||
style =
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# PostgREST
|
||||
|
||||
[](https://gitter.im/begriffs/postgrest)
|
||||
[](https://www.patreon.com/postgrest)
|
||||
[](https://www.paypal.me/postgrest)
|
||||
[](http://postgrest.org)
|
||||
[](https://github.com/PostgREST/postgrest/actions?query=branch%3Amain)
|
||||
|
||||
@@ -21,21 +19,21 @@ write from scratch.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/gnuhost.png">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/neon.jpg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/neon.jpg">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://code.build/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/code-build.png">
|
||||
<a href="https://www.euronodes.com/postgrest" target="_blank">
|
||||
<img width="296px" src="static/euronodes.svg">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
@@ -43,11 +41,6 @@ write from scratch.
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/supabase.png">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest" target="_blank">
|
||||
<img width="296px" src="https://raw.githubusercontent.com/PostgREST/postgrest/main/static/tembo.png">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -66,7 +59,7 @@ The image is built from scratch using
|
||||
[Nix](https://nixos.org/nixpkgs/manual/#sec-pkgs-dockerTools) instead of a
|
||||
`Dockerfile`, which yields a highly secure and optimized image. This is also why
|
||||
no commands are listed in the image history. See the [PostgREST
|
||||
respository](https://github.com/PostgREST/postgrest/tree/main/nix/tools/docker) for
|
||||
repository](https://github.com/PostgREST/postgrest/tree/main/nix/tools/docker) for
|
||||
details on the build process and how to inspect the image.
|
||||
|
||||
This does not apply to the arm64 variant, which is based on Ubuntu.
|
||||
|
||||
@@ -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:
|
||||
|
||||
```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/
|
||||
|
||||
## 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
|
||||
postgrest-docs-render
|
||||
```
|
||||
|
||||
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
|
||||
plantuml -tsvg uml/arch.uml -o ../../_static
|
||||
plantuml -tsvg -darkmode uml/dark/arch-dark.uml -o ../../../_static
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
skinparam backgroundColor transparent
|
||||
|
||||
package "PostgREST" {
|
||||
() HTTP as HTTPAPI
|
||||
HTTPAPI - [Auth]
|
||||
@@ -14,11 +16,9 @@ package "PostgREST" {
|
||||
[Admin] -r- () HTTPADMIN
|
||||
[Config] -l- () CLI
|
||||
|
||||
[Config] <-l~ Listener
|
||||
[Config] <-r~ Listener
|
||||
|
||||
HTTPADMIN -[hidden]r- CLI
|
||||
Listener -[hidden]r- [Schema Cache]
|
||||
Listener -[hidden]r- [Config]
|
||||
[Schema Cache] -l[hidden]- [Config]
|
||||
[Schema Cache] -l[hidden]- [Admin]
|
||||
[Schema Cache] -l[hidden]- CLI
|
||||
@@ -26,16 +26,16 @@ package "PostgREST" {
|
||||
|
||||
|
||||
database "PostgreSQL" {
|
||||
node "API schema" as API {
|
||||
rectangle "Functions, Views"
|
||||
}
|
||||
node Authorization {
|
||||
rectangle "Roles, GRANT, RLS"
|
||||
}
|
||||
node "API schema" as API {
|
||||
rectangle "Functions, Views"
|
||||
}
|
||||
rectangle "Tables, extensions" as tbs
|
||||
API -d- tbs
|
||||
|
||||
API -[hidden]l- Authorization
|
||||
API -l[hidden]- Authorization
|
||||
}
|
||||
|
||||
:user:
|
||||
@@ -60,6 +60,10 @@ note bottom of ApiRequest
|
||||
Parses the URL syntax
|
||||
end note
|
||||
|
||||
note bottom of Plan
|
||||
Generates internal AST
|
||||
end note
|
||||
|
||||
note bottom of Query
|
||||
Generates the SQL
|
||||
end note
|
||||
@@ -67,4 +71,21 @@ end note
|
||||
note top of Listener
|
||||
LISTEN session
|
||||
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 ApiRequest is [[../explanations/architecture.html#api-request]]
|
||||
url of Plan is [[../explanations/architecture.html#plan]]
|
||||
url of Query is [[../explanations/architecture.html#query]]
|
||||
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 [[../explanations/architecture.html#http]]
|
||||
url of HTTPAPI is [[../explanations/architecture.html#http]]
|
||||
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
|
||||
@@ -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
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -149,3 +149,7 @@ html[data-theme="dark"] .img-translucent img {
|
||||
transition: background-color 0.3s;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.svg-container-md {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 468 B After Width: | Height: | Size: 156 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentStyleType="text/css" height="391px" preserveAspectRatio="none" style="width:328px;height:391px;" version="1.1" viewBox="0 0 328 391" width="328px" zoomAndPan="magnify"><defs/><g><!--cluster PostgreSQL--><g id="cluster_PostgreSQL"><path d="M6,16 C6,6 158.5,6 158.5,6 C158.5,6 311,6 311,16 L311,293.59 C311,303.59 158.5,303.59 158.5,303.59 C158.5,303.59 6,303.59 6,293.59 L6,16 " fill="none" style="stroke:#E7E7E7;stroke-width:1.0;"/><path d="M6,16 C6,26 158.5,26 158.5,26 C158.5,26 311,26 311,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="92.2305" x="112.3848" y="40.9951">PostgreSQL</text></g><!--cluster public--><g id="cluster_public"><polygon fill="none" points="30,74,40,64,153,64,153,146.29,143,156.29,30,156.29,30,74" style="stroke:#E7E7E7;stroke-width:1.0;"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="143" x2="153" y1="74" y2="64"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="30" x2="143" y1="74" y2="74"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="143" x2="143" y1="74" y2="156.29"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="47.9063" x="63.5469" y="89.9951">public</text></g><!--cluster API--><g id="cluster_API"><polygon fill="none" points="70,190.29,80,180.29,246,180.29,246,269.59,236,279.59,70,279.59,70,190.29" style="stroke:#E7E7E7;stroke-width:1.0;"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="236" x2="246" y1="190.29" y2="180.29"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="70" x2="236" y1="190.29" y2="190.29"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="236" x2="236" y1="190.29" y2="279.59"/><text fill="#FFFFFF" font-family="sans-serif" font-size="20" font-weight="bold" lengthAdjust="spacing" textLength="34.668" x="136.666" y="211.8545">api</text></g><!--entity tables_public--><g id="elem_tables_public"><rect fill="#313139" height="36.2969" rx="2.5" ry="2.5" style="stroke:#E7E7E7;stroke-width:0.5;" width="62.752" x="71.62" y="104"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="42.752" x="81.62" y="126.9951">tables</text></g><!--entity extensions--><g id="elem_extensions"><polygon fill="none" points="169.14,109,179.14,99,294.8695,99,294.8695,135.2969,284.8695,145.2969,169.14,145.2969,169.14,109" style="stroke:#E7E7E7;stroke-width:1.0;"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="284.8695" x2="294.8695" y1="109" y2="99"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="169.14" x2="284.8695" y1="109" y2="109"/><line style="stroke:#E7E7E7;stroke-width:1.0;" x1="284.8695" x2="284.8695" y1="109" y2="145.2969"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" font-weight="bold" lengthAdjust="spacing" textLength="85.7295" x="184.14" y="131.9951">extensions</text></g><!--entity vf_api--><g id="elem_vf_api"><rect fill="#313139" height="36.2969" rx="2.5" ry="2.5" style="stroke:#E7E7E7;stroke-width:0.5;" width="144.6465" x="85.68" y="227.29"/><text fill="#FFFFFF" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="124.6465" x="95.68" y="250.2851">views + functions</text></g><!--entity PostgREST--><g id="elem_PostgREST"><ellipse cx="158" cy="352.59" 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="74.6895" x="120.6553" y="382.5851">PostgREST</text></g><!--reverse link tables_public to vf_api--><g id="link_tables_public_vf_api"><path d="M110.03,146.6 C110.03,169.85 110.03,203.55 110.03,226.86 " fill="none" id="tables_public-backto-vf_api" style="stroke:#E7E7E7;stroke-width:1.0;"/><polygon fill="#E7E7E7" points="110.03,140.6,106.03,149.6,110.03,145.6,114.03,149.6,110.03,140.6" style="stroke:#E7E7E7;stroke-width:1.0;"/></g><!--reverse link extensions to vf_api--><g id="link_extensions_vf_api"><path d="M199.73,151.63 C199.73,175.24 199.73,205.18 199.73,226.88 " fill="none" id="extensions-backto-vf_api" style="stroke:#E7E7E7;stroke-width:1.0;"/><polygon fill="#E7E7E7" points="199.73,145.63,195.73,154.63,199.73,150.63,203.73,154.63,199.73,145.63" style="stroke:#E7E7E7;stroke-width:1.0;"/></g><!--link vf_api to PostgREST--><g id="link_vf_api_PostgREST"><path d="M158,269.62 C158,292.9 158,320.34 158,337.81 " fill="none" id="vf_api-PostgREST" style="stroke:#E7E7E7;stroke-width:3.0;"/><polygon fill="#E7E7E7" points="158,263.62,154,272.62,158,268.62,162,272.62,158,263.62" style="stroke:#E7E7E7;stroke-width:3.0;"/><polygon fill="#E7E7E7" points="158,343.81,162,334.81,158,338.81,154,334.81,158,343.81" style="stroke:#E7E7E7;stroke-width:3.0;"/></g><!--SRC=[KypCIyufJKbLqDFJBqxEqCqipjShpSq10000]--></g></svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
@@ -50,7 +50,7 @@ source_suffix = ".rst"
|
||||
master_doc = "index"
|
||||
|
||||
# This is overriden by readthedocs with the version tag anyway
|
||||
version = "devel"
|
||||
version = "13.0"
|
||||
# To avoid repetition in <title> we set this to an empty string.
|
||||
release = ""
|
||||
|
||||
@@ -114,7 +114,7 @@ html_theme = "sphinx_rtd_theme"
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
html_theme_options = {"display_version": False}
|
||||
html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
@@ -292,15 +292,20 @@ def setup(app):
|
||||
app.add_css_file("css/custom.css")
|
||||
|
||||
|
||||
# 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; Ubuntu; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.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 = [
|
||||
r"https://stackoverflow.com/",
|
||||
# 403 only in CI / GitHub Actions
|
||||
r"https://www.patreon.com/postgrest",
|
||||
r"https://blog.frankel.ch/poor-man-api",
|
||||
# Odd SSL error
|
||||
r"https://www.dripdepot.com",
|
||||
# New GitHub UI delays comment load, so anchor fails
|
||||
r"https://github.com/.*#issuecomment",
|
||||
# Random 500 Internal Server Error
|
||||
r"https://jwt.io",
|
||||
]
|
||||
|
||||
# sphinx-tabs configuration
|
||||
|
||||
@@ -8,9 +8,6 @@ Community Tutorials
|
||||
|
||||
* `PostgREST + Auth0: Create REST API in mintutes, and add social login using Auth0 <https://samkhawase.com/blog/postgrest/>`_ - A step-by-step tutorial to show how to dockerize and integrate Auth0 to PostgREST service.
|
||||
|
||||
* `PostgREST + PostGIS API tutorial in 5 minutes <https://gis-ops.com/postgrest-postgis-api-tutorial-geospatial-api-in-5-minutes/>`_ -
|
||||
In this tutorial, GIS • OPS shows how to perform PostGIS calculations through PostgREST :ref:`functions` interface.
|
||||
|
||||
* `"CodeLess" backend using postgres, postgrest and oauth2 authentication with keycloak <https://www.mathieupassenaud.fr/codeless_backend/>`_ -
|
||||
A step-by-step tutorial for using PostgREST with KeyCloak(hosted on a managed service).
|
||||
|
||||
@@ -37,6 +34,7 @@ Templates
|
||||
Example Apps
|
||||
------------
|
||||
|
||||
* `archtika <https://github.com/thiloho/archtika>`_ - self‑hosted CMS
|
||||
* `delibrium-postgrest <https://gitlab.com/delibrium/delibrium-postgrest/>`_ - example school API and front-end in Vue.js
|
||||
* `ETH-transactions-storage <https://github.com/Adamant-im/ETH-transactions-storage>`_ - indexer for Ethereum to get transaction list by ETH address
|
||||
* `general <https://github.com/PierreRochard/general>`_ - example auth back-end
|
||||
@@ -62,6 +60,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.
|
||||
|
||||
* `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
|
||||
* `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
|
||||
@@ -89,7 +88,7 @@ Client-Side Libraries
|
||||
* `postgrest-go <https://github.com/supabase-community/postgrest-go>`_ - Go
|
||||
* `postgrest-js <https://github.com/supabase/postgrest-js>`_ - TypeScript/JavaScript
|
||||
* `postgrest-kt <https://github.com/supabase-community/postgrest-kt>`_ - Kotlin
|
||||
* `postgrest-py <https://github.com/supabase-community/postgrest-py>`_ - Python
|
||||
* `postgrest-py <https://github.com/supabase/postgrest-py>`_ - Python
|
||||
* `postgrest-rs <https://github.com/supabase-community/postgrest-rs>`_ - Rust
|
||||
* `postgrest-swift <https://github.com/supabase-community/postgrest-swift>`_ - Swift
|
||||
* `redux-postgrest <https://github.com/andytango/redux-postgrest>`_ - TypeScript/JS, client integrated with (React) Redux.
|
||||
|
||||
@@ -6,7 +6,22 @@ This page describes the architecture of PostgREST.
|
||||
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
|
||||
========
|
||||
@@ -43,7 +58,7 @@ A request might be rejected at this level if it's invalid. For example when prov
|
||||
Plan
|
||||
----
|
||||
|
||||
Using the Schema Cache, `Plan.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Plan.hs>`_ fills in out-of-band SQL details (like an ``ON CONFLICT (pk)`` clause) required to complete the user request.
|
||||
Using the Schema Cache, `Plan.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Plan.hs>`_ generates an internal AST, filling out-of-band SQL details (like an ``ON CONFLICT (pk)`` clause) required to complete the user request.
|
||||
|
||||
A request might be rejected at this level if it's invalid. For example when doing resource embedding on a nonexistent resource.
|
||||
|
||||
@@ -77,5 +92,4 @@ The HTTP server is provided by `Warp <https://aosabook.org/en/posa/warp.html>`_.
|
||||
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>`_
|
||||
that keeps the :ref:`schema_cache` and the :ref:`in_db_config` up to date.
|
||||
`Listener.hs <https://github.com/PostgREST/postgrest/blob/main/src/PostgREST/Listener.hs>`_ is in charge of the :ref:`listener`.
|
||||
|
||||
@@ -15,7 +15,7 @@ Roles for Each Web User
|
||||
|
||||
PostgREST can accommodate either viewpoint. If you treat a role as a single user then the :ref:`jwt_impersonation` does most of what you need. When an authenticated user makes a request PostgREST will switch into the database role for that user, which in addition to restricting queries, is available to SQL through the :code:`current_user` variable.
|
||||
|
||||
You can use row-level security to flexibly restrict visibility and access for the current user. Here is an `example <https://www.2ndquadrant.com/en/blog/application-users-vs-row-level-security/>`_ from Tomas Vondra, a chat table storing messages sent between users. Users can insert rows into it to send messages to other users, and query it to see messages sent to them by other users.
|
||||
You can use row-level security to flexibly restrict visibility and access for the current user. Here is an `example <https://www.enterprisedb.com:443/blog/application-users-vs-row-level-security>`_ from Tomas Vondra, a chat table storing messages sent between users. Users can insert rows into it to send messages to other users, and query it to see messages sent to them by other users.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.. _external_jwt:
|
||||
.. _external_auth:
|
||||
|
||||
External JWT Generation
|
||||
External Authentication
|
||||
-----------------------
|
||||
|
||||
JWT from Auth0
|
||||
@@ -3,7 +3,7 @@
|
||||
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.
|
||||
|
||||
You can also use your OS package manager.
|
||||
@@ -16,16 +16,11 @@ Supported PostgreSQL versions
|
||||
=============================
|
||||
|
||||
=============== =================================
|
||||
**Supported** PostgreSQL >= 9.6
|
||||
**Supported** PostgreSQL >= 12
|
||||
=============== =================================
|
||||
|
||||
PostgREST works with all PostgreSQL versions starting from 9.6.
|
||||
PostgREST works with all PostgreSQL versions still `officially supported <https://www.postgresql.org/support/versioning/>`_.
|
||||
|
||||
.. note::
|
||||
|
||||
Support for PostgreSQL versions 9.6, 10 and 11 is deprecated. From this on version onwards, PostgREST will only support non-end-of-life PostgreSQL versions.
|
||||
|
||||
See https://www.postgresql.org/support/versioning/.
|
||||
|
||||
Running PostgREST
|
||||
=================
|
||||
@@ -190,7 +185,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.
|
||||
|
||||
* `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
|
||||
|
||||
===================== =======================================
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.. note::
|
||||
|
||||
This page is a work in progress.
|
||||
|
||||
.. _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.
|
||||
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>
|
||||
|
||||
@@ -16,15 +16,9 @@ PostgREST Documentation
|
||||
.. image:: https://img.shields.io/docker/pulls/postgrest/postgrest.svg
|
||||
: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
|
||||
: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.
|
||||
@@ -44,30 +38,30 @@ Sponsors
|
||||
.. image:: ../static/cybertec.svg
|
||||
:target: https://www.cybertec-postgresql.com/en/?utm_source=postgrest.org&utm_medium=referral&utm_campaign=postgrest
|
||||
|
||||
.. image:: ../static/gnuhost.png
|
||||
:target: https://gnuhost.eu/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/neon-dark.jpg
|
||||
:target: https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:target: https://neon.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/neon.jpg
|
||||
:target: https://neon.tech/?utm_source=sponsor&utm_campaign=postgrest
|
||||
:target: https://neon.com/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. image:: ../static/tembo.png
|
||||
:target: https://www.tembo.io/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
|
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
.. image:: ../static/code-build-dark.png
|
||||
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. container:: img-light
|
||||
|
||||
.. image:: ../static/code-build.png
|
||||
:target: https://code.build/?utm_source=sponsor&utm_campaign=postgrest
|
||||
.. image:: ../static/euronodes.svg
|
||||
:target: https://www.euronodes.com/postgrest
|
||||
|
||||
.. container:: img-dark
|
||||
|
||||
@@ -79,13 +73,10 @@ Sponsors
|
||||
.. image:: ../static/supabase.png
|
||||
:target: https://supabase.com/?utm_source=postgrest%20backers&utm_medium=open%20source%20partner&utm_campaign=postgrest%20backers%20github&utm_term=homepage
|
||||
|
||||
.. image:: ../static/tembo.png
|
||||
:target: https://tembo.io/?utm_source=sponsor&utm_campaign=postgrest
|
||||
|
||||
.. The static/empty.png(created with `convert -size 320x95 xc:#fcfcfc empty.png`) is an ugly workaround
|
||||
to create space and center the logos. It's not easy to layout with restructuredText.
|
||||
|
||||
.. .. image:: _static/empty.png
|
||||
.. image:: _static/empty.png
|
||||
:target: #sponsors
|
||||
|
||||
|
|
||||
@@ -113,7 +104,7 @@ PostgREST has a focused scope. It works well with other tools like Nginx. This f
|
||||
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
|
||||
-------------
|
||||
@@ -218,19 +209,12 @@ Here are some companies that use PostgREST in production.
|
||||
* `Datrium <https://www.datrium.com>`_
|
||||
* `Drip Depot <https://www.dripdepot.com>`_
|
||||
* `Image-charts <https://www.image-charts.com>`_
|
||||
* `Moat <https://www.oracle.com/advertising/measurement/>`_
|
||||
* `Netwo <https://www.netwo.io>`_
|
||||
* `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>`_.
|
||||
* `OpenBooking <https://openbooking.ch>`_
|
||||
* `Redsmin <https://www.redsmin.com>`_
|
||||
* `Sompani <https://www.sompani.com>`_
|
||||
* `Supabase <https://supabase.com>`_
|
||||
|
||||
.. Failing links
|
||||
* `eGull <http://www.egull.co>`_
|
||||
* `MotionDynamic - Fast highly dynamic video generation at scale <https://motiondynamic.tech>`_
|
||||
|
||||
Testimonials
|
||||
------------
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
Greenplum
|
||||
#########
|
||||
|
||||
`Greenplum <https://greenplum.org/>`_ has been reported to work by adding ``LOGIN`` to the :ref:`anonymous and user roles <roles>`.
|
||||
|
||||
For more details, see https://github.com/PostgREST/postgrest/issues/2021.
|
||||
@@ -3,6 +3,7 @@ api
|
||||
API's
|
||||
APIs
|
||||
APISIX
|
||||
AST
|
||||
aud
|
||||
Auth
|
||||
auth
|
||||
@@ -12,10 +13,10 @@ booleans
|
||||
BOM
|
||||
Bytea
|
||||
Cardano
|
||||
casted
|
||||
cd
|
||||
centric
|
||||
CLI
|
||||
CMS
|
||||
coercible
|
||||
conf
|
||||
Cloudflare
|
||||
@@ -27,10 +28,12 @@ CSV
|
||||
durations
|
||||
DDL
|
||||
DOM
|
||||
DSL
|
||||
DevOps
|
||||
dockerize
|
||||
enum
|
||||
Enums
|
||||
Entra
|
||||
eq
|
||||
ETH
|
||||
Ethereum
|
||||
@@ -43,7 +46,6 @@ Github
|
||||
Google
|
||||
grantor
|
||||
GraphQL
|
||||
Greenplum
|
||||
gte
|
||||
GUC
|
||||
Haskell
|
||||
@@ -68,9 +70,11 @@ isdistinct
|
||||
JS
|
||||
js
|
||||
JSON
|
||||
JSPath
|
||||
JWK
|
||||
JWT
|
||||
jwt
|
||||
Keycloak
|
||||
Kubernetes
|
||||
localhost
|
||||
login
|
||||
@@ -94,10 +98,11 @@ npm
|
||||
nxl
|
||||
nxr
|
||||
OAuth
|
||||
ORM
|
||||
Observability
|
||||
Okta
|
||||
OpenAPI
|
||||
openapi
|
||||
ORM
|
||||
ov
|
||||
parametrized
|
||||
passphrase
|
||||
@@ -146,8 +151,6 @@ SHA
|
||||
signup
|
||||
SIGUSR
|
||||
sl
|
||||
spreaded
|
||||
Spreaded
|
||||
SQL
|
||||
sql
|
||||
SQLSTATE
|
||||
@@ -171,6 +174,7 @@ unikernel
|
||||
unix
|
||||
updatable
|
||||
unfulfillable
|
||||
unselected
|
||||
Untyped
|
||||
UPSERT
|
||||
Upsert
|
||||
|
||||
@@ -12,7 +12,7 @@ Health Check
|
||||
|
||||
You can enable a health check to verify if PostgREST is available for client requests. Also to check the status of its internal state.
|
||||
|
||||
Two endpoints ``live`` and ``ready`` will then be available.
|
||||
Two endpoints ``live`` and ``ready`` will then be available. Both these endpoints reply with a status code and empty response body.
|
||||
|
||||
.. important::
|
||||
|
||||
@@ -55,23 +55,6 @@ Metrics
|
||||
|
||||
Provides :ref:`metrics`.
|
||||
|
||||
Runtime Configuration
|
||||
=====================
|
||||
|
||||
Provides a ``config`` endpoint that returns the runtime :ref:`configuration`.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3001/config"
|
||||
|
||||
.. code-block::
|
||||
|
||||
db-aggregates-enabled = false
|
||||
db-anon-role = "web_anon"
|
||||
db-channel = "pgrst"
|
||||
db-channel-enabled = false
|
||||
...
|
||||
|
||||
Runtime Schema Cache
|
||||
====================
|
||||
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
Aggregate Functions
|
||||
###################
|
||||
|
||||
Aggregate functions allow you to summarize data by performing calculations across groups of rows. For instance, if you have an ``orders`` table that has an ``amount`` column, you could use an aggregate function to get the sum of the ``amount`` column, either for all rows, or for each group of rows that share specific values, for instance all rows that share the same ``order_date``.
|
||||
PostgREST supports the following aggregate functions: ``avg()``, ``count()``, ``max()``, ``min()``, and ``sum()``.
|
||||
Please refer to the `section on aggregate functions in the PostgreSQL documentation <https://www.postgresql.org/docs/current/functions-aggregate.html>`_ for a detailed explanation of these functions.
|
||||
|
||||
.. note::
|
||||
Aggregate functions are *disabled* by default in PostgREST, as without appropriate safeguards, aggregate functions can create performance problems. See :ref:`db-aggregates-enabled` for further details.
|
||||
Aggregate functions are *disabled* by default in PostgREST, because they can create performance problems without appropriate safeguards.
|
||||
See :ref:`db-aggregates-enabled` for further details.
|
||||
|
||||
PostgREST supports the following aggregate functions: ``avg()``, ``count()``, ``max()``, ``min()``, and ``sum()``. Please refer to the `section on aggregate functions in the PostgreSQL documentation <https://www.postgresql.org/docs/current/functions-aggregate.html>`_ for a detailed explanation of these functions.
|
||||
|
||||
To use an aggregate function, you append the function to a value in the ``select`` parameter, like so:
|
||||
To use an aggregate function, append it to a column in the ``select`` parameter, like so:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=amount.sum()"
|
||||
|
||||
With the above query, PostgREST will return a single row with a single column named ``sum`` that contains the sum of all the values in the ``amount`` column:
|
||||
This will return a ``sum`` of all the values of the ``amount`` column in a single row:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
@@ -26,15 +26,29 @@ With the above query, PostgREST will return a single row with a single column na
|
||||
}
|
||||
]
|
||||
|
||||
You can use multiple aggregate functions by just adding more columns with aggregate functions to the ``select`` parameter.
|
||||
You can ``select`` multiple aggregate functions at the same time (you may need to :ref:`rename them <renaming_columns>` to disambiguate).
|
||||
|
||||
To group by other columns, you simply add those columns to the ``select`` parameter. For instance:
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=total_amount:amount.sum(),avg_amount:amount.avg(),total_quantity:quantity.sum()"
|
||||
|
||||
.. note::
|
||||
Aggregate functions work alongside other PostgREST features, like :ref:`h_filter`, :ref:`json_columns`, and :ref:`ordering`.
|
||||
However they are not compatible with :ref:`domain_reps` for the moment.
|
||||
Additionally, PostgreSQL's ``HAVING`` clause and ordering by aggregated columns are not yet supported.
|
||||
|
||||
Automatic ``GROUP BY``
|
||||
======================
|
||||
|
||||
In SQL, a ``GROUP BY`` clause is required to aggregate the selected columns.
|
||||
However, PostgREST handles grouping automatically if the columns are already present in the ``select`` parameter.
|
||||
For instance:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=amount.sum(),amount.avg(),order_date"
|
||||
|
||||
This will return a row for each unique value in the ``order_date`` column, with the sum and average of the ``amount`` column for all rows that share the same ``order_date``:
|
||||
This will get the sum and average of the amounts grouped by each unique value in the ``order_date`` column:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
@@ -51,67 +65,55 @@ This will return a row for each unique value in the ``order_date`` column, with
|
||||
}
|
||||
]
|
||||
|
||||
.. note::
|
||||
Aggregate functions work alongside other PostgREST features, like :ref:`h_filter`, :ref:`json_columns`, and :ref:`ordering`. Please note at this time aggregate functions are not compatible with :ref:`domain_reps`. Additionally, PostgreSQL's ``HAVING`` clause and ordering by aggregated columns are not yet supported.
|
||||
|
||||
The Case of ``count()``
|
||||
===========================
|
||||
The ``count()`` Aggregate
|
||||
=========================
|
||||
|
||||
.. note::
|
||||
Before the addition of aggregate functions, it was possible to count by adding ``count`` (without parentheses) to the ``select`` parameter. While this is still supported, it may be deprecated in the future, and thus use of this legacy feature is **not recommended.** Please use ``count()`` (with parentheses) instead.
|
||||
Before the addition of aggregate functions, it was possible to count by adding ``count`` (without parentheses) to the ``select`` parameter.
|
||||
While this is still supported, it may be deprecated in the future, and thus use of this legacy feature is **not recommended**.
|
||||
Please use ``count()`` (with parentheses) instead.
|
||||
|
||||
|
||||
``count()`` is treated specially, as it can be used without an associated column. Take for example the following query:
|
||||
``count()`` is a special case because it can be used with or without an aggregated column. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=count(),order_date"
|
||||
|
||||
This would return a row for each unique value in the ``order_date`` column, with the count of all rows that share the same ``order_date``:
|
||||
curl "http://localhost:3000/orders?select=count(),observation_count:observation.count(),order_date"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"count": 4,
|
||||
"observation_count": 2,
|
||||
"order_date": "2023-01-01"
|
||||
},
|
||||
{
|
||||
"count": 2,
|
||||
"observation_count": 1,
|
||||
"order_date": "2023-01-02"
|
||||
}
|
||||
]
|
||||
|
||||
When ``count()`` is used with an associated column, its behavior is slightly different: It will return the count of all values that are not ``NULL``. This is due to how PostgreSQL itself implements the ``count()`` function.
|
||||
|
||||
Renaming and Casting
|
||||
====================
|
||||
|
||||
Renaming Aggregates
|
||||
-------------------
|
||||
|
||||
Just like with other columns, you can rename aggregated columns too. See :ref:`renaming_columns` for details.
|
||||
|
||||
Renaming columns is especially helpful in the context of aggregate functions, as by default a column with an aggregate function applied will take on the name of the applied aggregate function. You may want to provide a more semantically meaningful name or prevent collisions when using multiple aggregate functions of the same type.
|
||||
Note that there is a difference between the result of ``count()`` and ``observation.count()``.
|
||||
The former counts the whole row, while the latter counts the non ``NULL`` values of the ``observation`` column (both grouped by ``order_date``).
|
||||
This is due to how PostgreSQL itself implements the ``count()`` function.
|
||||
|
||||
Casting Aggregates
|
||||
------------------
|
||||
==================
|
||||
|
||||
When applying an aggregate function to a column, you are able to cast both the value of the input to the aggregate function *and* the value of the output from the aggregate function. In both cases, the syntax works as described in :ref:`casting_columns`, with the only difference being the placement of the cast.
|
||||
It is :ref:`possible to cast <casting_columns>` the aggregated column or the aggregate itself, or both at the same time.
|
||||
|
||||
Casting the Value of the Input
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Casting the Aggregated Column
|
||||
-----------------------------
|
||||
|
||||
For instance, imagine that the ``orders`` table has a JSON column, ``order_details``, and this column contains a JSON object that has a key, ``tax_amount``. Let's say you want to get the sum of the tax amount for every order. You can use the ``->`` or ``->>`` operators to extract the value with this key (see :ref:`json_columns`), but these operators will return values of the types JSON and ``text`` respectively, and neither of these types can be used with ``sum()``.
|
||||
|
||||
Therefore, you will need to first cast the input value to a type that is compatible with ``sum()`` (e.g. ``numeric``). Casting the input value is done in exactly the same way as casting any other value:
|
||||
For example, let's say that ``orders`` has an ``order_details`` :ref:`JSON column <json_columns>` with a ``tax_amount`` key.
|
||||
We cannot sum ``tax_amount`` directly because using ``->`` or ``->>`` will return the data in ``json`` or ``text`` format.
|
||||
So we need to cast it to a compatible type (e.g. ``numeric``) right before the aggregate function:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=order_details->tax_amount::numeric.sum()"
|
||||
|
||||
With this, you will receive the sum of the casted ``tax_amount`` value:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
@@ -120,17 +122,15 @@ With this, you will receive the sum of the casted ``tax_amount`` value:
|
||||
}
|
||||
]
|
||||
|
||||
Casting the Value of the Output
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Casting the Aggregate
|
||||
---------------------
|
||||
|
||||
Now let's return to an example involving the ``amount`` column of the ``orders`` table. Imagine that we want to get the rounded average of the ``amount`` column. One way to do this is to use the ``avg()`` aggregate function and then to cast the output value of the function to ``int``. To cast the value of the output of the function, we simply place the cast *after* the aggregate function:
|
||||
For instance, if we wanted to round the average of the ``amount`` column, we could do so by casting ``avg()`` to an ``int``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=amount.avg()::int"
|
||||
|
||||
You will then receive the rounded average as the result:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
@@ -139,27 +139,22 @@ You will then receive the rounded average as the result:
|
||||
}
|
||||
]
|
||||
|
||||
Of course, you can use both input and output casts at the same time, if you so desire.
|
||||
Aggregates and Resource Embedding
|
||||
=================================
|
||||
|
||||
You can group an aggregate function by an :ref:`embedded resource <resource_embedding>` and also use the aggregates inside them.
|
||||
|
||||
Using Aggregate Functions with Resource Embedding
|
||||
=================================================
|
||||
Grouping by an Embedded Resource
|
||||
--------------------------------
|
||||
|
||||
Aggregate functions can be used in conjunction with :ref:`resource_embedding`. You can use embedded resources as grouping columns, use aggregate functions within the context of an embedded resource, or use columns from a spreaded resource as grouping columns or as inputs to aggregate functions.
|
||||
|
||||
Using Embedded Resources as Grouping Columns
|
||||
--------------------------------------------
|
||||
|
||||
Using an embedded resource as a grouping column allows you to use data from an association to group the results of an aggregation.
|
||||
|
||||
For example, imagine that the ``orders`` table from the examples above is related to a ``customers`` table. If you want to get the sum of the ``amount`` column grouped by the ``name`` column from the ``customers`` table, you can include the customer name, using the standard :ref:`resource_embedding` syntax, and perform a sum on the ``amount`` column.
|
||||
Similar to grouping by columns, aggregate functions can also be grouped by embedded resources.
|
||||
For example, let's say that the ``orders`` table is related to a ``customers`` table.
|
||||
To get the sum of the ``amount`` column grouped by the ``name`` column from the ``customers`` table, we would do the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=amount.sum(),customers(name)"
|
||||
|
||||
You will then get the summed amount, along with the embedded customer resource:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
@@ -177,15 +172,16 @@ You will then get the summed amount, along with the embedded customer resource:
|
||||
}
|
||||
]
|
||||
|
||||
.. note::
|
||||
The previous example uses a has-one association to demonstrate this functionality, but you may also use has-many associations as grouping columns, although there are few obvious use cases for this.
|
||||
The previous example uses a "to-one" relationship, but this can be done on "to-many" relationships as well (although there are few obvious use cases).
|
||||
|
||||
Using Aggregate Functions Within the Context of an Embedded Resource
|
||||
--------------------------------------------------------------------
|
||||
This also works in a similar way for :ref:`spread embedded resources <spread_embed>`.
|
||||
For example, ``select=amount.sum(),...customers(name)`` would sum the ``amount`` grouped by the ``name`` column.
|
||||
|
||||
When embedding a resource, you can apply aggregate functions to columns from the associated resource to perform aggregations within the context of an embedded resource.
|
||||
Using Aggregates Inside Embedded Resources
|
||||
------------------------------------------
|
||||
|
||||
Continuing with the example relationship between ``orders`` and ``customers`` from the previous section, imagine that you want to fetch the ``name``, ``city``, and ``state`` for each customer, along with the sum of amount of the customer's orders, grouped by the order date. This can be done in the following way:
|
||||
Using the relationship from the previous example, let's take all the ``customers`` and embed their ``orders``.
|
||||
If we also want to get the total ``amount`` grouped by the ``order_date`` of the ``orders``, we would do the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -226,51 +222,20 @@ Continuing with the example relationship between ``orders`` and ``customers`` fr
|
||||
}
|
||||
]
|
||||
|
||||
In this example, the ``amount`` column is summed and grouped by the ``order_date`` *within* the context of the embedded resource. That is, the ``name``, ``city``, and ``state`` from the ``customers`` table have no bearing on the aggregation performed in the context of the ``orders`` association; instead, each aggregation can be seen as being performed independently on just the orders belonging to a particular customer, using only the data from the embedded resource for both grouping and aggregation.
|
||||
Note that the aggregate is done within the embedded resource ``orders``.
|
||||
It is not affected by any of the columns from the top-level relationship ``customers``.
|
||||
|
||||
Using Columns from a Spreaded Resource
|
||||
--------------------------------------
|
||||
Aggregates in To-One Spreads
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When you :ref:`spread an embedded resource <spread_embed>`, the columns from the spreaded resource are treated as if they were columns of the top-level resource, both when using them as grouping columns and when applying aggregate functions to them.
|
||||
|
||||
Grouping with Columns from a Spreaded Resource
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For instance, assume you want to sum the ``amount`` column from the ``orders`` table, using the ``city`` and ``state`` columns from the ``customers`` table as grouping columns. To achieve this, you may select these two columns from the ``customers`` table and spread them; they will then be used as grouping columns:
|
||||
All the aggregates inside a :ref:`one-to-one or many-to-one spread embedded resource <spread_to_one_embed>` will be hoisted to the top-level relationship.
|
||||
In other words, it will behave as if the aggregate was done in the top-level relationship itself. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=amount.sum(),...customers(city,state)
|
||||
curl "http://localhost:3000/orders?select=order_date,...customers(subscription_date.max(),subscription_date.min())
|
||||
|
||||
The result will be the same as if ``city`` and ``state`` were columns from the ``orders`` table:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"sum": 2000.29,
|
||||
"city": "New York",
|
||||
"state": "NY"
|
||||
},
|
||||
{
|
||||
"sum": 9241.21,
|
||||
"city": "Los Angeles",
|
||||
"state": "CA"
|
||||
}
|
||||
]
|
||||
|
||||
Aggregate Functions with Columns from a Spreaded Resource
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now imagine that the ``customers`` table has a ``joined_date`` column that represents the date that the customer joined. You want to get both the most recent and the oldest ``joined_date`` for customers that placed an order on every distinct order date. This can be expressed as follows:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/orders?select=order_date,...customers(joined_date.max(),joined_date.min())
|
||||
|
||||
As columns from a spreaded resource are treated as if they were columns from the top-level resource, the ``max()`` and ``min()`` are applied *within* the context of the top-level, rather than within the context of the embedded resource, as in the previous section.
|
||||
|
||||
The result will be the same as if the aggregations were applied to columns from the top-level:
|
||||
This will take the ``max`` and ``min`` subscription date of every customer and group it by the ``order_date`` column:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
@@ -286,3 +251,7 @@ The result will be the same as if the aggregations were applied to columns from
|
||||
"min": "2016-02-11"
|
||||
}
|
||||
]
|
||||
|
||||
.. note::
|
||||
|
||||
Aggregates inside to-many spreads are not supported
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
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.
|
||||
|
||||
@@ -71,6 +71,40 @@ The function parameter names match the JSON object keys in the POST case, for th
|
||||
|
||||
.. _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
|
||||
----------------------------------------------
|
||||
|
||||
@@ -97,10 +131,6 @@ For this the ``Content-Type: application/json`` header must be included in the r
|
||||
|
||||
If an overloaded function has a single ``json`` or ``jsonb`` unnamed parameter, PostgREST will call this function as a fallback provided that no other overloaded function is found with the parameters sent in the POST request.
|
||||
|
||||
.. warning::
|
||||
|
||||
Sending the JSON request body as a single argument is also possible with :ref:`Prefer: params=single-object <prefer_params>` but this method is **deprecated**.
|
||||
|
||||
.. _function_single_unnamed:
|
||||
|
||||
Functions with a single unnamed parameter
|
||||
@@ -264,6 +294,23 @@ Let's get its :ref:`explain_plan` when calling it with filters applied:
|
||||
|
||||
Notice there's no "Function Scan" node in the plan, which tells us it has been inlined.
|
||||
|
||||
Horizontal Filtering
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Table-valued functions support horizontal filtering on selected and unselected columns.
|
||||
|
||||
For example, the following RPC with filter on unselected column returns:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/rpc/getallprojects?select=id,client_id&name=like.OSX"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{ "id": 4, "client_id": 2 }
|
||||
]
|
||||
|
||||
.. _scalar_functions:
|
||||
|
||||
Scalar functions
|
||||
|
||||
@@ -19,6 +19,9 @@ For extra customization, the OpenAPI output contains a "description" field for e
|
||||
COMMENT ON TABLE monotremes IS
|
||||
'Freakish mammals lay the best eggs for breakfast';
|
||||
|
||||
COMMENT ON VIEW monotremes_v IS
|
||||
'Only the platypus is publicly visible';
|
||||
|
||||
COMMENT ON COLUMN monotremes.has_venomous_claw IS
|
||||
'Sometimes breakfast is not worth it';
|
||||
|
||||
|
||||
@@ -12,10 +12,9 @@ The following preferences are supported.
|
||||
- ``Prefer: return``. See :ref:`prefer_return`.
|
||||
- ``Prefer: count``. See :ref:`prefer_count`.
|
||||
- ``Prefer: resolution``. See :ref:`prefer_resolution`.
|
||||
- ``Prefer: missing``. See :ref:`bulk_insert_default`.
|
||||
- ``Prefer: missing``. See :ref:`prefer_missing`.
|
||||
- ``Prefer: max-affected``, See :ref:`prefer_max_affected`.
|
||||
- ``Prefer: tx``. See :ref:`prefer_tx`.
|
||||
- ``Prefer: params``. See :ref:`prefer_params`.
|
||||
|
||||
.. _prefer_handling:
|
||||
|
||||
@@ -197,12 +196,53 @@ The ``tx`` preference can be set to specify if the :ref:`transaction <transactio
|
||||
{"id": 35, "name": "Project X"}
|
||||
|
||||
|
||||
.. _prefer_missing:
|
||||
|
||||
Missing
|
||||
=======
|
||||
|
||||
When doing ``POST`` and ``PATCH`` requests, any missing columns in the payload will be inserted as ``null`` value by default. To use the ``DEFAULT`` column value instead, use the ``Prefer: missing=default`` header.
|
||||
|
||||
Having:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table foo (
|
||||
id bigint generated by default as identity primary key
|
||||
, bar text
|
||||
, baz int default 100
|
||||
);
|
||||
|
||||
A request:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/foo?columns=id,bar,baz" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Prefer: missing=default, return=representation" \
|
||||
-d @- << EOF
|
||||
[
|
||||
{ "bar": "val1" },
|
||||
{ "bar": "val2", "baz": 15 }
|
||||
]
|
||||
EOF
|
||||
|
||||
Will result in:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{ "id": 1, "bar": "val1", "baz": 100 },
|
||||
{ "id": 2, "bar": "val2", "baz": 15 }
|
||||
]
|
||||
|
||||
|
||||
.. _prefer_max_affected:
|
||||
|
||||
Max Affected
|
||||
============
|
||||
|
||||
You can set a limit to the amount of resources affected in a request by sending ``max-affected`` preference. This feature works in combination with ``handling=strict`` preference. ``max-affected`` would be ignored with lenient handling. The "affected resources" are the number of rows returned by ``DELETE`` and ``PATCH`` requests. This is also supported through ``RPC`` calls.
|
||||
You can set a limit to the amount of resources affected in a request by sending ``max-affected`` preference. This feature works in combination with ``handling=strict`` preference. ``max-affected`` would be ignored with lenient handling. The "affected resources" are the number of rows returned by ``DELETE`` and ``PATCH`` requests.
|
||||
|
||||
To illustrate the use of this preference, consider the following scenario where the ``items`` table contains 14 rows.
|
||||
|
||||
@@ -225,30 +265,34 @@ To illustrate the use of this preference, consider the following scenario where
|
||||
"hint": null
|
||||
}
|
||||
|
||||
.. _prefer_params:
|
||||
|
||||
Single JSON object as Function Parameter
|
||||
----------------------------------------
|
||||
|
||||
.. warning::
|
||||
|
||||
Using this preference is **deprecated** in favor of :ref:`function_single_json`.
|
||||
|
||||
:code:`Prefer: params=single-object` allows sending the JSON request body as the single argument of a :ref:`function <functions>`.
|
||||
With :ref:`RPC <functions>`, the preference is honored completely on the basis of the number of rows returned in the result set of the function. This can be useful for complex mutation queries using `data-modifying statements <https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-MODIFYING>`_. A simple example:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
CREATE FUNCTION mult_them(param json) RETURNS int AS $$
|
||||
SELECT (param->>'x')::int * (param->>'y')::int
|
||||
CREATE FUNCTION test.delete_items()
|
||||
RETURNS SETOF items AS $$
|
||||
DELETE FROM items WHERE id < 15 RETURNING *;
|
||||
$$ LANGUAGE SQL;
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/rpc/mult_them" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-H "Prefer: params=single-object" \
|
||||
-d '{ "x": 4, "y": 2 }'
|
||||
curl -i "http://localhost:3000/rpc/delete_items" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Prefer: handling=strict, max-affected=10"
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 400 Bad Request
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
8
|
||||
{
|
||||
"code": "PGRST124",
|
||||
"message": "Query result exceeds max-affected preference constraint",
|
||||
"details": "The query affects 14 rows",
|
||||
"hint": null
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
It is important for functions to return ``SETOF`` or ``TABLE`` when called with ``max-affected`` preference. A violation of this would cause a :ref:`PGRST128 <pgrst128>` error.
|
||||
|
||||
@@ -209,18 +209,25 @@ The join table is also detected if the composite key has additional columns.
|
||||
One-to-one relationships
|
||||
------------------------
|
||||
|
||||
One-to-one relationships are detected in two ways.
|
||||
One-to-one relationships are detected in two ways. (We'll use the ``films`` and ``technical_specs`` tables from the :ref:`sample film database <erd_film>` as an example).
|
||||
|
||||
- When the foreign key is a primary key as specified in the :ref:`sample film database <erd_film>`.
|
||||
- When the foreign key has a unique constraint.
|
||||
- When the foreign key is also a primary key.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table technical_specs(
|
||||
film_id int references films(id) unique,
|
||||
runtime time,
|
||||
camera text,
|
||||
sound text
|
||||
film_id int references films(id) primary key
|
||||
-- ...
|
||||
);
|
||||
|
||||
- Or when the foreign key has a unique constraint.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table technical_specs(
|
||||
id int primary key
|
||||
, film_id int references films(id) unique
|
||||
-- ...
|
||||
);
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -927,7 +934,12 @@ Filters can also be applied on nested embedded resources:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/films?select=*,roles(*,actors(*))&roles.actors.order=last_name&roles.actors.first_name=like.*Tom*"
|
||||
# curl "http://localhost:3000/films?select=*,roles(*,actors(*))&roles.actors.order=last_name&roles.actors.first_name=like.*Tom*"
|
||||
|
||||
curl --get "http://localhost:3000/films" \
|
||||
-d "select=*,roles(*,actors(*))" \
|
||||
-d "roles.actors.order=last_name" \
|
||||
-d "roles.actors.first_name=like.*Tom*"
|
||||
|
||||
The result will show the nested actors named Tom and order them by last name. Aliases can also be used instead of the resource names to filter the nested tables.
|
||||
|
||||
@@ -1138,14 +1150,19 @@ For example, to arrange the films in descending order using the director's last
|
||||
Spread embedded resource
|
||||
========================
|
||||
|
||||
On many-to-one and one-to-one relationships, you can "spread" the embedded resource. That is, remove the surrounding JSON object for the embedded resource columns.
|
||||
You can modify the shape of the embedded resources by using the spread syntax (``...``).
|
||||
|
||||
.. _spread_to_one_embed:
|
||||
|
||||
Spread To-One relationships
|
||||
---------------------------
|
||||
|
||||
Spread on resources forming :ref:`one-to-one <one-to-one>` and :ref:`many-to-one <many-to-one>` relationships, will lift the embedded columns to the top object.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# curl "http://localhost:3000/films?select=title,...directors(director_last_name:last_name)&title=like.*Workers*"
|
||||
|
||||
curl --get "http://localhost:3000/films" \
|
||||
-d "select=title,...directors(director_last_name:last_name)" \
|
||||
-d "select=title,...directors(director_first_name:first_name, director_last_name:last_name)" \
|
||||
-d "title=like.*Workers*"
|
||||
|
||||
.. code-block:: json
|
||||
@@ -1153,17 +1170,148 @@ On many-to-one and one-to-one relationships, you can "spread" the embedded resou
|
||||
[
|
||||
{
|
||||
"title": "Workers Leaving The Lumière Factory In Lyon",
|
||||
"director_first_name": "Louis",
|
||||
"director_last_name": "Lumière"
|
||||
}
|
||||
]
|
||||
|
||||
Note that there is no ``"directors"`` object. Also the embed columns can be aliased normally.
|
||||
Note that there is no wrapping ``"directors"`` object, unlike regularly embedding :ref:`many-to-one <many-to-one>` relationships. Also note that embedded columns can be aliased normally.
|
||||
|
||||
You can use this to get the columns of a join table in a many-to-many relationship. For instance, to get films and its actors, but including the ``character`` column from the roles table:
|
||||
.. _spread_to_many_embed:
|
||||
|
||||
Spread To-Many relationships
|
||||
----------------------------
|
||||
|
||||
Spread on resources forming :ref:`one-to-many <one-to-many>` and :ref:`many-to-many <many-to-many>` relationships, will convert the embedded columns into correlated arrays.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
# curl "http://localhost:3000/films?select=title,actors:roles(character,...actors(first_name,last_name))&title=like.*Lighthouse*"
|
||||
curl --get "http://localhost:3000/directors" \
|
||||
-d "select=first_name,...films(film_titles:title,film_years:year)" \
|
||||
-d "first_name=like.Quentin*"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"first_name": "Quentin",
|
||||
"film_titles": [
|
||||
"Pulp Fiction",
|
||||
"Reservoir Dogs"
|
||||
],
|
||||
"film_years": [
|
||||
1994,
|
||||
1992
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Note that ``films`` is no longer an array of objects, unlike regularly embedding :ref:`one-to-many`. The embedded columns become arrays and they're correlated—in the above result, we can say that "Pulp Fiction" premiered in 1994 and "Reservoir Dogs" in 1992.
|
||||
|
||||
Order in spread to-many
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the above example, the order of the values inside the correlated arrays is unspecified, but all the values are guaranteed to be in the same unspecified order.
|
||||
|
||||
You can order the correlated arrays explicitly. For example, to order by the film year:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl --get "http://localhost:3000/directors" \
|
||||
-d "select=first_name,...films(film_titles:title,film_years:year)" \
|
||||
-d "first_name=like.Quentin*" \
|
||||
-d "films.order=year"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"first_name": "Quentin",
|
||||
"film_titles": [
|
||||
"Reservoir Dogs",
|
||||
"Pulp Fiction"
|
||||
],
|
||||
"film_years": [
|
||||
1992,
|
||||
1994
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
.. warning::
|
||||
|
||||
Aliasing spreaded columns is recommended since JSON allows duplicate keys. Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl --get "localhost:3000/projects" \
|
||||
-d "select=id,name,...clients(id,name)"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[{"id":1,"name":"Windows 7","id":1,"name":"Microsoft"},
|
||||
{"id":2,"name":"Windows 10","id":1,"name":"Microsoft"},
|
||||
{"id":3,"name":"IOS","id":2,"name":"Apple"},
|
||||
{"id":4,"name":"OSX","id":2,"name":"Apple"},
|
||||
{"id":5,"name":"Orphan","id":null,"name":null}]
|
||||
|
||||
This can be a problem in Javascript objects, since only the last duplicated key will be considered. To solve it do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl --get "localhost:3000/projects" \
|
||||
-d "select=id,name,...clients(client_id:id,client_name:name)"
|
||||
|
||||
|
||||
Multiple Spreads
|
||||
----------------
|
||||
|
||||
You can use multiple spreads at any level. For example, let's spread ``technical_specs`` and ``roles`` into ``films`` and then spread ``films`` into ``directors``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl --get "http://localhost:3000/directors" \
|
||||
-d "select=first_name,...films(film_titles:title,film_years:year,...technical_specs(film_runtimes:runtime),...roles(film_characters:character))" \
|
||||
-d "first_name=like.Quentin*" \
|
||||
-d "films.order=year" \
|
||||
-d "films.roles.order=character"
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{
|
||||
"first_name": "Quentin",
|
||||
"film_titles": [
|
||||
"Reservoir Dogs",
|
||||
"Pulp Fiction"
|
||||
],
|
||||
"film_years": [
|
||||
1992,
|
||||
1994
|
||||
],
|
||||
"film_runtimes": [
|
||||
"01:39:00",
|
||||
"02:29:00"
|
||||
]
|
||||
"film_characters": [
|
||||
[ "Mr. Pink", "Mr. White" ],
|
||||
[ "Mia Wallace", "Vincent Vega" ]
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Note that:
|
||||
|
||||
- All the ``film_*`` arrays are correlated—"Reservoir Dogs" premiered in 1992, its runtime is 1:39:00 and it has the following characters: ``[ "Mr. Pink", "Mr. White" ]``.
|
||||
- The ``film_*`` arrays are ordered by ``year`` (due to ``films.order=year``).
|
||||
- The bottom level array ``film_characters`` is ordered (due to ``films.roles.order=character``).
|
||||
|
||||
Spread a join table
|
||||
-------------------
|
||||
|
||||
Spread can be used to move the columns of a join table in a :ref:`many-to-many <many-to-many>` to the top object. For instance, to get the ``character`` column of the ``roles`` join table into ``actors``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl --get "http://localhost:3000/films" \
|
||||
-d "select=title,actors:roles(character,...actors(first_name,last_name))" \
|
||||
@@ -1184,6 +1332,4 @@ You can use this to get the columns of a join table in a many-to-many relationsh
|
||||
}
|
||||
]
|
||||
|
||||
.. note::
|
||||
|
||||
The spread operator ``...`` is borrowed from the Javascript `spread syntax <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax>`_.
|
||||
|
||||
@@ -16,6 +16,32 @@ Use the Accept request header to specify the acceptable format (or formats) for
|
||||
curl "http://localhost:3000/people" \
|
||||
-H "Accept: application/json"
|
||||
|
||||
.. note::
|
||||
|
||||
The ordering of columns in the response isn't guaranteed to align with the order specified in the ``select`` clause. For example, with resource embedding:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
http://localhost:3000/films?select=directors(last_name,id),title
|
||||
|
||||
We may get:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
[
|
||||
{
|
||||
"title": "title",
|
||||
"directors": {
|
||||
"id": 5,
|
||||
"last_name": "name"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
This is in line with the `JSON schema spec <https://json-schema.org/draft/2020-12/json-schema-core#name-instance-data-model>`_:
|
||||
|
||||
*"object: An unordered set of properties mapping a string to an instance"*
|
||||
|
||||
.. _builtin_media:
|
||||
|
||||
Builtin Media Type Handlers
|
||||
@@ -74,17 +100,15 @@ This returns
|
||||
|
||||
{ "id": 1 }
|
||||
|
||||
with a :code:`Content-Type: application/vnd.pgrst.object+json`.
|
||||
|
||||
When a singular response is requested but no entries are found, the server responds with an error message and 406 Not Acceptable status code rather than the usual empty array and 200 status:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"message": "JSON object requested, multiple (or no) rows returned",
|
||||
"details": "Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row",
|
||||
"hint": null,
|
||||
"code": "PGRST505"
|
||||
"code": "PGRST116",
|
||||
"message": "Cannot coerce the result to a single JSON object",
|
||||
"details": "The result contains 0 rows",
|
||||
"hint": null
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -72,7 +72,7 @@ imatch :code:`~*` ~* operator, see :ref:`pattern_matching`
|
||||
in :code:`IN` one of a list of values, e.g. :code:`?a=in.(1,2,3)`
|
||||
– also supports commas in quoted strings like
|
||||
:code:`?a=in.("hi,there","yes,you")`
|
||||
is :code:`IS` checking for exact equality (null,true,false,unknown)
|
||||
is :code:`IS` checking for exact equality (null,not_null,true,false,unknown)
|
||||
isdistinct :code:`IS DISTINCT FROM` not equal, treating :code:`NULL` as a comparable value
|
||||
fts :code:`@@` :ref:`fts` using to_tsquery
|
||||
plfts :code:`@@` :ref:`fts` using plainto_tsquery
|
||||
@@ -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}`
|
||||
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.
|
||||
:code: `?arr=ov.{1,3}`
|
||||
:code:`?arr=ov.{1,3}`
|
||||
sl :code:`<<` strictly left of, e.g. :code:`?range=sl.(1,10)`
|
||||
sr :code:`>>` strictly right of
|
||||
nxr :code:`&<` does not extend to the right of, e.g. :code:`?range=nxr.(1,10)`
|
||||
@@ -175,25 +175,47 @@ To ensure best performance on larger data sets, an `appropriate index <https://w
|
||||
Full-Text Search
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The :code:`fts` filter mentioned above has a number of options to support flexible textual queries, namely the choice of plain vs phrase search and the language used for stemming. Suppose that :code:`tsearch` is a table with column :code:`my_tsv`, of type `tsvector <https://www.postgresql.org/docs/current/datatype-textsearch.html>`_. The following examples illustrate the possibilities.
|
||||
The :code:`fts` operator has a number of options to support flexible textual queries, namely the choice of plain vs phrase search and the language used for stemming.
|
||||
|
||||
The following examples illustrate the possibilities, assuming column :code:`my_tsv` is of type `tsvector <https://www.postgresql.org/docs/current/datatype-textsearch.html>`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/tsearch?my_tsv=fts(french).amusant"
|
||||
curl --get "http://localhost:3000/people" \
|
||||
-d "my_tsv=fts(french).amusant"
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/tsearch?my_tsv=plfts.The%20Fat%20Cats"
|
||||
curl --get "http://localhost:3000/people" \
|
||||
-d "my_tsv=plfts.The%20Fat%20Cats"
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/tsearch?my_tsv=not.phfts(english).The%20Fat%20Cats"
|
||||
curl --get "http://localhost:3000/people" \
|
||||
-d "my_tsv=not.phfts(english).The%20Fat%20Cats"
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/tsearch?my_tsv=not.wfts(french).amusant"
|
||||
curl --get "http://localhost:3000/people" \
|
||||
-d "my_tsv=not.wfts(french).amusant"
|
||||
|
||||
Using `websearch_to_tsquery` requires PostgreSQL of version at least 11.0 and will raise an error in earlier versions of the database.
|
||||
.. _fts_to_tsvector:
|
||||
|
||||
Automatic ``tsvector`` conversion
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If the filtered column is not of type ``tsvector``, then it will be automatically converted using `to_tsvector() <https://www.postgresql.org/docs/current/functions-textsearch.html#TEXTSEARCH-FUNCTIONS-TABLE>`_.
|
||||
This allows using the ``fts`` operator on ``text`` and ``json`` types out of the box.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl --get "http://localhost:3000/people" \
|
||||
-d "my_text_column=fts(french).amusant"
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl --get "http://localhost:3000/people" \
|
||||
-d "my_json_column=not.phfts(english).The%20Fat%20Cats"
|
||||
|
||||
.. _v_filter:
|
||||
|
||||
@@ -525,45 +547,6 @@ To bulk insert JSON post an array of objects having all-matching keys
|
||||
]
|
||||
EOF
|
||||
|
||||
.. _bulk_insert_default:
|
||||
|
||||
Bulk Insert with Default Values
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Any missing columns in the payload will be inserted as ``null`` values. To use the ``DEFAULT`` column value instead, use the ``Prefer: missing=default`` header.
|
||||
|
||||
Having:
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
create table foo (
|
||||
id bigint generated by default as identity primary key
|
||||
, bar text
|
||||
, baz int default 100
|
||||
);
|
||||
|
||||
A request:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/foo?columns=id,bar,baz" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Prefer: missing=default, return=representation" \
|
||||
-d @- << EOF
|
||||
[
|
||||
{ "bar": "val1" },
|
||||
{ "bar": "val2", "baz": 15 }
|
||||
]
|
||||
EOF
|
||||
|
||||
Will result in:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
[
|
||||
{ "id": 1, "bar": "val1", "baz": 100 },
|
||||
{ "id": 2, "bar": "val2", "baz": 15 }
|
||||
]
|
||||
|
||||
.. _specify_columns:
|
||||
|
||||
@@ -606,7 +589,13 @@ To update a row or rows in a table, use the PATCH verb. Use :ref:`h_filter` to s
|
||||
-X PATCH -H "Content-Type: application/json" \
|
||||
-d '{ "category": "child" }'
|
||||
|
||||
Updates also support :ref:`prefer_return`, :ref:`resource_embedding` and :ref:`v_filter`.
|
||||
Updates also support:
|
||||
|
||||
- :ref:`prefer_return`
|
||||
- :ref:`resource_embedding`
|
||||
- :ref:`v_filter`
|
||||
- :ref:`Missing Preference <prefer_missing>`
|
||||
- :ref:`specify_columns`
|
||||
|
||||
.. warning::
|
||||
|
||||
@@ -623,18 +612,34 @@ You can make an upsert with :code:`POST` and the :code:`Prefer: resolution=merge
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "http://localhost:3000/employees" \
|
||||
curl "http://localhost:3000/products" \
|
||||
-X POST -H "Content-Type: application/json" \
|
||||
-H "Prefer: resolution=merge-duplicates" \
|
||||
-d @- << EOF
|
||||
[
|
||||
{ "id": 1, "name": "Old employee 1", "salary": 30000 },
|
||||
{ "id": 2, "name": "Old employee 2", "salary": 42000 },
|
||||
{ "id": 3, "name": "New employee 3", "salary": 50000 }
|
||||
{ "sku": "CL2031", "name": "Existing T-shirt", "price": 35 },
|
||||
{ "sku": "CL2040", "name": "Existing Hoodie", "price": 60 },
|
||||
{ "sku": "AC1022", "name": "New Cap", "price": 30 }
|
||||
]
|
||||
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` with :ref:`prefer_missing`:
|
||||
|
||||
.. 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::
|
||||
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`.
|
||||
@@ -700,31 +705,6 @@ Deletions also support :ref:`prefer_return`, :ref:`resource_embedding` and :ref:
|
||||
|
||||
Beware of accidentally deleting all rows in a table. To learn to prevent that see :ref:`block_fulltable`.
|
||||
|
||||
.. _limited_update_delete:
|
||||
|
||||
Limited Update/Delete
|
||||
=====================
|
||||
|
||||
You can limit the amount of affected rows by :ref:`update` or :ref:`delete` with the ``limit`` query parameter. For this, you must add an explicit ``order`` on a unique column(s).
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -X PATCH "/users?limit=10&order=id&last_login=lt.2020-01-01" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{ "status": "inactive" }'
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -X DELETE "http://localhost:3000/users?limit=10&order=id&status=eq.inactive"
|
||||
|
||||
If your table has no unique columns, you can use the `ctid <https://www.postgresql.org/docs/current/ddl-system-columns.html>`_ system column.
|
||||
|
||||
Using ``offset`` to target a different subset of rows is also possible.
|
||||
|
||||
.. note::
|
||||
|
||||
There is no native ``UPDATE...LIMIT`` or ``DELETE...LIMIT`` support in PostgreSQL; the generated query simulates that behavior and is based on `this Crunchy Data blog post <https://www.crunchydata.com/blog/simulating-update-or-delete-with-limit-in-postgres-ctes-to-the-rescue>`_.
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -739,6 +719,7 @@ Using ``offset`` to target a different subset of rows is also possible.
|
||||
'#estimated-count': 'pagination_count.html#estimated-count',
|
||||
'#prefer-return-headers-only': 'preferences.html#headers-only',
|
||||
'#prefer-return-representation': 'preferences.html#full',
|
||||
'#bulk-insert-default': 'preferences.html#prefer-missing',
|
||||
};
|
||||
|
||||
let willRedirectTo = redirects[hash];
|
||||
|
||||
@@ -77,7 +77,7 @@ If the client included no JWT (or one without a role claim) then PostgREST switc
|
||||
JWT Generation
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
You can create a valid JWT either from inside your database (see :ref:`sql_user_management`) or via an external service (see :ref:`external_jwt`).
|
||||
You can create a valid JWT either from inside your database (see :ref:`sql_user_management`) or via an external service (see :ref:`external_auth`).
|
||||
|
||||
.. _client_auth:
|
||||
|
||||
@@ -156,10 +156,90 @@ You can specify the literal value as we saw earlier, or reference a filename to
|
||||
|
||||
jwt-secret = "@rsa.jwk.pub"
|
||||
|
||||
JWK ``kid`` validation
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
PostgREST has built-in validation of the `key ID parameter <https://www.rfc-editor.org/rfc/rfc7517#section-4.5>`_, useful when working with a JWK Set.
|
||||
It goes as follows:
|
||||
|
||||
- If the JWT contains a ``kid`` parameter, then PostgREST will look for the JWK in the :ref:`jwt-secret`.
|
||||
|
||||
+ If no JWK matches the same ``kid`` value (or if they do not have a ``kid``), then the token will be rejected with a :ref:`401 Unauthorized <pgrst301>` error.
|
||||
+ If a JWK matches the ``kid`` value then it will validate the token against that JWK accordingly.
|
||||
|
||||
- If the JWT does not have a ``kid`` parameter, then PostgREST will validate the token against each JWK in the :ref:`jwt-secret`.
|
||||
|
||||
.. _jwt_claims_validation:
|
||||
|
||||
JWT Claims Validation
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
PostgREST honors the :code:`exp` claim for token expiration, rejecting expired tokens.
|
||||
PostgREST honors the following `JWT claims <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4>`_:
|
||||
|
||||
- ``exp`` Expiration Time
|
||||
- ``iat`` Issued At
|
||||
- ``nbf`` Not Before
|
||||
- ``aud`` :ref:`Audience <jwt_aud_validation>`
|
||||
|
||||
.. note::
|
||||
PostgREST allows for a 30-second clock skew when validating the ``exp``, ``iat`` and ``nbf`` claims.
|
||||
In other words, it gives an extra 30 seconds before the token is rejected if there is a slight discrepancy in the timestamps.
|
||||
|
||||
.. _jwt_aud_validation:
|
||||
|
||||
JWT ``aud`` Claim Validation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
PostgREST has built-in validation of the `JWT audience claim <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_.
|
||||
It works this way:
|
||||
|
||||
- If :ref:`jwt-aud` is not set (the default), PostgREST identifies with all audiences and allows the JWT for any ``aud`` claim.
|
||||
- If :ref:`jwt-aud` is set to a specific audience, PostgREST will check if this audience is present in the ``aud`` claim:
|
||||
|
||||
+ If the ``aud`` value is a JSON string, it will match it to the :ref:`jwt-aud`.
|
||||
+ If the ``aud`` value is a JSON array of strings, it will search every element for a match.
|
||||
+ If the match fails or if the ``aud`` value is not a string or array of strings, then the token will be rejected with a :ref:`401 Unauthorized <pgrst303>` error.
|
||||
+ If the ``aud`` key **is not present** or if its value is ``null`` or ``[]``, PostgREST will interpret this token as allowed for all audiences and will complete the request.
|
||||
|
||||
.. _jwt_role_extract:
|
||||
|
||||
JWT Role Extraction
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
A JSPath DSL that specifies the location of the :code:`role` key in the JWT claims. It's configured by :ref:`jwt-role-claim-key`. This can be used to consume a JWT provided by a third party service like Auth0, Okta, Microsoft Entra or Keycloak.
|
||||
|
||||
The DSL follows the `JSONPath <https://goessner.net/articles/JsonPath/>`_ expression grammar with extended string comparison operators. Supported operators are:
|
||||
|
||||
- ``==`` selects the first array element that exactly matches the right operand
|
||||
- ``!=`` selects the first array element that does not match the right operand
|
||||
- ``^==`` selects the first array element that starts with the right operand
|
||||
- ``==^`` selects the first array element that ends with the right operand
|
||||
- ``*==`` selects the first array element that contains the right operand
|
||||
|
||||
Usage examples:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# {"postgrest":{"roles": ["other", "author"]}}
|
||||
# the DSL accepts characters that are alphanumerical or one of "_$@" as keys
|
||||
jwt-role-claim-key = ".postgrest.roles[1]"
|
||||
|
||||
# {"https://www.example.com/role": { "key": "author" }}
|
||||
# non-alphanumerical characters can go inside quotes(escaped in the config value)
|
||||
jwt-role-claim-key = ".\"https://www.example.com/role\".key"
|
||||
|
||||
# {"postgrest":{"roles": ["other", "author"]}}
|
||||
# `@` represents the current element in the array
|
||||
# all the these match the string "author"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ == \"author\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ != \"other\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ ^== \"aut\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ ==^ \"hor\")]"
|
||||
jwt-role-claim-key = ".postgrest.roles[?(@ *== \"utho\")]"
|
||||
|
||||
.. note::
|
||||
|
||||
The string comparison operators are implemented as a custom extension to the JSPath and does not strictly follow the `RFC 9535 <https://www.rfc-editor.org/rfc/rfc9535.html>`_.
|
||||
|
||||
JWT Security
|
||||
~~~~~~~~~~~~
|
||||
|
||||
@@ -135,19 +135,32 @@ To reload the configuration via signal, send a SIGUSR2 signal to the server proc
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
List of parameters
|
||||
==================
|
||||
|
||||
.. _admin-server-host:
|
||||
|
||||
admin-server-host
|
||||
-----------------
|
||||
|
||||
=============== =======================
|
||||
**Type** String
|
||||
**Default** `server-host` value
|
||||
**Reloadable** N
|
||||
**Environment** PGRST_ADMIN_SERVER_HOST
|
||||
**In-Database** `n/a`
|
||||
=============== =======================
|
||||
|
||||
Specifies the host for the :ref:`admin_server`. Defaults to :ref:`server-host` value.
|
||||
|
||||
.. _admin-server-port:
|
||||
|
||||
admin-server-port
|
||||
@@ -161,7 +174,7 @@ admin-server-port
|
||||
**In-Database** `n/a`
|
||||
=============== =======================
|
||||
|
||||
Specifies the port for the :ref:`admin_server`.
|
||||
Specifies the port for the :ref:`admin_server`. Cannot be equal to :ref:`server-port`.
|
||||
|
||||
.. _app.settings.*:
|
||||
|
||||
@@ -178,6 +191,10 @@ app.settings.*
|
||||
|
||||
Arbitrary settings that can be used to pass in secret keys directly as strings, or via OS environment variables. For instance: :code:`app.settings.jwt_secret = "$(MYAPP_JWT_SECRET)"` will take :code:`MYAPP_JWT_SECRET` from the environment and make it available to PostgreSQL functions as :code:`current_setting('app.settings.jwt_secret')`.
|
||||
|
||||
When using the environment variable `PGRST_APP_SETTINGS_*` form, the remainder of the variable is used as the new name. Case is not important : :code:`PGRST_APP_SETTINGS_MY_ENV_VARIABLE=some_value` can be accessed in postgres as :code:`current_setting('app.settings.my_env_variable')`.
|
||||
|
||||
The :code:`current_setting` function has `an optional boolean second <https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-SET>`_ argument to avoid it from raising an error if the value was not defined. Default values to :code:`app.settings` can then be given by combining this argument with :code:`coalesce` and :code:`nullif` : :code:`coalesce(nullif(current_setting('app.settings.my_custom_variable', true), ''), 'default value')`. The use of :code:`nullif` is necessary because if set in a transaction, the setting is sometimes not "rolled back" to :code:`null`. See also :ref:`this section <guc_req_headers_cookies_claims>` for more information on this behaviour.
|
||||
|
||||
.. _db-aggregates-enabled:
|
||||
|
||||
db-aggregates-enabled
|
||||
@@ -298,6 +315,10 @@ db-extra-search-path
|
||||
|
||||
Multiple schemas can be added in a comma-separated string, e.g. ``public, extensions``.
|
||||
|
||||
.. important::
|
||||
|
||||
We default this config to ``public`` because it is the most common schema used to install PostgreSQL extensions such as :ref:`PostGIS <ww_postgis>`. You can disable this by setting this config to ``""``.
|
||||
|
||||
.. _db-hoisted-tx-settings:
|
||||
|
||||
db-hoisted-tx-settings
|
||||
@@ -582,11 +603,7 @@ jwt-aud
|
||||
**In-Database** pgrst.jwt_aud
|
||||
=============== =================================
|
||||
|
||||
Specifies the `JWT audience claim <https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3>`_. If this claim is present in the client provided JWT then you must set this to the same value as in the JWT, otherwise verifying the JWT will fail.
|
||||
|
||||
.. warning::
|
||||
|
||||
Using this setting will only reject tokens with a different audience claim. Tokens **without** audience claim will still be accepted.
|
||||
Specifies an audience for the JWT ``aud`` claim. See :ref:`jwt_aud_validation`.
|
||||
|
||||
.. _jwt-role-claim-key:
|
||||
|
||||
@@ -603,17 +620,7 @@ jwt-role-claim-key
|
||||
|
||||
*For backwards compatibility, this config parameter is also available without prefix as "role-claim-key".*
|
||||
|
||||
A JSPath DSL that specifies the location of the :code:`role` key in the JWT claims. This can be used to consume a JWT provided by a third party service like Auth0, Okta or Keycloak. Usage examples:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# {"postgrest":{"roles": ["other", "author"]}}
|
||||
# the DSL accepts characters that are alphanumerical or one of "_$@" as keys
|
||||
jwt-role-claim-key = ".postgrest.roles[1]"
|
||||
|
||||
# {"https://www.example.com/role": { "key": "author }}
|
||||
# non-alphanumerical characters can go inside quotes(escaped in the config value)
|
||||
jwt-role-claim-key = ".\"https://www.example.com/role\".key"
|
||||
See :ref:`jwt_role_extract` on how to specify key paths and usage examples.
|
||||
|
||||
.. _jwt-secret:
|
||||
|
||||
@@ -696,10 +703,35 @@ log-level
|
||||
log-level = "info"
|
||||
|
||||
# All the above plus events for development purposes are logged
|
||||
# Logs connection pool events and the schema cache parsing time
|
||||
log-level = "debug"
|
||||
|
||||
Because currently there's no buffering for logging, the levels with minimal logging(``crit/error``) will increase throughput.
|
||||
|
||||
.. _log-query:
|
||||
|
||||
log-query
|
||||
---------
|
||||
|
||||
=============== =================================
|
||||
**Type** String
|
||||
**Default** "disabled"
|
||||
**Reloadable** Y
|
||||
**Environment** PGRST_LOG_QUERY
|
||||
**In-Database** `n/a`
|
||||
=============== =================================
|
||||
|
||||
Logs the SQL query for the corresponding request at the current :ref:`log-level`.
|
||||
See :ref:`sql_query_logs`.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
# Logs the main SQL query
|
||||
log-query = "main-query"
|
||||
|
||||
# Disables logging the SQL query
|
||||
log-query = "disabled"
|
||||
|
||||
.. _openapi-mode:
|
||||
|
||||
openapi-mode
|
||||
@@ -813,6 +845,12 @@ server-host
|
||||
* :code:`*6` - any IPv4 or IPv6 hostname, IPv6 preferred
|
||||
* :code:`!6` - any IPv6 hostname
|
||||
|
||||
Examples:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
server-host = "127.0.0.1"
|
||||
|
||||
.. _server-port:
|
||||
|
||||
server-port
|
||||
@@ -856,7 +894,7 @@ server-timing-enabled
|
||||
**In-Database** pgrst.server_timing_enabled
|
||||
=============== =================================
|
||||
|
||||
Enables the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing>`_ header.
|
||||
Enables the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing>`_ header.
|
||||
See :ref:`server-timing_header`.
|
||||
|
||||
.. _server-unix-socket:
|
||||
|
||||
@@ -5,6 +5,8 @@ Errors
|
||||
|
||||
PostgREST error messages follow the PostgreSQL error structure. It includes ``MESSAGE``, ``DETAIL``, ``HINT``, ``ERRCODE`` and will add an HTTP status code to the response.
|
||||
|
||||
.. _postgresql_errors:
|
||||
|
||||
Errors from PostgreSQL
|
||||
======================
|
||||
|
||||
@@ -205,14 +207,6 @@ Related to the HTTP request elements.
|
||||
| | | specified in the ``select`` part of the query string. |
|
||||
| PGRST108 | | See :ref:`embed_filters`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst109: | 400 | Restricting a Deletion or an Update using limits must |
|
||||
| | | include the ordering of a unique column. |
|
||||
| PGRST109 | | See :ref:`limited_update_delete`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst110: | 400 | When restricting a Deletion or an Update using limits |
|
||||
| | | modifies more rows than the maximum specified in the limit. |
|
||||
| PGRST110 | | See :ref:`limited_update_delete`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst111: | 500 | An invalid ``response.headers`` was set. |
|
||||
| | | See :ref:`guc_resp_hdrs`. |
|
||||
| PGRST111 | | |
|
||||
@@ -241,10 +235,6 @@ Related to the HTTP request elements.
|
||||
| | | there is no many-to-one or one-to-one relationship between |
|
||||
| PGRST118 | | them. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst119: | 400 | Could not use the spread operator on the related table |
|
||||
| | | because there is no many-to-one or one-to-one relationship |
|
||||
| PGRST119 | | between them. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst120: | 400 | An embedded resource can only be filtered using the |
|
||||
| | | ``is.null`` or ``not.is.null`` :ref:`operators <operators>`.|
|
||||
| PGRST120 | | |
|
||||
@@ -257,6 +247,31 @@ Related to the HTTP request elements.
|
||||
| | | ``Prefer: handling=strict``. See :ref:`prefer_handling`. |
|
||||
| PGRST122 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst123: | 400 | Aggregate functions are disabled. |
|
||||
| | | See :ref:`db-aggregates-enabled`. |
|
||||
| PGRST123 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst124: | 400 | ``max-affected`` preference is violated. |
|
||||
| | | See :ref:`prefer_max_affected`. |
|
||||
| PGRST124 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst125: | 404 | Invalid path is specified in request URL. |
|
||||
| | | |
|
||||
| PGRST125 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst126: | 404 | Open API config is disabled but API root path is |
|
||||
| | | accessed. See :ref:`openapi-mode`. |
|
||||
| PGRST126 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst127: | 400 | The feature specified in the ``details`` field is not |
|
||||
| | | implemented. |
|
||||
| PGRST127 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst128: | 400 | ``max-affected`` preference is violated with ``RPC`` call. |
|
||||
| | | See :ref:`prefer_max_affected`. |
|
||||
| PGRST128 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
|
||||
.. _pgrst2**:
|
||||
|
||||
@@ -290,6 +305,10 @@ Related to a :ref:`schema_cache`. Most of the time, these errors are solved by :
|
||||
| | | in the ``columns`` query parameter is not found. |
|
||||
| PGRST204 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst205: | 404 | Caused when the :ref:`table specified <tables_views>` in |
|
||||
| | | the URI is not found. |
|
||||
| PGRST205 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
.. _pgrst3**:
|
||||
|
||||
@@ -305,14 +324,18 @@ Related to the authentication process using JWT. You can follow the :ref:`tut1`
|
||||
| | | configuration. |
|
||||
| PGRST300 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst301: | 401 | Any error related to the verification of the JWT, |
|
||||
| | | which means that the JWT provided is invalid in some way. |
|
||||
| .. _pgrst301: | 401 | Provided JWT couldn't be decoded or it is invalid. |
|
||||
| | | |
|
||||
| PGRST301 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst302: | 401 | Attempted to do a request without |
|
||||
| | | :ref:`authentication <client_auth>` when the anonymous role |
|
||||
| PGRST302 | | is disabled by not setting it in :ref:`db-anon-role`. |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
| .. _pgrst303: | 401 | :ref:`JWT claims validation <jwt_claims_validation>` |
|
||||
| | | or parsing failed. |
|
||||
| PGRST303 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
.. The Internal Errors Group X** is always at the end
|
||||
|
||||
@@ -331,6 +354,8 @@ Internal errors. If you encounter any of these, you may have stumbled on a Postg
|
||||
| PGRSTX00 | | |
|
||||
+---------------+-------------+-------------------------------------------------------------+
|
||||
|
||||
.. _custom_errors:
|
||||
|
||||
Custom Errors
|
||||
=============
|
||||
|
||||
@@ -427,3 +452,24 @@ For non standard HTTP status, you can optionally add ``status_text`` to describe
|
||||
detail = '{"status":419,"status_text":"Page Expired","headers":{"X-Powered-By":"Nerd Rage"}}';
|
||||
|
||||
If PostgREST can't parse the JSON objects ``message`` and ``detail``, it will throw a ``PGRST121`` error. See :ref:`Errors from PostgREST<pgrst1**>`.
|
||||
|
||||
.. _proxy-status_header:
|
||||
|
||||
Proxy-Status Header
|
||||
===================
|
||||
|
||||
For error cases, the standard `Proxy-Status <https://www.rfc-editor.org/rfc/rfc9209.html#name-the-proxy-status-http-field>`_ header is returned with the error code. The error code comes from either :ref:`PostgREST <pgrst_errors>`, :ref:`PostgreSQL <postgresql_errors>` or :ref:`Custom <custom_errors>` errors. This is useful when doing ``HEAD`` requests where the HTTP status is not descriptive enough.
|
||||
|
||||
For example, doing a request on a table with high count (say 30_000_000), we get:
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HEAD /table HTTP/1.1
|
||||
Prefer: count=exact
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 500 Internal Server Error
|
||||
Proxy-Status: PostgREST; error=57014
|
||||
|
||||
The PostgreSQL error code ``57014`` (`ref <https://www.postgresql.org/docs/current/errcodes-appendix.html>`_) reveals that the error is due to a short ``statement_timeout`` value.
|
||||
|
||||
@@ -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.
|
||||
@@ -15,28 +15,70 @@ Observability allows measuring a system's current state based on the data it gen
|
||||
Logs
|
||||
====
|
||||
|
||||
PostgREST logs basic request information to ``stdout``, including the authenticated user if available, the requesting IP address and user agent, the URL requested, and HTTP response status.
|
||||
PostgREST logs basic request information to ``stdout``, including the authenticated user if available, the requesting IP address and user agent, the URL requested, the HTTP response status and the response body size in bytes if available.
|
||||
|
||||
With :ref:`log-level` set to ``info``, we get:
|
||||
|
||||
.. code::
|
||||
|
||||
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 - user [26/Jul/2021:01:56:38 -0500] "GET /clients HTTP/1.1" 200 56 "" "curl/7.64.0"
|
||||
127.0.0.1 - anonymous [26/Jul/2021:01:56:48 -0500] "GET /unexistent HTTP/1.1" 404 162 "" "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::
|
||||
|
||||
06/May/2024:08:16:11 -0500: Starting PostgREST 12.1...
|
||||
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: Listening on port 3000
|
||||
06/May/2024:08:16:11 -0500: Connection Pool initialized with a maximum size of 10 connections
|
||||
06/May/2024:08:16:11 -0500: API server listening on port 3000
|
||||
06/May/2024:08:16:11 -0500: Listening for database 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
|
||||
|
||||
.. note::
|
||||
|
||||
Logs are based on the ``log-level`` setting. See :ref:`log-level`.
|
||||
|
||||
.. _sql_query_logs:
|
||||
|
||||
SQL Query Logs
|
||||
--------------
|
||||
|
||||
To log the :ref:`main SQL query <main_query>` executed for a request, set the :ref:`log-query` to ``main-query``.
|
||||
It will be logged based on the current :ref:`log-level` setting.
|
||||
For example, with this configuration:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
log-level = "warn"
|
||||
log-query = "main-query"
|
||||
|
||||
The SQL queries will only be logged on ``400`` HTTP errors and up.
|
||||
So, if the user requests a resource without sufficient privileges:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl "localhost:3000/protected_table"
|
||||
|
||||
This will be logged by PostgREST:
|
||||
|
||||
.. code::
|
||||
|
||||
17/Feb/2025:17:28:15 -0500: WITH pgrst_source AS ( SELECT "public"."protected_table".* FROM "public"."protected_table" ) SELECT null::bigint AS total_result_set, pg_catalog.count(_postgrest_t) AS page_total, coalesce(json_agg(_postgrest_t), '[]') AS body, nullif(current_setting('response.headers', true), '') AS response_headers, nullif(current_setting('response.status', true), '') AS response_status, '' AS response_inserted FROM ( SELECT * FROM pgrst_source ) _postgrest_t
|
||||
127.0.0.1 - web_anon [17/Feb/2025:17:28:15 -0500] "GET /protected_table HTTP/1.1" 401 99 "" "curl/8.7.1"
|
||||
|
||||
Database Logs
|
||||
-------------
|
||||
|
||||
Currently PostgREST doesn't log the SQL commands executed against the underlying database.
|
||||
|
||||
To find the SQL operations, you can watch the database logs. By default PostgreSQL does not keep these logs, so you'll need to make the configuration changes below.
|
||||
Additionally, to find all the SQL operations, you can watch the database logs. By default PostgreSQL does not keep these logs, so you'll need to make the configuration changes below.
|
||||
|
||||
Find :code:`postgresql.conf` inside your PostgreSQL data directory (to find that, issue the command :code:`show data_directory;`). Either find the settings scattered throughout the file and change them to the following values, or append this block of code to the end of the configuration file.
|
||||
|
||||
@@ -86,6 +128,11 @@ The ``metrics`` endpoint on the :ref:`admin_server` endpoint provides metrics in
|
||||
|
||||
curl "http://localhost:3001/metrics"
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; charset=utf-8
|
||||
|
||||
# HELP pgrst_schema_cache_query_time_seconds The query time in seconds of the last schema cache load
|
||||
# TYPE pgrst_schema_cache_query_time_seconds gauge
|
||||
pgrst_schema_cache_query_time_seconds 1.5937927e-2
|
||||
@@ -194,12 +241,17 @@ You can enable tracing HTTP requests by setting :ref:`server-trace-header`. Spec
|
||||
HTTP/1.1 200 OK
|
||||
X-Request-Id: 123
|
||||
|
||||
Proxy-Status Header
|
||||
-------------------
|
||||
|
||||
See :ref:`proxy-status_header`.
|
||||
|
||||
.. _server-timing_header:
|
||||
|
||||
Server-Timing Header
|
||||
--------------------
|
||||
|
||||
You can enable the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing>`_ header by setting :ref:`server-timing-enabled` on.
|
||||
You can enable the `Server-Timing <https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Server-Timing>`_ header by setting :ref:`server-timing-enabled` on.
|
||||
This header communicates metrics of the different phases in the request-response cycle.
|
||||
|
||||
.. code-block:: bash
|
||||
@@ -223,6 +275,27 @@ This header communicates metrics of the different phases in the request-response
|
||||
|
||||
We're working on lowering the duration of the ``parse`` and ``plan`` stages on https://github.com/PostgREST/postgrest/issues/2816.
|
||||
|
||||
.. _content-length_header:
|
||||
|
||||
Content-Length Header
|
||||
---------------------
|
||||
|
||||
You can verify the response body size in bytes in the `Content-Length header <https://httpwg.org/specs/rfc9110.html#field.content-length>`_.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -i 'localhost:3000/users'
|
||||
|
||||
.. code-block:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Length: 104
|
||||
|
||||
Note that this header won't be returned on ``HEAD`` requests for optimization purposes (see :ref:`head_req`).
|
||||
This is in line with `RFC 9110 <https://httpwg.org/specs/rfc9110.html#field.content-length>`_.
|
||||
|
||||
The body size is also present in the :ref:`PostgREST logs <pgrst_logging>`.
|
||||
|
||||
.. _explain_plan:
|
||||
|
||||
Execution plan
|
||||
|
||||
@@ -47,16 +47,12 @@ For docker you can do:
|
||||
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
|
||||
|
||||
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:
|
||||
|
||||
Automatic Schema Cache Reloading
|
||||
|
||||
@@ -284,11 +284,10 @@ However, starting from PostgreSQL 15, you can grant privileges for these setting
|
||||
|
||||
GRANT SET ON PARAMETER <setting> TO <authenticator>;
|
||||
|
||||
Function Settings
|
||||
~~~~~~~~~~~~~~~~~
|
||||
Hoisted Function Settings
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In addition to :ref:`impersonated_settings`, PostgREST will also apply function settings as transaction-scoped settings. This allows functions settings to override
|
||||
the impersonated and connection role settings.
|
||||
PostgREST can "hoist" function settings to transaction-scoped settings. This allows functions settings to override the impersonated and connection role settings.
|
||||
|
||||
.. code-block:: postgres
|
||||
|
||||
@@ -303,7 +302,7 @@ When calling the above function (see :ref:`functions`), the statement timeout wi
|
||||
|
||||
.. note::
|
||||
|
||||
Only the transactions that are hoisted by config :ref:`db-hoisted-tx-settings` will be applied.
|
||||
Only the settings in :ref:`db-hoisted-tx-settings` will be hoisted.
|
||||
|
||||
.. _main_query:
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
docutils==0.20.1
|
||||
sphinx-copybutton
|
||||
sphinx-intl
|
||||
sphinx-rtd-theme>=0.5.1
|
||||
sphinx-rtd-dark-mode>=1.3.0
|
||||
sphinx-tabs>=3.2.0
|
||||
sphinx>=5.0.2
|
||||
# This file is auto-generated by postgrest-nixpkgs-upgrade
|
||||
sphinx==7.4.7
|
||||
sphinx-copybutton==0.5.2
|
||||
sphinx-rtd-dark-mode==1.3.0
|
||||
sphinx-rtd-theme==3.0.2
|
||||
sphinx-tabs==3.4.7
|
||||
sphinxext-opengraph==0.9.1
|
||||
urllib3==2.2.1
|
||||
|
||||
@@ -28,13 +28,24 @@
|
||||
|
||||
pacman -S postgrest
|
||||
|
||||
.. tab:: Nix
|
||||
.. tab:: Nix via nixpkgs
|
||||
|
||||
You can install PostgREST from nixpkgs.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
nix-env -i haskellPackages.postgrest
|
||||
nix-env -i postgrest
|
||||
|
||||
.. tab:: Nix via flake
|
||||
|
||||
You can install PostgREST via flake.
|
||||
|
||||
.. code:: nix
|
||||
|
||||
{
|
||||
inputs.postgrest.url = "github:postgrest/postgrest";
|
||||
# ...
|
||||
}
|
||||
|
||||
.. group-tab:: Windows
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Step 1. Relax, we'll help
|
||||
-------------------------
|
||||
|
||||
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
|
||||
Step 1. 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.
|
||||
@@ -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!
|
||||
|
||||
|
||||
Step 3. Install PostgREST
|
||||
Step 2. Install PostgREST
|
||||
-------------------------
|
||||
|
||||
Using a Package Manager
|
||||
@@ -122,7 +117,7 @@ If everything is working correctly it will print out its version and the availab
|
||||
</details>
|
||||
</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:
|
||||
@@ -185,7 +180,7 @@ Now quit out of psql; it's time to start the API!
|
||||
|
||||
\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:
|
||||
@@ -218,12 +213,8 @@ You should see something similar to:
|
||||
.. code-block:: text
|
||||
|
||||
Starting PostgREST 12.0.2...
|
||||
Attempting to connect to the database...
|
||||
Connection successful
|
||||
Listening on port 3000
|
||||
Config reloaded
|
||||
Listening for notifications on the pgrst channel
|
||||
Schema cache loaded
|
||||
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
|
||||
API server listening on port 3000
|
||||
|
||||
It's now ready to serve web requests. There are many nice graphical API exploration tools you can use, but for this tutorial we'll use :code:`curl` because it's likely to be installed on your system already. Open a new terminal (leaving the one open that PostgREST is running inside). Try doing an HTTP request for the todos.
|
||||
|
||||
|
||||
@@ -52,17 +52,31 @@ Check that the :code:`tutorial.conf` (created in the previous tutorial) has the
|
||||
|
||||
If the PostgREST server is still running from the previous tutorial, restart it to load the updated configuration file.
|
||||
|
||||
.. _tut1_step3:
|
||||
|
||||
Step 3. Sign a Token
|
||||
--------------------
|
||||
|
||||
Ordinarily your own code in the database or in another server will create and sign authentication tokens, but for this tutorial we will make one "by hand." Go to `jwt.io <https://jwt.io/#debugger-io>`_ and fill in the fields like this:
|
||||
Ordinarily your own code in the database or in another server will create and sign authentication tokens, but for this tutorial we will make one "by hand" using ``bash`` and ``openssl``.
|
||||
|
||||
.. figure:: ../_static/tuts/tut1-jwt-io.png
|
||||
:alt: jwt.io interface
|
||||
.. code:: bash
|
||||
|
||||
How to create a token at https://jwt.io
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
**Remember to fill in the secret you generated rather than the word "secret".** After you have filled in the secret and payload, the encoded data on the left will update. Copy the encoded token.
|
||||
JWT_SECRET='test_secret_that_is_at_least_32_characters_long'
|
||||
|
||||
_base64 () { openssl base64 -e -A | tr '+/' '-_' | tr -d '='; }
|
||||
|
||||
header=$(echo -n '{"alg":"HS256","typ":"JWT"}' | _base64)
|
||||
|
||||
payload=$(echo -n "{\"role\":\"todo_user\"}" | _base64)
|
||||
|
||||
signature=$(echo -n "$header.$payload" | openssl dgst -sha256 -hmac "$JWT_SECRET" -binary | _base64)
|
||||
|
||||
echo -n "$header.$payload.$signature"
|
||||
|
||||
**Remember to fill in the secret you generated rather than keeping the "test_secret_that_is_at_least_32_characters_long".** After you have filled in the secret and payload, the encoded data on the left will update. Copy the encoded token.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -145,14 +159,22 @@ To observe expiration in action, we'll add an :code:`exp` claim of five minutes
|
||||
|
||||
select extract(epoch from now() + '5 minutes'::interval) :: integer;
|
||||
|
||||
Go back to jwt.io and change the payload to
|
||||
Or in ``bash``:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"role": "todo_user",
|
||||
"exp": 123456789
|
||||
}
|
||||
.. code-block:: bash
|
||||
|
||||
exp=$(( EPOCHSECONDS + 5*60 )) # five minutes
|
||||
|
||||
echo $exp
|
||||
|
||||
Go back to :ref:`tut1_step3` and change the payload to
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
payload=$(echo -n "{\"role\":\"todo_user\",\"exp\":\"123456789\"}" | _base64)
|
||||
|
||||
echo -n "$header.$payload.$signature"
|
||||
|
||||
**NOTE**: Don't forget to change the dummy epoch value :code:`123456789` in the snippet above to the epoch value returned by the :code:`psql` command.
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1731165248,
|
||||
"narHash": "sha256-DiHFKIdBmMx5/DUARhVqaxvEIiy4EE6Eqs9Qs4oxme8=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a90280100f41a10914edfe729a4053e60c92b8e3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
description = "REST API for any Postgres database";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
};
|
||||
|
||||
nixConfig = {
|
||||
extra-substituters = "https://postgrest.cachix.org";
|
||||
extra-trusted-public-keys = "postgrest.cachix.org-1:icgW4R15fz1+LqvhPjt4EnX/r19AaqxiVV+1olwlZtI=";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, ... }:
|
||||
let
|
||||
systems = [
|
||||
"aarch64-darwin"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"x86_64-linux"
|
||||
];
|
||||
|
||||
pgrstFor = system: import ./default.nix {
|
||||
inherit system;
|
||||
nixpkgsVersion = {
|
||||
owner = "nixos";
|
||||
repo = "nixpkgs";
|
||||
inherit (nixpkgs) rev;
|
||||
tarballHash = nixpkgs.narHash;
|
||||
};
|
||||
};
|
||||
|
||||
genSystems = f: nixpkgs.lib.genAttrs systems (system: f (pgrstFor system));
|
||||
in
|
||||
{
|
||||
packages = genSystems (attrs: {
|
||||
default = attrs.postgrestPackage;
|
||||
profiled = attrs.postgrestProfiled;
|
||||
} // nixpkgs.lib.optionalAttrs (attrs ? postgrestStatic) {
|
||||
static = attrs.postgrestStatic;
|
||||
});
|
||||
|
||||
apps = genSystems (attrs: {
|
||||
default = {
|
||||
type = "app";
|
||||
program = "${attrs.postgrestStatic or attrs.postgrestPackage}/bin/postgrest";
|
||||
meta.description = "REST API for any Postgres database";
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -72,45 +72,53 @@ The PostgREST utilities available in `nix-shell` all have names that begin with
|
||||
```bash
|
||||
# Note: The utilities listed here might not be up to date.
|
||||
[nix-shell]$ postgrest-<tab>
|
||||
postgrest-build postgrest-test-spec
|
||||
postgrest-check postgrest-watch
|
||||
postgrest-clean postgrest-with-all
|
||||
postgrest-coverage postgrest-with-postgresql-10
|
||||
postgrest-lint postgrest-with-postgresql-11
|
||||
postgrest-run postgrest-with-postgresql-12
|
||||
postgrest-style postgrest-with-postgresql-13
|
||||
postgrest-style-check postgrest-with-postgresql-9.6
|
||||
postgrest-test-io
|
||||
postgrest-build postgrest-profiled-run
|
||||
postgrest-check postgrest-push-cachix
|
||||
postgrest-clean postgrest-release
|
||||
postgrest-coverage postgrest-repl
|
||||
postgrest-coverage-draft-overlay postgrest-run
|
||||
postgrest-docs-build postgrest-style
|
||||
postgrest-docs-check postgrest-style-check
|
||||
postgrest-docs-dictcheck postgrest-test-big-schema
|
||||
postgrest-docs-linkcheck postgrest-test-doctests
|
||||
postgrest-docs-render postgrest-test-io
|
||||
postgrest-docs-serve postgrest-test-memory
|
||||
postgrest-docs-spellcheck postgrest-test-replica
|
||||
postgrest-dump-minimal-imports postgrest-test-spec
|
||||
postgrest-dump-schema postgrest-test-spec-idempotence
|
||||
postgrest-gen-ctags postgrest-watch
|
||||
postgrest-gen-jwt postgrest-with-all
|
||||
postgrest-gen-secret postgrest-with-git
|
||||
postgrest-git-hooks postgrest-with-pgrst
|
||||
postgrest-hsie-graph-modules postgrest-with-postgresql-12
|
||||
postgrest-hsie-graph-symbols postgrest-with-postgresql-13
|
||||
postgrest-hsie-minimal-imports postgrest-with-postgresql-14
|
||||
postgrest-lint postgrest-with-postgresql-15
|
||||
postgrest-loadtest postgrest-with-postgresql-16
|
||||
postgrest-loadtest-against postgrest-with-postgresql-17
|
||||
postgrest-loadtest-report postgrest-with-slow-pg
|
||||
postgrest-nixpkgs-upgrade postgrest-with-slow-postgrest
|
||||
postgrest-parallel-curl
|
||||
...
|
||||
|
||||
[nix-shell]$
|
||||
|
||||
```
|
||||
|
||||
Some additional modules like `memory`, `docker` and `release`
|
||||
have large dependencies that would need to be built before the shell becomes
|
||||
The `docker` module has large dependencies to be build before the shell becomes
|
||||
available, which could take an especially long time if the cachix binary cache
|
||||
is not used. You can activate those by passing a flag to `nix-shell` with
|
||||
`nix-shell --arg <module> true`. This will make the respective utilities available:
|
||||
is not used. You can activate it by passing a flag to `nix-shell` with
|
||||
`nix-shell --arg docker true`. This will make the respective utilities available:
|
||||
|
||||
```bash
|
||||
$ nix-shell --arg memory true
|
||||
[nix-shell]$ postgrest-<tab>
|
||||
postgrest-build postgrest-test-spec
|
||||
postgrest-check postgrest-watch
|
||||
postgrest-clean postgrest-with-all
|
||||
postgrest-coverage postgrest-with-postgresql-10
|
||||
postgrest-lint postgrest-with-postgresql-11
|
||||
postgrest-run postgrest-with-postgresql-12
|
||||
postgrest-style postgrest-with-postgresql-13
|
||||
postgrest-style-check postgrest-with-postgresql-9.6
|
||||
postgrest-test-io
|
||||
postgrest-test-memory
|
||||
$ nix-shell --arg docker true
|
||||
[nix-shell]$ postgrest-docker-<tab>
|
||||
postgrest-docker-load
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Note that `postgrest-test-memory` is now also available.
|
||||
Note that `postgrest-docker-load` is now also available.
|
||||
|
||||
To run one-off commands, you can also use `nix-shell --run <command>`, which
|
||||
will launch the Nix shell, run that one command and exit. Note that the tab
|
||||
@@ -371,10 +379,8 @@ that).
|
||||
|
||||
We also use `default.nix` to load our pinned version of the `nixpkgs`
|
||||
repository. This set of packages will always be the same, independently from
|
||||
where or when you use it. The pinned version can be upgraded with the small
|
||||
`nixpkgs-upgrade` utility. Running `nixpkgs-upgrade > nix/nixpkgs-version.nix`
|
||||
in `nix-shell` will upgrade the pinned version to the latest `nixpkgs-unstable`
|
||||
version.
|
||||
where or when you use it. The pinned version is taken from `flake.lock` and
|
||||
can be updated with `nix flake update`.
|
||||
|
||||
### `shell.nix`
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
{ buildPythonPackage
|
||||
, fetchFromGitHub
|
||||
, lib
|
||||
, nose
|
||||
, sphinx
|
||||
, sphinx-rtd-theme
|
||||
}:
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "sphinx-rtd-dark-mode";
|
||||
version = "1.3.0";
|
||||
format = "setuptools";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "MrDogeBro";
|
||||
repo = "sphinx_rtd_dark_mode";
|
||||
rev = "refs/tags/v${version}";
|
||||
hash = "sha256-N5KG2Wqn9wfGNY3VH4FnBce1aZUbnvVmwD10Loe0Qn4=";
|
||||
};
|
||||
|
||||
propagatedBuildInputs = [
|
||||
sphinx-rtd-theme
|
||||
];
|
||||
|
||||
nativeCheckInputs = [
|
||||
nose
|
||||
sphinx
|
||||
];
|
||||
|
||||
checkPhase = ''
|
||||
runHook preCheck
|
||||
nosetests tests
|
||||
runHook postCheck
|
||||
'';
|
||||
|
||||
pythonImportsCheck = [
|
||||
"sphinx_rtd_dark_mode"
|
||||
];
|
||||
|
||||
meta = with lib; {
|
||||
description = "Adds a toggleable dark mode to the Read the Docs theme for Sphinx.";
|
||||
homepage = "https://github.com/MrDogeBro/sphinx_rtd_dark_mode";
|
||||
changelog = "https://github.com/MrDogeBro/sphinx_rtd_dark_mode/releases/tag/v${version}";
|
||||
license = licenses.mit;
|
||||
};
|
||||
}
|
||||
@@ -19,6 +19,10 @@ stdenv.mkDerivation {
|
||||
pname = "libpq";
|
||||
inherit (postgresql) src version patches;
|
||||
|
||||
__structuredAttrs = true;
|
||||
env.CFLAGS = "-fdata-sections -ffunction-sections"
|
||||
+ (if stdenv.cc.isClang then " -flto" else " -fmerge-constants -Wl,--gc-sections");
|
||||
|
||||
configureFlags = [
|
||||
"--without-gssapi"
|
||||
"--without-icu"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
# Pinned version of Nixpkgs, generated with postgrest-nixpkgs-upgrade.
|
||||
{
|
||||
owner = "NixOS";
|
||||
repo = "nixpkgs";
|
||||
date = "2024-04-20";
|
||||
rev = "92d295f588631b0db2da509f381b4fb1e74173c5";
|
||||
tarballHash = "162w28y4i5c8g5qhjvs827qxphf2a8n4c8fwhcywzl1j1a35h2im";
|
||||
}
|
||||
@@ -3,9 +3,6 @@
|
||||
checked-shell-script = import ./checked-shell-script;
|
||||
gitignore = import ./gitignore.nix;
|
||||
haskell-packages = import ./haskell-packages.nix;
|
||||
postgis = import ./postgis.nix;
|
||||
postgresql-libpq = import ./postgresql-libpq.nix;
|
||||
postgresql-legacy = import ./postgresql-legacy.nix;
|
||||
postgresql-future = import ./postgresql-future.nix;
|
||||
slocat = import ./slocat.nix;
|
||||
}
|
||||
|
||||
@@ -48,31 +48,36 @@ let
|
||||
|
||||
# Before upgrading fuzzyset to 0.3, check: https://github.com/PostgREST/postgrest/issues/3329
|
||||
# jailbreak, because hspec limit for tests
|
||||
fuzzyset = lib.doJailbreak
|
||||
(prev.callHackageDirect
|
||||
fuzzyset = prev.fuzzyset_0_2_4;
|
||||
|
||||
hasql-pool = lib.dontCheck (prev.callHackageDirect
|
||||
{
|
||||
pkg = "fuzzyset";
|
||||
ver = "0.2.4";
|
||||
sha256 = "sha256-lpkrTFcR0B4rT/P6x7ui31Twgq7BBj6KIvjKyqXKdpc=";
|
||||
pkg = "hasql-pool";
|
||||
ver = "1.0.1";
|
||||
sha256 = "sha256-Hf1f7lX0LWkjrb25SDBovCYPRdmUP1H6pAxzi7kT4Gg=";
|
||||
}
|
||||
{ });
|
||||
|
||||
hasql-pool = lib.dontCheck prev.hasql-pool_1_0_1;
|
||||
|
||||
postgresql-libpq = lib.dontCheck
|
||||
(prev.postgresql-libpq.override {
|
||||
postgresql = super.libpq;
|
||||
});
|
||||
|
||||
hasql-notifications = lib.dontCheck (prev.callHackageDirect
|
||||
{
|
||||
pkg = "hasql-notifications";
|
||||
ver = "0.2.2.0";
|
||||
sha256 = "sha256-73OQ9/su2qvO7HavF3xuuNWLXSXyB9reBUQDaHys06I=";
|
||||
ver = "0.2.2.2";
|
||||
sha256 = "sha256-myKwlug7OgTa/qP6mHfCD+5Q8IhM17JvpJBfSo+M01k=";
|
||||
}
|
||||
{ }
|
||||
);
|
||||
{ });
|
||||
|
||||
# newer nixpkgs already has 0.10., so we fallback to default for forward compat
|
||||
jose-jwt = prev.jose-jwt_0_10_0 or prev.jose-jwt;
|
||||
|
||||
postgresql-libpq = lib.dontCheck (prev.callHackageDirect
|
||||
{
|
||||
pkg = "postgresql-libpq";
|
||||
ver = "0.10.1.0";
|
||||
sha256 = "sha256-tXOMqCO8opMilI9rx0D+njqjIjbZsH168Bzb8Aq8Ff4=";
|
||||
}
|
||||
{
|
||||
postgresql = super.libpq;
|
||||
});
|
||||
};
|
||||
in
|
||||
{
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
final: prev:
|
||||
let
|
||||
postgis_3_2_3 = rec {
|
||||
version = "3.2.3";
|
||||
src = final.fetchurl {
|
||||
url = "https://download.osgeo.org/postgis/source/postgis-${version}.tar.gz";
|
||||
sha256 = "sha256-G02LXHVuWrpZ77wYM7Iu/k1lYneO7KVvpJf+susTZow=";
|
||||
};
|
||||
meta.broken = false;
|
||||
};
|
||||
in
|
||||
{
|
||||
postgresql_11 = prev.postgresql_11.override { this = final.postgresql_11; } // {
|
||||
pkgs = prev.postgresql_11.pkgs // {
|
||||
postgis = prev.postgresql_11.pkgs.postgis.overrideAttrs (_: postgis_3_2_3);
|
||||
};
|
||||
};
|
||||
postgresql_10 = prev.postgresql_10.override { this = final.postgresql_11; } // {
|
||||
pkgs = prev.postgresql_10.pkgs // {
|
||||
postgis = prev.postgresql_10.pkgs.postgis.overrideAttrs (_: postgis_3_2_3);
|
||||
};
|
||||
};
|
||||
postgresql_9_6 = prev.postgresql_9_6.override { this = final.postgresql_11; } // {
|
||||
pkgs = prev.postgresql_9_6.pkgs // {
|
||||
postgis = prev.postgresql_9_6.pkgs.postgis.overrideAttrs (_: postgis_3_2_3);
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
_: _:
|
||||
# Overlay that adds future versions of PostgreSQL that are supported by
|
||||
# PostgREST.
|
||||
{
|
||||
## Example for including a postgresql version from a specific nixpks commit:
|
||||
##
|
||||
# postgresql_16 =
|
||||
# let
|
||||
# rev = "5148520bfab61f99fd25fb9ff7bfbb50dad3c9db";
|
||||
# tarballHash = "1dfjmz65h8z4lk845724vypzmf3dbgsdndjpj8ydlhx6c7rpcq3p";
|
||||
#
|
||||
# pinnedPkgs =
|
||||
# builtins.fetchTarball {
|
||||
# url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
|
||||
# sha256 = tarballHash;
|
||||
# };
|
||||
# in
|
||||
# (import pinnedPkgs { }).pkgs.postgresql_16;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
_: _:
|
||||
# Overlay that adds legacy versions of PostgreSQL that are supported by
|
||||
# PostgREST.
|
||||
{
|
||||
# PostgreSQL 9.6 was removed from Nixpkgs with
|
||||
# https://github.com/NixOS/nixpkgs/commit/757dd008b2f2926fc0f7688fa8189f930ea47521
|
||||
# We pin its parent commit to get the last version that was available.
|
||||
postgresql_9_6 =
|
||||
let
|
||||
rev = "571cbf3d1db477058303cef8754fb85a14e90eb7";
|
||||
tarballHash = "0q74wn418i1bn5sssacmw8ykpmqvzr0s93sj6pbs3rf6bf134fkz";
|
||||
pinnedPkgs =
|
||||
builtins.fetchTarball {
|
||||
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
|
||||
sha256 = tarballHash;
|
||||
};
|
||||
in
|
||||
(import pinnedPkgs { }).pkgs.postgresql_9_6;
|
||||
|
||||
# PostgreSQL 10 was removed from Nixpkgs with
|
||||
# https://github.com/NixOS/nixpkgs/commit/aa1483114bb329fee7e1266100b8d8921ed4723f
|
||||
# We pin its parent commit to get the last version that was available.
|
||||
postgresql_10 =
|
||||
let
|
||||
rev = "79661ba7e2fb96ebefbb537458a5bbae9dc5bd1a";
|
||||
tarballHash = "0rn796pfn4sg90ai9fdnwmr10a2s835p1arazzgz46h6s5cxvq97";
|
||||
pinnedPkgs =
|
||||
builtins.fetchTarball {
|
||||
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
|
||||
sha256 = tarballHash;
|
||||
};
|
||||
in
|
||||
(import pinnedPkgs { }).pkgs.postgresql_10;
|
||||
|
||||
# PostgreSQL 11 was removed from Nixpkgs with
|
||||
# https://github.com/NixOS/nixpkgs/commit/1220a4d4dd1a4590780a5e1c18d1333a121be366
|
||||
# We pin its parent commit to get the last version that was available.
|
||||
postgresql_11 =
|
||||
let
|
||||
rev = "f5458516e42cc5cb4123cc2d93f45c240548aa18";
|
||||
tarballHash = "1h03621sxfhw4z6ya74k6c2lyx3z7pvf2jcg4vs7i01yz2m6w3cv";
|
||||
pinnedPkgs =
|
||||
builtins.fetchTarball {
|
||||
url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";
|
||||
sha256 = tarballHash;
|
||||
};
|
||||
in
|
||||
(import pinnedPkgs { }).pkgs.postgresql_11;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
_: super:
|
||||
{
|
||||
libpq = super.callPackage ../libpq.nix {
|
||||
# Depending on which nixpkgs version is pinned, libpq might either be available already - or not.
|
||||
libpq = super.libpq or (super.callPackage ../libpq.nix {
|
||||
postgresql = super.postgresql_16;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,12 +4,8 @@
|
||||
, src
|
||||
}:
|
||||
let
|
||||
# This builds a static PostgREST exectuable based on pkgsStatic.
|
||||
# pkgsStatic is based on musl, so is a kind of cross-compilation.
|
||||
# We still make this explicit here via pkgsCross, because we need
|
||||
# to get postgresql/libpq for musl, too.
|
||||
pkgsCross = pkgs.pkgsCross.musl64;
|
||||
inherit (pkgsCross) pkgsStatic;
|
||||
# This builds a static PostgREST executable based on pkgsStatic.
|
||||
inherit (pkgs) pkgsStatic;
|
||||
inherit (pkgsStatic.haskell) lib;
|
||||
|
||||
packagesStatic =
|
||||
@@ -25,9 +21,16 @@ let
|
||||
|
||||
overrides = pkgs.lib.composeExtensions old.overrides (_: prev: {
|
||||
postgresql-libpq = (lib.overrideCabal prev.postgresql-libpq {
|
||||
# TODO: This section can be simplified when this PR has made it's way to us:
|
||||
# https://github.com/NixOS/nixpkgs/pull/286370
|
||||
# Additionally, we need to use the default version in nixpkgs, otherwise the
|
||||
# override will not be active as well.
|
||||
# Using use-pkg-config flag, because pg_config won't work when cross-compiling
|
||||
configureFlags = [ "-fuse-pkg-config" ];
|
||||
# postgresql doesn't build in the fully static overlay - but the default
|
||||
# derivation is built with static libraries anyway.
|
||||
libraryPkgconfigDepends = [ pkgsCross.libpq ];
|
||||
libraryPkgconfigDepends = [ pkgsStatic.libpq ];
|
||||
librarySystemDepends = [ ];
|
||||
}).overrideAttrs (_: prevAttrs: {
|
||||
buildInputs = prevAttrs.buildInputs ++ [ pkgsStatic.openssl ];
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ let
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
exec ${cabal-install}/bin/cabal v2-build ${devCabalOptions} "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
@@ -59,6 +60,35 @@ let
|
||||
postgrest "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
|
||||
runProfiled =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-profiled-run";
|
||||
docs = "Run a profiled build of postgREST. This will generate a postgrest.prof file that can be used to do optimization.";
|
||||
args =
|
||||
[
|
||||
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [postgrest_test_anonymous], [PostgREST anonymous role])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL], [1], [PostgREST pool size])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL_ACQUISITION_TIMEOUT], [1], [PostgREST pool timeout])"
|
||||
"ARG_USE_ENV([PGRST_JWT_SECRET], [reallyreallyreallyreallyverysafe], [PostgREST JWT secret])"
|
||||
"ARG_LEFTOVERS([PostgREST arguments])"
|
||||
];
|
||||
workingDir = "/";
|
||||
withEnv = postgrest.env;
|
||||
}
|
||||
''
|
||||
export PGRST_DB_ANON_ROLE
|
||||
export PGRST_DB_POOL
|
||||
export PGRST_DB_POOL_ACQUISITION_TIMEOUT
|
||||
export PGRST_JWT_SECRET
|
||||
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal --builddir="dist-prof" v2-build --enable-profiling --disable-shared exe:postgrest
|
||||
${cabal-install}/bin/cabal --builddir="dist-prof" v2-run -- \
|
||||
postgrest +RTS -p -h -RTS "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
|
||||
repl =
|
||||
checkedShellScript
|
||||
{
|
||||
@@ -80,6 +110,7 @@ buildToolbox
|
||||
build
|
||||
clean
|
||||
run
|
||||
runProfiled
|
||||
repl;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -246,6 +246,7 @@ let
|
||||
}
|
||||
''
|
||||
mkdir -p "$_arg_dumpdir"
|
||||
${cabal-install}/bin/cabal v2-update
|
||||
${cabal-install}/bin/cabal v2-build ${devCabalOptions} \
|
||||
--builddir="$tmpdir" \
|
||||
--ghc-option=-ddump-minimal-imports \
|
||||
@@ -333,20 +334,29 @@ let
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-gen-jwt";
|
||||
docs = "Generate a JWT";
|
||||
docs = ''
|
||||
Generate a JWT. Example: postgrest-gen-jwt --exp 10 postgrest_test_author
|
||||
|
||||
# This can be used to quickly prove a JWT expiry
|
||||
$ curl localhost:3000/authors_only -H "Authorization: Bearer \$(postgrest-gen-jwt --exp -31 postgrest_test_author)"
|
||||
'';
|
||||
args = [
|
||||
"ARG_POSITIONAL_SINGLE([role], [role for the jwt payload])"
|
||||
"ARG_OPTIONAL_SINGLE([secret],, [secret used to sign the JWT], [reallyreallyreallyreallyverysafe])"
|
||||
"ARG_OPTIONAL_SINGLE([exp],, [seconds for JWT expiry, it accepts negative values], [3600])"
|
||||
];
|
||||
}
|
||||
''
|
||||
# From https://stackoverflow.com/questions/59002949/how-to-create-a-json-web-token-jwt-using-openssl-shell-commands
|
||||
# Based on https://stackoverflow.com/questions/59002949/how-to-create-a-json-web-token-jwt-using-openssl-shell-commands
|
||||
|
||||
# Construct the header
|
||||
jwt_header=$(echo -n '{"alg":"HS256","typ":"JWT"}' | base64 | sed s/\+/-/g | sed 's/\//_/g' | sed -E s/=+$//)
|
||||
|
||||
# Construct the exp value
|
||||
expiry=$((EPOCHSECONDS + _arg_exp))
|
||||
|
||||
# Construct the payload
|
||||
payload=$(echo -n "{\"role\":\"$_arg_role\"}" | base64 | sed s/\+/-/g |sed 's/\//_/g' | sed -E s/=+$//)
|
||||
payload=$(echo -n "{\"role\": \"$_arg_role\", \"exp\": $expiry}" | base64 | sed s/\+/-/g |sed 's/\//_/g' | sed -E s/=+$//)
|
||||
|
||||
# Convert secret to hex
|
||||
hexsecret=$(echo -n "$_arg_secret" | xxd -p | paste -sd "")
|
||||
|
||||
@@ -2,26 +2,41 @@
|
||||
, aspellDicts
|
||||
, buildToolbox
|
||||
, checkedShellScript
|
||||
, imagemagick
|
||||
, fetchPypi
|
||||
, lib
|
||||
, plantuml
|
||||
, python3
|
||||
, python3Packages
|
||||
, texlive
|
||||
, writeTextFile
|
||||
, writers
|
||||
, plantuml
|
||||
}:
|
||||
let
|
||||
selectPythonPackages = ps: [
|
||||
selectPythonPackages = ps:
|
||||
let
|
||||
# TODO: Remove with next nixpkgs update
|
||||
sphinx-rtd-theme = assert ps.sphinx-rtd-theme.version == "2.0.0"; ps.sphinx-rtd-theme.overrideAttrs rec {
|
||||
version = "3.0.2";
|
||||
src = fetchPypi {
|
||||
pname = "sphinx_rtd_theme";
|
||||
inherit version;
|
||||
hash = "sha256-t0V7wl3acjsgsIamcLmVPIWeq2CioD7o6yuyPhduX4U=";
|
||||
};
|
||||
};
|
||||
in
|
||||
[
|
||||
ps.sphinx
|
||||
ps.sphinx_rtd_theme
|
||||
ps.livereload
|
||||
ps.sphinx-tabs
|
||||
ps.sphinx-copybutton
|
||||
(ps.sphinx-rtd-dark-mode.override { inherit sphinx-rtd-theme; })
|
||||
sphinx-rtd-theme
|
||||
ps.sphinx-tabs
|
||||
ps.sphinxext-opengraph
|
||||
(ps.callPackage ../docs-extensions/sphinx-rtd-dark-mode.nix { })
|
||||
# TODO: Remove override once new sphinx-intl version (> 2.1.0) is released and available in nixpkgs
|
||||
(ps.sphinx-intl.overrideAttrs (drv: { nativeBuildInputs = drv.nativeBuildInputs ++ [ ps.six ]; }))
|
||||
];
|
||||
|
||||
requirements = writeTextFile {
|
||||
name = "requirements.txt";
|
||||
text = lib.concatMapStringsSep "\n" (pkg: "${pkg.pname}==${pkg.version}") (selectPythonPackages python3Packages);
|
||||
};
|
||||
|
||||
python = python3.withPackages selectPythonPackages;
|
||||
|
||||
build =
|
||||
@@ -61,42 +76,21 @@ let
|
||||
'';
|
||||
|
||||
render =
|
||||
let
|
||||
pdflatex = texlive.combine {
|
||||
inherit (texlive)
|
||||
amsmath
|
||||
booktabs
|
||||
cancel
|
||||
gensymb
|
||||
mathdots
|
||||
multirow
|
||||
pgf
|
||||
pgf-blur
|
||||
scheme-basic
|
||||
siunitx
|
||||
standalone
|
||||
yhmath
|
||||
;
|
||||
};
|
||||
in
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-docs-render";
|
||||
docs = "Render the diagrams.";
|
||||
workingDir = "/docs/_diagrams";
|
||||
withTmpDir = true;
|
||||
}
|
||||
''
|
||||
${pdflatex}/bin/pdflatex -halt-on-error -output-directory="$tmpdir" db.tex
|
||||
${imagemagick}/bin/convert -density 300 "$tmpdir/db.pdf" ../_static/db.png
|
||||
|
||||
${plantuml}/bin/plantuml arch.uml -o ../_static
|
||||
${plantuml}/bin/plantuml -tsvg uml/*.uml -o ../../_static
|
||||
${plantuml}/bin/plantuml -tsvg -darkmode uml/dark/*.uml -o ../../../_static
|
||||
'';
|
||||
|
||||
server =
|
||||
writers.writePython3
|
||||
"postgrest-docs-server"
|
||||
{ libraries = selectPythonPackages python3Packages; }
|
||||
{ libraries = selectPythonPackages python3Packages ++ [ python3Packages.livereload ]; }
|
||||
''
|
||||
import sys
|
||||
from livereload import Server, shell
|
||||
@@ -206,4 +200,5 @@ buildToolbox
|
||||
serve
|
||||
spellcheck;
|
||||
};
|
||||
extra = { inherit requirements; };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# generates a file to be used by the vegeta load testing tool
|
||||
import time
|
||||
import hmac
|
||||
import hashlib
|
||||
import base64
|
||||
import json
|
||||
import argparse
|
||||
import sys
|
||||
import random
|
||||
|
||||
SECRET = b"reallyreallyreallyreallyverysafe"
|
||||
URL = "http://postgrest"
|
||||
JWT_DURATION = 120
|
||||
TOTAL_TARGETS = 50000 # tuned by hand to reduce result variance
|
||||
|
||||
|
||||
def base64url_encode(data: bytes) -> str:
|
||||
"""URL-safe Base64 encode without padding."""
|
||||
return base64.urlsafe_b64encode(data).rstrip(b"=").decode("ascii")
|
||||
|
||||
|
||||
def generate_jwt(exp_inc: int) -> str:
|
||||
"""Generate an HS256 JWT"""
|
||||
# Header & payload
|
||||
header = {"alg": "HS256", "typ": "JWT"}
|
||||
now = int(time.time())
|
||||
payload = {
|
||||
"sub": f"user_{random.getrandbits(32)}",
|
||||
"iat": now,
|
||||
"exp": now + exp_inc,
|
||||
"role": "postgrest_test_author",
|
||||
}
|
||||
|
||||
# Encode to JSON and then to Base64URL
|
||||
header_b = json.dumps(header, separators=(",", ":")).encode()
|
||||
payload_b = json.dumps(payload, separators=(",", ":")).encode()
|
||||
header_b64 = base64url_encode(header_b)
|
||||
payload_b64 = base64url_encode(payload_b)
|
||||
|
||||
# Sign (HMAC‑SHA256) the "<header>.<payload>" string
|
||||
signing_input = f"{header_b64}.{payload_b64}".encode()
|
||||
signature = hmac.new(SECRET, signing_input, hashlib.sha256).digest()
|
||||
signature_b64 = base64url_encode(signature)
|
||||
|
||||
return f"{header_b64}.{payload_b64}.{signature_b64}"
|
||||
|
||||
|
||||
# We want to ensure 401 Unauthorized responses don't happen during
|
||||
# JWT validation, this can happen when the jwt `exp` is too short.
|
||||
# At the same time, we want to ensure the `exp` is not too big,
|
||||
# so expires will occur and postgREST will have to clean cached expired JWTs.
|
||||
def estimate_adequate_jwt_exp_increase(iteration: int) -> int:
|
||||
# estimated time takes to build and run postgrest itself
|
||||
build_run_postgrest_time = 2
|
||||
# estimated time it takes to generate the targets file
|
||||
file_generation_time = TOTAL_TARGETS // (10**-5)
|
||||
# estimated exp time so some JWTs will expire
|
||||
dynamic_exp_inc = iteration // 1000
|
||||
|
||||
return build_run_postgrest_time + file_generation_time + dynamic_exp_inc
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate Vegeta targets with unique JWTs"
|
||||
)
|
||||
parser.add_argument(
|
||||
"output",
|
||||
help="Path to write the generated targets file",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
lines = []
|
||||
start_time = time.time()
|
||||
|
||||
for i in range(TOTAL_TARGETS):
|
||||
token = generate_jwt(estimate_adequate_jwt_exp_increase(i))
|
||||
lines.append(f"OPTIONS {URL}/authors_only")
|
||||
lines.append(f"Authorization: Bearer {token}")
|
||||
lines.append("") # blank line to separate requests
|
||||
|
||||
try:
|
||||
with open(args.output, "w") as f:
|
||||
f.write("\n".join(lines))
|
||||
except IOError as e:
|
||||
print(f"Error writing to {args.output}: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
print(f"Created {TOTAL_TARGETS} targets in {args.output} ({elapsed:.2f}s)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -41,6 +41,8 @@ let
|
||||
args = [
|
||||
"ARG_OPTIONAL_SINGLE([output], [o], [Filename to dump json output to], [./loadtest/result.bin])"
|
||||
"ARG_OPTIONAL_SINGLE([testdir], [t], [Directory to load tests and fixtures from], [./test/load])"
|
||||
"ARG_OPTIONAL_SINGLE([kind], [k], [Kind of loadtest (mixed: repeat mixed requests, jwt: run once over many requests with unique jwts)], [mixed])"
|
||||
"ARG_TYPE_GROUP_SET([KIND], [KIND], [kind], [mixed,jwt])"
|
||||
"ARG_LEFTOVERS([additional vegeta arguments])"
|
||||
];
|
||||
workingDir = "/";
|
||||
@@ -55,10 +57,26 @@ let
|
||||
export PGRST_DB_POOL="1"
|
||||
export PGRST_DB_TX_END="rollback-allow-override"
|
||||
export PGRST_LOG_LEVEL="crit"
|
||||
export PGRST_JWT_SECRET="reallyreallyreallyreallyverysafe"
|
||||
export PGRST_JWT_CACHE_MAX_LIFETIME="86400"
|
||||
|
||||
mkdir -p "$(dirname "$_arg_output")"
|
||||
abs_output="$(realpath "$_arg_output")"
|
||||
|
||||
case "$_arg_kind" in
|
||||
jwt)
|
||||
|
||||
${genTargets} "$_arg_testdir"/gen_targets.http
|
||||
|
||||
# shellcheck disable=SC2145
|
||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||
${withTools.withPgrst} \
|
||||
sh -c "cd \"$_arg_testdir\" && ${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||
${vegeta}/bin/vegeta report -type=text "$_arg_output"
|
||||
;;
|
||||
|
||||
*)
|
||||
|
||||
# shellcheck disable=SC2145
|
||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||
${withTools.withSlowPg} \
|
||||
@@ -66,6 +84,9 @@ let
|
||||
${withTools.withSlowPgrst} \
|
||||
sh -c "cd \"$_arg_testdir\" && ${runner} -targets targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||
${vegeta}/bin/vegeta report -type=text "$_arg_output"
|
||||
;;
|
||||
|
||||
esac
|
||||
'';
|
||||
|
||||
loadtestAgainst =
|
||||
@@ -83,6 +104,7 @@ let
|
||||
'';
|
||||
args = [
|
||||
"ARG_POSITIONAL_INF([target], [Commit-ish reference to compare with], 1)"
|
||||
"ARG_OPTIONAL_SINGLE([kind], [k], [Kind of loadtest], [mixed])"
|
||||
];
|
||||
positionalCompletion =
|
||||
''
|
||||
@@ -106,7 +128,7 @@ let
|
||||
# Save the results in the current working tree, too,
|
||||
# otherwise they'd be lost in the temporary working tree
|
||||
# created by withTools.withGit.
|
||||
${withTools.withGit} "$tgt" ${loadtest} --output "$PWD/loadtest/$tgt.bin" --testdir "$PWD/test/load"
|
||||
${withTools.withGit} "$tgt" ${loadtest} -k "$_arg_kind" --output "$PWD/loadtest/$tgt.bin" --testdir "$PWD/test/load"
|
||||
|
||||
cat << EOF
|
||||
|
||||
@@ -122,7 +144,7 @@ let
|
||||
|
||||
EOF
|
||||
|
||||
${loadtest} --output "$PWD/loadtest/head.bin" --testdir "$PWD/test/load"
|
||||
${loadtest} -k "$_arg_kind" --output "$PWD/loadtest/head.bin" --testdir "$PWD/test/load"
|
||||
|
||||
cat << EOF
|
||||
|
||||
@@ -178,6 +200,7 @@ let
|
||||
| ${toMarkdown}
|
||||
'';
|
||||
|
||||
genTargets = writers.writePython3 "postgrest-gen-loadtest-targets" { } (builtins.readFile ./generate_targets.py);
|
||||
in
|
||||
buildToolbox {
|
||||
name = "postgrest-loadtest";
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# The memory tests have large dependencies (a profiled build of PostgREST)
|
||||
# and are run less often than the spec tests, so we don't include them in
|
||||
# the default test environment. We make them available through a separate module.
|
||||
# TODO both of these require reentering the nix-shell if you make a change to the code
|
||||
{ buildToolbox
|
||||
, checkedShellScript
|
||||
, curl
|
||||
, postgrestProfiled
|
||||
, withTools
|
||||
}:
|
||||
let
|
||||
test =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-test-memory";
|
||||
docs = "Run the memory tests.";
|
||||
workingDir = "/";
|
||||
withPath = [ postgrestProfiled curl ];
|
||||
}
|
||||
''
|
||||
${withTools.withPg} -f test/spec/fixtures/load.sql test/memory/memory-tests.sh
|
||||
'';
|
||||
|
||||
runProfiled =
|
||||
checkedShellScript
|
||||
{
|
||||
name = "postgrest-profiled-run";
|
||||
docs = "Run a profiled build of postgREST. This will generate a postgrest.prof file that can be used to do optimization. Note: if you make a change to the code, you must reenter the nix-shell for an updated profiled build.";
|
||||
args =
|
||||
[
|
||||
"ARG_USE_ENV([PGRST_DB_ANON_ROLE], [postgrest_test_anonymous], [PostgREST anonymous role])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL], [1], [PostgREST pool size])"
|
||||
"ARG_USE_ENV([PGRST_DB_POOL_ACQUISITION_TIMEOUT], [1], [PostgREST pool timeout])"
|
||||
"ARG_LEFTOVERS([PostgREST arguments])"
|
||||
];
|
||||
workingDir = "/";
|
||||
withPath = [ postgrestProfiled ];
|
||||
}
|
||||
''
|
||||
export PGRST_DB_ANON_ROLE
|
||||
export PGRST_DB_POOL
|
||||
export PGRST_DB_POOL_ACQUISITION_TIMEOUT
|
||||
|
||||
postgrest +RTS -p -h -RTS "''${_arg_leftovers[@]}"
|
||||
'';
|
||||
in
|
||||
buildToolbox
|
||||
{
|
||||
name = "postgrest-memory";
|
||||
tools = { inherit test runProfiled; };
|
||||
}
|
||||