<!--
Les sources pour ces sujet sont :

ON_ERROR
* https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=9e2d87011
* https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=b725b7eec
* https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=40bbc8cf0
* https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=a6d0fa5ef

LOG_VERBOSITY
* https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=f5a227895

Skipped tuples
* https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=729439607

FORCE_NULL et FORCE_NOT_NULL
* https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=f6d4c9cf1

-->

<div class="slide-content">

  * Deux nouvelles options pour COPY
    + `ON_ERROR`
    + `LOG_VERBOSITY`
  * Deux options qui évoluent
    + `FORCE_NULL`
    + `FORCE_NOT_NULL`
  * Une nouvelle colonne pour `pg_stat_progress_copy`

</div>

<div class="notes">

L'un des gros reproches formulés par les utilisateurs de `COPY` est l'annulation
forcée de l'insertion des lignes valides à cause d'une ligne invalide. Certains
aimeraient pouvoir insérer les lignes valides et soit ignorer les lignes
invalides, soit les enregistrer dans une table d'erreur ou dans les fichiers de
trace. Ce manque a d'ailleurs donné lieu à l'écriture de différents outils,
comme pgloader.

La version 17 ajoute l'option `ON_ERROR` qui accepte deux valeurs. La valeur
`stop` correspond au comportement historique : la requête tombe en erreur et
aucune ligne, y compris les valides, n'est insérée. Comme il s'agit du
comportement historique, c'est la valeur par défaut quand l'option n'est pas
utilisée.

L'autre valeur est `ignore` et elle fait exactement cela : elle permet d'ignorer
les lignes invalides. Cependant, attention, cela concerne uniquement les erreurs
de conversion de la donnée du fichier vers le type de données de la colonne. De
ce fait, les erreurs sur les contraintes ne sont pas ignorées.

En voici un exemple complet :

```
postgres=# \! cat materiel.csv
1,tente
2,sac de couchage
1,duvet
4,oreiller

postgres=# CREATE TABLE materiel (id integer, nom varchar(10));
CREATE TABLE

postgres=# COPY materiel FROM '/home/guillaume/materiel.csv' WITH (FORMAT csv);
ERROR:  value too long for type character varying(10)
CONTEXT:  COPY materiel, line 2, column nom: "sac de couchage"

postgres=# TABLE materiel;
 id | nom
----+-----
(0 rows)
```

La deuxième colonne de la deuxième ligne du fichier CSV contient un texte de
plus de dix caractères. Une colonne `varchar(10)` ne peut pas l'accueillir.
La requête est donc en erreur, aucune ligne n'est insérée.

```sql
postgres=# COPY materiel FROM '/home/guillaume/materiel.csv' WITH (FORMAT csv, ON_ERROR ignore);
NOTICE:  1 row was skipped due to data type incompatibility
COPY 3
```

```
postgres=# TABLE materiel;
 id |   nom
----+----------
  1 | tente
  1 | duvet
  4 | oreiller
(3 rows)
```

En ajoutant l'option `ON_ERROR ignore`, la ligne deux est ignorée. Un message de
niveau `NOTICE` apparaît et la requête est considérée comme réussie, toutes les
autres lignes sont insérées.

```
postgres=# TRUNCATE materiel;
TRUNCATE TABLE

postgres=# ALTER TABLE materiel ADD PRIMARY KEY (id);
ALTER TABLE

postgres=# COPY materiel FROM '/home/guillaume/materiel.csv' WITH (FORMAT csv, ON_ERROR ignore);
ERROR:  duplicate key value violates unique constraint "materiel_pkey"
DETAIL:  Key (id)=(1) already exists.
CONTEXT:  COPY materiel, line 3
```

Dans le cas d'une violation de contrainte, y compris avec l'option `ON_ERROR
ignore`, les lignes valides ne seront pas insérées et la requête est en erreur.

Pour que la fonctionnalité soit complète, il faudrait pouvoir enregistrer les
lignes invalides dans une table ou un fichier. Les valeurs `log` et `table` ont
été discutées, gageons que cela arrivera dans une prochaine version.

Le message de niveau `NOTICE` qui apparaît pour chaque ligne invalide indique
uniquement le nombre de lignes invalides. Pour avoir plus de détails, il faut
utiliser la nouvelle option `LOG_VERBOSITY`. Cette dernière peut avoir deux
valeurs : `default` ou `verbose`. Si nous reprenons l'exemple précédent :

```sql
postgres=# ALTER TABLE materiel DROP CONSTRAINT materiel_pkey;
ALTER TABLE
postgres=# COPY materiel FROM '/home/guillaume/materiel.csv' WITH (FORMAT csv, ON_ERROR ignore);
NOTICE:  1 row was skipped due to data type incompatibility
COPY 3
postgres=# COPY materiel FROM '/home/guillaume/materiel.csv' WITH (FORMAT csv, ON_ERROR ignore, LOG_VERBOSITY default);
NOTICE:  1 row was skipped due to data type incompatibility
COPY 3
postgres=# COPY materiel FROM '/home/guillaume/materiel.csv' WITH (FORMAT csv, ON_ERROR ignore, LOG_VERBOSITY verbose);
NOTICE:  skipping row due to data type incompatibility at line 2 for column nom: "sac de couchage"
NOTICE:  1 row was skipped due to data type incompatibility
COPY 3
```

Attention, ce message est affiché pour chaque ligne invalide, ce qui peut
devenir très verbeux si le fichier en entrée est d'une qualité pauvre.

Enfin, toujours dans la lignée de la nouvelle option `ON_ERROR`, il est
possible de suivre le nombre de lignes ignorées lors de l'exécution de la
requête `COPY FROM` avec la nouvelle colonne `tuples_skipped` de la vue
`pg_stat_progress_copy`.

Quant aux deux options `FORCE_NULL` et `FORCE_NOT_NULL`, elles n'acceptaient
qu'une liste de colonnes. Il est maintenant possible d'utiliser `*` à la place
de la liste de toutes les colonnes.

</div>
