Cheatsheet Postgres
===
Références:
- https://gist.github.com/apolloclark/ea5466d5929e63043dcf#posgres-cheatsheet
- https://gist.github.com/Kartones/dd3ff5ec5ea238d4c546
- http://daminoux.fr/les-notions-de-base-de-postgresql/
- livre: http://shop.oreilly.com/product/9780596009762.do SQL cookbook O'Reilly
- the Art of Postgresql: https://theartofpostgresql.com/
- Postgresql tutorial: https://www.postgresqltutorial.com/
- Postgres exercises: https://pgexercises.com/questions/string/
- Free code camp YouTube: https://www.youtube.com/watch?v=qw--VYLpxG4
- Range: https://wiki.postgresql.org/images/7/73/Range-types-pgopen-2012.pdf
## Quick connect with Docker container
```sh
docker run --rm postgres:11 psql -A -h hostname -U postgres postgres -tc "SELECT json_agg(table) FROM table;" > out.json
```
## Serial et séquence
https://stackoverflow.com/a/27309311/2728710
Une colonne serial est un pseudo type.
C'est un entier avec des commandes DDL supplémentaires exécutées automatiquement.
The pseudo data type is resolved doing all of this:
- create a sequence named tablename_colname_seq
- create the column with type integer (or int2 / int8 respectively for smallserial / bigserial)
- make the column NOT NULL DEFAULT nextval('tablename_colname_seq')
- make the column own the sequence, so that it gets dropped with it automatically
Pour initialiser une colonne serial (utile après une migration qui a effacé toutes les données et recrée des données avec un id précalculé)
```sql
select pg_catalog.setval(pg_get_serial_sequence('askers', 'id'), (select max(id) from askers) + 1);
```
## Boostrap a project with Postgres
```shell
sudo -u postgres psql
postgres=# create database mydb;
postgres=# create user myuser with encrypted password 'mypass';
postgres=# grant all privileges on database mydb to myuser;
```
Ou, si on a un user maître avec le nom/mdp user/password :
```shell
psql postgres://user:password@localhost:5435
create user ozon with encrypted password 'ozon';
create database ozon with owner ozon encoding 'UTF8'; # Create a database (see LC_CTYPE and LC_COLLATE if needed)
alter user ozon with superuser; # if user need privileges to create extension, and so
```
Mémo :
- see owner of database via listing all databases `\l`
- see owner of table via `select * from pg_tables where tablename = 'my-table'`;
## Boostrap a project with Postgres with docker-compose
_docker-compose.yml_
```yml
version: "3"
services:
db:
restart: always
image: postgres:12.1
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 5s
timeout: 5s
retries: 10
environment:
# master password
- POSTGRES_USER=${POSTGRES_USER:-user}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
ports:
- 5454:5432
volumes:
- pg-art-pgdata:/var/lib/postgresql/data-pg-art
volumes:
pg-art-pgdata:
```
Run with: `docker-compose up --build -d`
Connect with: `psql postgres://user:password@localhost:5454`
```sql
create user pom with encrypted password 'pom';
```
*How to connect via VPN?*
`ssh -L 1111:127.0.0.1:5434 52.143.173.205`
*How to connect via database URL?*
`psql postgres://my-user:my-password@localhost:1111/`
It allows to connect as if it was local on port 1111.
**Mac**
Sous Mac, j'utilise Postgres.app.
Le CLI psql est accessible via `/Applications/Postgres.app/Contents/Versions/10/bin`. Mettre ce chemin dans le PATH de bash_profile.
`psql` permet de se connecter avec le user Linux courant sur le port 5432.
`psql -U medle -p 5454 myDatabase` permet de se connecter à la base de données myDatabase en tant que utilisteur medle avec le port 5454.
`GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO medle;`
`GRANT CONNECT ON DATABASE <db_name> TO <user_name>;` autorise la connexion à un utilisateur
`GRANT USAGE ON SCHEMA public TO <user_name>;` accorde la permission au schéma public
`GRANT SELECT, UPDATE, INSERT ON ALL TABLES IN SCHEMA public TO <user_name>;` accorde tous les droits sur toutes les tables à l'utilisateur
`GRANT SELECT, UPDATE, INSERT ON <table_name> TO <user_name>;` accorde tous les droits sur une table à l'utilisateur
`select current_user;` see the current user
`select current_database();` see the current database
`select version();` see the PostgreSQL version
`\?` see general help
`\h <Command>` see help for the command (like `\h ALTER TABLE`)
`\c` or `use medle` change database
`\l` see all databases
`\dt` see tables of the current database
`\du` see all users and their assigned roles
`\l` list all available databases
`\d <table>` describe a table (or `\d+ <table>`)
`\dn` list available schema
`\g` execute de last command
`\s` see the history of command
`\i filename` execute pgsql commands from a filename
`\timing` turn on query execution time (say it again to turn it off)
`\q` quit session
`ALTER ROLE <user_name> WITH PASSWORD '<password>';` modifie le mot de passe pour l'utilisateur
`medle=> \conninfo`
"You are connected to database "medle" as user "medle" via socket in "/tmp" at port "5432".`"
`alter table etablissement_sante owner to medle;` pour changer le propriétaire d'une table
`CREATE DATABASE mydb;` pour créer une base de données
`DROP DATABASE mydb;` pour suppprimer une base de données
`CREATE SCHEMA factbook;` créer un schéma
`SET search_path = my_schema, "$user", public;` modifier l'ordre de recherche des objets (tablest, etc.), seulement pour la session de l'utilisateur
`ALTER ROLE your_role SET search_path = my_schema, "$user", public;`idem, mais persistant pour le rôle
## SQL query avec date et interval
```sql
update conferences
set "expiresAt" = now() - interval '2 days'
where id = 'a4264225-9ada-4c5e-90ae-5f73de095c3c';
```
## Nom de colonne en Camel Case
```sql
select "expiresAt" from conferences;
```
## Ajouter le client psql
`sudo yum install postgresql-devel postgresql-libs`
## Supprimer tout un schéma
```
psql postgres://USER:PASSWORD@HOSTNAME/DATABASE -c " DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT USAGE ON SCHEMA public to PUBLIC; GRANT CREATE ON SCHEMA public to PUBLIC; COMMENT ON SCHEMA public IS 'standard public schema';"
```
https://www.shanestillwell.com/2018/04/04/delete-all-tables-in-postgres/
## Recherche plein texte et trigramme
https://www.miximum.fr/blog/la-recherche-plein-texte-avec-postgresql/
https://medium.com/@sukorenomw/when-to-use-postgresql-full-text-search-and-trigram-indexes-4c7c36223421
## Concept 1 serveur / n bases
1 serveur Postgres peut avoir n bases. C'est ce qu'on va utiliser pour créer plusieurs bases par PR ou branche Git.
L'utilisateur qui crée une base a un full accès sur celle-ci. Donc pas besoin d'utilisateur applicatif medle comme je faisais auparavant.
## Extension
Faire `create extension` pour ajouter une extension (en tant qu'admin).
## Chargement d'un dump Postgres
_Sur le nouveau PG managé sur Azure_
```bash
psql "postgresql://dev1%40medledevserver:~=*Bn6#2fCdCReZxc*4@medledevserver.postgres.database.azure.com:5432/dev1?sslmode=require" < backup_2020-02-18_14:51:24.sql
```
- attention: le mdp n'est pas bien parsé. Mettre un mdp sans caractère spéciaux
- faire un `pg_dump` avec `--no-owner` pour ne pas avoir de référence à l'utilisateur medle
host: medledevserver.postgres.database.azure.com
user: medleadmin@medledevserver
pass: sq;h9w[M5qwm1
user app: dev1@medledevserver
password: 01VihjO5xderUnTqmzcb
## Knex / pg + SSL
Problème d'URL Postgres en https. On a beau mettre une url en `?ssl=true` ou bien ajouter `ssl: true` dans `knexfile.js`, ce n'est pas pris en compte à cause d'un bug dans la librairie de parse de l'URL utilisé par Knex.
Pour corriger cela, il faut modifier la librairie pg dans le fichier _knexfile.js_.
```js
const pg = require("pg")
pg.defaults.ssl = true
```
## Timezone posix/Europe/Paris
Changer le knexfile.js avec un champ timezone qu'il comprend. En effet, `posix/Europe/Paris` ne fait pas partie de la liste des timezones que propose Postgre managé par Azure.
posix/Europe/Paris -> Europe/Paris.
## JSONB
À requêter de la forme:
```sql
with nb_locations as (
select id, coalesce(jsonb_array_length(extra_data->'location'), 0) as num
from acts) select id, num
from nb_locations where num = 1;
```
## Migrations sur PG managée Azure
- connexion à la prod: `ssh -L 1111:127.0.0.1:5434 factory@40.89.136.101`
- arrêt du front:
- `sudo docker container ls`
- `sudo docker stop medle_app_1`
- lancement du script de backup: `./scripts/backup.sh`
- chargement de la nouvelle base:
```shell
psql "postgresql://prod%40medleprodserver.postgres.database.azure.com:eqwQgEvZpNQP28zwap8W@medleprodserver.postgres.database.azure.com/prod?sslmode=require" < backup_2020-03-03_14:45:55.sql
```
Un warning: `ERROR: unrecognized configuration parameter "default_table_access_method"``
- modification du .env de prod:
```
# Common variables
NODE_ENV=production
APP_URL=https://medle.fabrique.social.gouv.fr
API_URL=https://medle.fabrique.social.gouv.fr/api
POSTGRES_USER=medle # à changer ?
POSTGRES_PASSWORD=R5J8Kzpu5s@K&Z # à changer?
# DB
DATABASE_URL=psql://dev1%40medledevserver:01VihjO5xderUnTqmzcb@medledevserver.postgres.database.azure.com:5432/medle
# JWT
JWT_SECRET=mw6msg$pu^$AFd
# Test variables
# TEST_CURRENT_DATE=10/09/2019
```
## Voir toutes les contraintes
```sql
SELECT con.*
FROM pg_catalog.pg_constraint con
INNER JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
INNER JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
WHERE nsp.nspname = 'public'
AND rel.relname = 'employments';
```
Changer le schéma et le nom de la table selon vos besoins.
## Backup de la dev et dump en local
_Octobre 2020_
Utilisation du serveur bastion pour se connecter en local sur la dev.
Rappel URL de connexion : `psql postgres://dev1%40medledevserver:01VihjO5xderUnTqmzcb@localhost:2345/dev1`
Attention :
`createdb: could not connect to database template1: could not connect to server: No such file or directory`
> Attention: la db de medle a le même port que celle de emjpm, donc bien arrêter l'une pour lancer l'autre__
```
# En local
# 1. dump de la base dev1 sur le PG managé de recette
pg_dump -Fc dev1 --host=localhost --port=2345 --username=dev1@medledevserver > dev1.dump
# Optionnel. Le superadmin donne le droit à l'utilisateur medle de créer des db
#psql postgres://medle:jJFWsfW5ePbN7J@localhost:5434
psql postgres://user:password@localhost:5434
# alter user medle createdb
alter user medle superuser # nécessaire aussi pour éviter les problèmes de droits manquants pour ajouter des extensions
# 2. Supprime si besoin la db dev1 par le user "user"
dropdb --host=localhost --port=5434 --username=user --echo dev1
# (archive). Suppression du user dev1, si besoin (ancien, maintenant c'est le user medle qui va avoir le ownership de dev1)
#dropuser --host=localhost --port=5434 --username=user --echo dev1
# (archive). Création du user dev1, par le user "user" (mettre mdp = password pour faire simple) (ancien)
#createuser --host=localhost --port=5434 --username=user --pwprompt --echo dev1
# 3. Créer la db dev1 par le user "user" et en ajouter le propriétaire medle
createdb --host=localhost --port=5434 --username=user --owner=medle --echo dev1 # marche avec medle, depuis qu'il a le droit de créer une db
# 4. Appliquer le dump dev1.dump sur la db dev1, par le user medle
pg_restore -d dev1 --host=localhost --port=5434 --username=medle --no-owner dev1.dump
=> Problème avec le user dev1 pour le pg_restore. Pas le droit de créer des extensions. C'est pour cela que medle est superuser aussi.
```