code. grind. sleep.

lundi 30 janvier 2012

Got GUC?

Les paramètres de configuration de PostgreSQL sont appelés GUC ce qui signifie Grand Unified Configuration, c'est le nom de la partie du code qui gère les paramètres de configuration. En gros, ce sont tous les paramètres du fichier postgresql.conf.

Ce qui est moins connu et utilisé, c'est la possibilité de configurer ces paramètres à différents niveaux :

  1. Fichier postgresql.conf
  2. Ligne de commande du postmaster, le processus principal du serveur
  3. Base de données
  4. Rôle
  5. Session
  6. Transaction

La précédence des valeurs va en descendant dans la liste, par exemple la valeur d'un paramètre au niveau d'un rôle écrase celle positionnée au niveau de la base de donnée ou la ligne de commande. Ce comportement est très intéressant pour définir une valeur d'un paramètre dépendante du contexte d'exécution d'un traitement. Par exemple on peut placer un timeout des requêtes au niveau de la base pour éviter qu'une application ne jette l'éponge avant PostgreSQL, et configurer l'absence de timeout pour un rôle dédié aux opérations de VACUUM et ANALYSE, on limite ainsi l'effet de bord du timeout :

-- timeout à 30 secondes sur la base de données
ALTER DATABASE mabase SET statement_timeout TO 30000;

-- pas de timeout pour le role maintenance chargé du vacuum
ALTER ROLE maintenance SET statement_timeout TO 0;

Selon l'endroit où doit être positionné la valeur on utilise :

  • postgresql.conf : directement dans le fichier
  • ligne de commande : dans le script d'init avec l'option -c et à l'exécution de pg_ctl avec l'option -o
  • base de données : ALTER DATABASE nom_base SET param TO valeur;
  • rôle : ALTER ROLE nom_role SET param TO valeur;
  • session : SET SESSION param TO valeur;
  • transaction : SET LOCAL param TO valeur;

Pour le passage des valeurs au niveau SQL, on peut utiliser ... RESET param à la place de SET param TO pour réinitialiser la valeur à son défaut pour le contexte choisi.

On peut également définir des paramètres personnalisés, comme le font certaines extensions. Pour cela il faut définir une classe de variables personnalisée, en déclarant un préfixe (on en sépare plusieurs par des virgules) dans le paramètre de configuration custom_variable_classes :

custom_variable_classes = 'nico'

Ensuite, on peut directement ajouter nos variables personnalisées en les préfixant par nico. :

nico.test_guc = 1000

On peut alors manipuler ces variables comme ceci :

mydb=# SHOW nico.test_guc;
 nico.test_guc 
---------------
 1000
(1 row)

mydb=# SHOW nico.test_guc;
 nico.test_guc 
---------------
 1000
(1 row)

mydb=# SET nico.test_guc = 3;
SET
mydb=# SHOW nico.test_guc;
 nico.test_guc 
---------------
 3
(1 row)

mydb=# SET nico.reguc = on;
SET
mydb=# SHOW nico.reguc;
 nico.reguc 
------------
 on
(1 row)

Enfin, on peut utiliser les fonctions current_setting() et set_config() pour manipuler ces variables dans des fonctions :

mydb=# SELECT set_config('nico.test_guc', '100', false);
 set_config 
------------
 100
(1 row)

mydb=# SELECT current_setting('nico.test_guc');
 current_setting 
-----------------
 100
(1 row)

PS: merci à ce post pour l'idée de creuser le sujet.

mardi 18 octobre 2011

Quand PostgreSQL n'a plus d'espace disque à manger

Voilà donc une question intéressante, comment se comporte PostgreSQL face à un système de fichier plein ? Un peu d'expérimentation est nécessaire pour se rassurer...

On crée deux systèmes de fichiers de faible taille pour les tests. Le premier stockera PGDATA, ainsi qu'une base de données nommée db_data. Le second sera le tablesapce ts1, dans lequel on créera une base de données db_ts1.

L'objectif est de montrer que seules les transactions modifiant des objets stockés sur des systèmes de fichier plein sont affectées, c'est pourquoi on a besoin de plusieurs tablespaces

Les binaires se trouvent dans /home/pgsql/postgresql-9.0.4, PGDATA dans /home/pgsql/postgresql-9.0.4/data, et le tablespace dans /home/pgsql/postgresql-9.0.4/ts1. On a donc monté et donné la propriété des deux filesystèmes à orgrim, que fera tourner PostgreSQL :

# df -h
...
/dev/mapper/sys-pg1   504M   54M  425M  12% /home/pgsql/postgresql-9.0.4/data
/dev/mapper/sys-pg2   504M   17M  462M   4% /home/pgsql/postgresql-9.0.4/ts1
...

# cd /home/pgsql/postgresql-9.0.4/
# chown orgrim: data ts1
# chmod 700 data ts1

Le cluster est préparé avec l'environnement suivant :

$ env | grep PG
PGPORT=5904
PGDATABASE=postgres
PGDATA=/home/pgsql/postgresql-9.0.4/data
PATH=/home/pgsql/postgresql-9.0.4/bin:$PATH

On lance donc initdb, puis on crée les bases avec le tablespace :

$ initdb
$ psql
psql (9.0.4)
Type "help" for help.

postgres=# CREATE DATABASE db_data;
CREATE DATABASE
postgres=# CREATE TABLESPACE ts1 LOCATION '/home/pgsql/postgresql-9.0.4/ts1';
CREATE TABLESPACE
postgres=# CREATE DATABASE db_ts1 TABLESPACE ts1;
CREATE DATABASE

Ensuite, on se connecte à la base de données db_ts1 et on y crée une base qui permettra de remplir le tablespace ts1 :

$ while ((1)); do psql -c "INSERT INTO t SELECT generate_series(1, 1000) AS i;" db_ts1; done

Au bout, d'un moment le système de fichier du tablespace est plein et tout requête générant des écritures sort en erreur avec un message de cette forme :

ERROR:  could not extend file "pg_tblspc/16385/PG_9.0_201008051/16386/16390": wrote only 4096 of 8192 bytes at block 58438
HINT:  Check free disk space.

Ensuite, on essaye avec le répertoire PGDATA, on crée donc une table dans la base db_data :

$ psql db_data
psql (9.0.4)
Type "help" for help.

db_data=# CREATE TABLE t (i int);
CREATE TABLE

De la même façon, on remplit cette table jusqu'à épuisement de l'espace libre :

$ while ((1)); do psql -c "INSERT INTO t SELECT generate_series(1, 1000) AS i;" db_data; done

Résultat, les requêtes sortent en erreur de la même façon. On a peut-être de la chance ici, le filesystem contenant pg_xlog, les problèmes pourraient être pis.

Il est également possible de remplir le système de fichiers de journaux de transactions, ce qui est problématique du fait que chaque transaction est écrite ici tout tablespace confondu. On vide donc les deux bases :

$ psql db_ts1
psql (9.0.4)
Type "help" for help.

db_ts1=# TRUNCATE t;
TRUNCATE TABLE

$ psql db_data
psql (9.0.4)
Type "help" for help.

db_data=# TRUNCATE t;
TRUNCATE TABLE

Et on place le paramètre checkpoint_segments à 300, valeur démesurée par rapport à la place disponible dans PGDATA.

Après un reload, on refait alors le test de remplissage de la base db_ts1, qui assure que les journaux de transactions seuls remplissent PGDATA.

Le serveur PostgreSQL s'arrête parce qu'il ne peut écrire le journal de transaction :

PANIC:  could not write to file "pg_xlog/xlogtemp.8795": No space left on device
STATEMENT:  INSERT INTO t SELECT generate_series(1, 1000) AS i;
LOG:  server process (PID 8795) was terminated by signal 6: Aborted
LOG:  terminating any other active server processes
WARNING:  terminating connection because of crash of another server process
DETAIL:  The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory.
HINT:  In a moment you should be able to reconnect to the database and repeat your command.
LOG:  all server processes terminated; reinitializing

Il redémarre illico, mais le problème persiste :

FATAL:  the database system is in recovery mode
LOG:  database system was interrupted; last known up at 2011-10-12 00:01:40 CEST
LOG:  database system was not properly shut down; automatic recovery in progress
LOG:  consistent recovery state reached at 0/5FDB7480
LOG:  redo starts at 0/5AA57D68
LOG:  could not open file "pg_xlog/000000010000000000000074" (log file 0, segment 116): No such file or directory
LOG:  redo done at 0/73FFFF90
LOG:  last completed transaction was at log time 2011-10-12 00:05:11.710639+02
PANIC:  could not write to file "pg_xlog/xlogtemp.8797": No space left on device
LOG:  startup process (PID 8797) was terminated by signal 6: Aborted
LOG:  aborting startup due to startup process failure

Le seul moyen de se sortir de cette situation est d'utiliser les 5% d'espace libre du filesystème réservés au super utilisateur, de baisser la valeur de checkpoint_segments à une valeur évitant le filesystem full, les checkpoints successif supprimeront les fichiers en trop.

Lorsqu'on a plus besoin de 5% réservés, il ne faut pas oublier de remettre la réservation.

Enfin, ce cas le plus critique peut arriver assez facilement lorsqu'on a de l'archivage, si le serveur ne peut plus archiver les fichiers WAL, alors il les conserve, un filesystem full sur un espace d'archivage peut donc être transmis au serveur...

vendredi 5 août 2011

Could not open relation with oid N

On peut parfois trouver cet étrange message d'erreur dans les traces de PostgreSQL (N étant un nombre) ou lors de l'exécution d'une requête :

ERROR:  could not open relation with OID N

Si on recherche ce message dans les mailing-lists du projet, on peut facilement conclure que la base de données est corrompue, qu'il y a des problèmes matériels et que la sécurité des données est en péril. Et bien, ce n'est pas forcément le cas : obtenir ce message peut être tout à fait normal.

Pour démontrer cela, on a besoin d'une table :

$ createdb test
$ psql test
psql (9.0.4)
Type "help" for help.

test=# CREATE TABLE truc AS SELECT generate_series(0, 5) AS i;
SELECT 6
test=#

On lance une session qui bloquerait un DROP de cette table, pour cela on pose un verrou exclusif, le mode « ExclusiveLock » ne laisse passer que les lectures (c'est important pour la suite) :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# BEGIN;
BEGIN
test=# LOCK TABLE truc IN EXCLUSIVE MODE;
LOCK TABLE
test=#

On laisse cette transaction « ouverte », avec le verrou posé et on lance une session pour supprimer la table :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# BEGIN;
BEGIN
test=# DROP TABLE truc;

L'ordre SQL DROP TABLE ne rend pas la main, cette deuxième session attend le verrou « AccessExclusiveLock », qui est le plus restrictif, sur la table pour pouvoir la supprimer. La page http://wiki.postgresql.org/wiki/Loc... fournie une requête montrant les dépendances entre requêtes du point de vue du verrouillage. Dans ce cas, elle donne le résultat suivant :

 waiting_locktype | waiting_table |  waiting_query   |    waiting_mode     | waiting_pid | other_locktype | other_table |      other_query      |  other_mode   | other_pid | other_granted 
------------------+---------------+------------------+---------------------+-------------+----------------+-------------+-----------------------+---------------+-----------+---------------
 relation         | truc          | DROP TABLE truc; | AccessExclusiveLock |       25632 | relation       | truc        | <IDLE> in transaction | ExclusiveLock |     24217 | t

On lance une troisième session, avec un SELECT sur notre table :

$ psql test
psql (9.0.4)
Type "help" for help.

test=# SELECT * FROM truc;

L'ordre SELECT ne rend pas la main, cette troisième session se met à attendre le DROP TABLE et la première session. C'est d'ailleurs le DROP TABLE qui bloque réellement le SELECT, car la première session à verrouillé la table en lecture seule :

 waiting_locktype | waiting_table |    waiting_query    |    waiting_mode     | waiting_pid | other_locktype | other_table |      other_query      |     other_mode      | other_pid | other_granted 
------------------+---------------+---------------------+---------------------+-------------+----------------+-------------+-----------------------+---------------------+-----------+---------------
 relation         | truc          | SELECT * FROM truc; | AccessShareLock     |       28629 | relation       | truc        | DROP TABLE truc;      | AccessExclusiveLock |     25632 | f
 relation         | truc          | DROP TABLE truc;    | AccessExclusiveLock |       25632 | relation       | truc        | <IDLE> in transaction | ExclusiveLock       |     24217 | t
 relation         | truc          | SELECT * FROM truc; | AccessShareLock     |       28629 | relation       | truc        | <IDLE> in transaction | ExclusiveLock       |     24217 | t
 relation         | truc          | DROP TABLE truc;    | AccessExclusiveLock |       25632 | relation       | truc        | SELECT * FROM truc;   | AccessShareLock     |     28629 | f
(4 rows)

On libère la première session :

test=# ROLLBACK;
ROLLBACK
test=#

Le DROP TABLE passe, et le SELECT continue d'attendre :

 waiting_locktype | waiting_table |    waiting_query    |  waiting_mode   | waiting_pid | other_locktype | other_table |      other_query      |     other_mode      | other_pid | other_granted 
------------------+---------------+---------------------+-----------------+-------------+----------------+-------------+-----------------------+---------------------+-----------+---------------
 relation         | truc          | SELECT * FROM truc; | AccessShareLock |       28629 | relation       | truc        | <IDLE> in transaction | AccessExclusiveLock |     25632 | t

On voit que le SELECT attend la transaction qui a lancé le DROP TABLE. Même si le DROP TABLE est terminé, son effet ne sera connu des transactions concurrentes seulement ou moment du commit ou rollback, parce qu'on utilise le niveau d'isolation des transaction « read committed » (par défaut). Il n'y a pas de « UNLOCK » sur les tables dans PostgreSQL, il faut attendre la fin de la transaction pour que les verrous soient libérés, du moins lorsqu'on n'utilise pas de savepoints.

Maintenant, on valide le DROP TABLE, avec l'ordre COMMIT. Le SELECT termine en erreur, on voit alors le message dans les logs :

ERROR:  could not open relation with OID 17366 at character 15
STATEMENT:  SELECT * FROM truc;

Lorsque le SELECT n'est plus bloqué par le verrou, il ne peut accéder pas à la table car elle n'existe plus. Le message n'est pas très explicite parce que la requête est en cours d'exécution : le moteur a déjà terminé le travail de parsing et de planification, il ne travaille qu'avec les OID qu'il a récupéré du catalogue système à ce moment là.

Dans ce cas précis, obtenir ce message n'est un problème de corruption de la base ou du catalogue système.

Deux machines NetBSD dans la buildfarm de PostgreSQL

Il y a quelques temps, j'avais remarqué que seules deux machines NetBSD étaient présentes dans la buildfarm de PostgreSQL sur du powerpc et du mips. Pas de machines i386 et amd64, qui sont pourtant les architectures phares du TIER 1, et ce depuis plus d'un an.

L'affront est désormais réparé et deux nouveaux drapeaux oranges flottent fièrement dans les prés de la buildfarm, fournies par votre serviteur :

  • panther : NetBSD 5.1 gcc 4.1.3 i386
  • smilodon : NetBSD 5.1 gcc 4.1.3 amd64

Les machines tournent maintenant depuis quelques temps, sans aucun soucis.

jeudi 4 août 2011

pitrery : des scripts pour faciliter la sauvegarde PITR

Gérer une sauvegarde PITR (Point-In-Time Recovery), permettant de revenir à un point précis dans le temps sur un serveur PostgreSQL, n'est pas forcément évident à mettre en place et ensuite à gérer au quotidien : la restauration en cas de problème peut même prendre beaucoup de temps à mettre en place, avec des possibilités d'erreurs non négligeables. C'est d'autant plus stressant que la pression monte quand on en a besoin...

C'est pourquoi, dans le cadre de mon travail, j'ai écrit un ensemble de scripts shell en Bash (ce qui permet d'utiliser des commandes qui sont déjà sur une installation classique de Linux) pour faciliter ce travail. Le code est hébergé sur github : https://github.com/dalibo/pitrery. La version 1.0 vient d'être taguée. C'est encore un peu brut de décoffrage niveau documentation, mais les scripts ont été testés dans tous les sens, donc ce jeu d'outils devrait être utilisable en production sans trop de difficultés.

Il y a un Makefile GNU, à l'ancienne, les fichiers de configuration sont plein de commentaires pour expliquer et il y des « usage » pour chaque script à base de -? ou -h qui devraient parler à ceux qui savent déjà gérer du PITR.

jeudi 7 avril 2011

Retrouver un trigger à partir du nom de la fonction associée

Ici encore, tout est dans le catalogue système de PostgreSQL, il suffit de regarder dans la table pg_trigger.

Avec les jointures qui vont bien, on peut obtenir les informations intéressantes :

SELECT nspname AS schema, relname AS TABLE, tgname AS TRIGGER, proname AS FUNCTION
FROM pg_trigger t
  JOIN pg_class c ON (t.tgrelid = c.oid)
  JOIN pg_namespace n ON (n.oid = c.relnamespace)
  JOIN pg_proc p ON (p.oid = t.tgfoid);

En ajoutant une clause WHERE, on peut facilement retrouver la table associée au trigger :

SELECT nspname AS schema, relname AS TABLE, tgname AS TRIGGER, proname AS FUNCTION
FROM pg_trigger t
  JOIN pg_class c ON (t.tgrelid = c.oid)
  JOIN pg_namespace n ON (n.oid = c.relnamespace)
  JOIN pg_proc p ON (p.oid = t.tgfoid)
WHERE proname = 'mafonction';

mardi 15 mars 2011

Voir tous les champs d'une table

Avec PostgreSQL, on peut utiliser la requête suivante pour obtenir la taille de chacune des bases de données d'un cluster :

SELECT datname AS base,
  pg_size_pretty(pg_database_size(oid)) AS taille
FROM pg_database;

Qui donne par exemple :

         base          | taille  
-----------------------+---------
 template0             | 5273 kB
 postgres              | 5385 kB
 redmine               | 9057 kB
 roundcube             | 9153 kB
 template1             | 5369 kB
 dotclear              | 6833 kB
 dspam                 | 204 MB
 exim                  | 5649 kB
 (8 rows)

Mais comment est-ce possible ? on a appliqué la fonction pg_database_size() sur une colonne appelée oid, alors que la définition de pg_database est la suivante :

postgres=# \d pg_database
    Table "pg_catalog.pg_database"
    Column     |   Type    | Modifiers 
---------------+-----------+-----------
 datname       | name      | not null
 datdba        | oid       | not null
 encoding      | integer   | not null
 datcollate    | name      | not null
 datctype      | name      | not null
 datistemplate | boolean   | not null
 datallowconn  | boolean   | not null
 datconnlimit  | integer   | not null
 datlastsysoid | oid       | not null
 datfrozenxid  | xid       | not null
 dattablespace | oid       | not null
 datacl        | aclitem[] | 
Indexes:
    "pg_database_datname_index" UNIQUE, btree (datname), tablespace "pg_global"
    "pg_database_oid_index" UNIQUE, btree (oid), tablespace "pg_global"
Tablespace: "pg_global"

Il n'y a pas de colonne oid dans cette table ! Et pourtant... Cette colonne est tout simplement cachée, mais elle existe bel et bien. Pour vérifier, on recherche donc dans le catalogue système la liste des champs de la table :

SELECT attname, typname, attnum
FROM pg_attribute a
  JOIN pg_class c ON (c.oid = a.attrelid)
  JOIN pg_type t ON (t.oid = a.atttypid)
WHERE relname = 'pg_database';

Qui donne :

    attname    | typname  | attnum 
---------------+----------+--------
 tableoid      | oid      |     -7
 cmax          | cid      |     -6
 xmax          | xid      |     -5
 cmin          | cid      |     -4
 xmin          | xid      |     -3
 oid           | oid      |     -2
 ctid          | tid      |     -1
 datname       | name     |      1
 datdba        | oid      |      2
 encoding      | int4     |      3
 datcollate    | name     |      4
 datctype      | name     |      5
 datistemplate | bool     |      6
 datallowconn  | bool     |      7
 datconnlimit  | int4     |      8
 datlastsysoid | oid      |      9
 datfrozenxid  | xid      |     10
 dattablespace | oid      |     11
 datacl        | _aclitem |     12
(19 rows)

La colonne attnum de pg_attribute indique la position de la colonne, on remarque ainsi qu'il y a sept colonnes avec une position négative. Ces colonnes ne sont pas montrés par la commande \d de psql, ce sont les colonnes système de la table (plus d'information sur ce qu'elles représentent dans la doc)

En plus, on a encore utilisé cette colonne oid pour la retrouver dans la table... Maintenant, on peut être rassuré sur le catalogue système, ce n'est qu'un ensemble de tables, auxquelles on accède en SQL standard...

pg_class.reltuples n'est qu'une approximation du nombre de lignes

Dans le catalogue de PostgreSQL, qui donne plein d'information sur le cluster et la base de données, la table pg_class regroupe les informations sur les tables, les séquences, les index, tout ce qui contient des colonnes, appelé relation. L'un des champs disponibles est reltuples, il contient le nombre approximatif de lignes dans une relation. C'est approximatif, ainsi ce n'est pas un donnée sure... La preuve par l'exemple.

On crée un table avec une colonne de type entier :

db=# CREATE TABLE truc (i int);
CREATE TABLE

On retrouve ensuite notre table ainsi que la valeur de reltuples dans le catalogue système :

db=# SELECT relname, reltuples FROM pg_class WHERE relname = 'truc';
 relname | reltuples 
---------+-----------
 truc    |         0
(1 row)

La table est vide, et reltuples est à zéro. On insère ensuite quelques lignes dans cette table :

db=# INSERT INTO dalibo.truc VALUES (1),(2),(3),(4);
INSERT 0 4

On regarde la valeur de reltuples pour la table après cette insertion :

db=# SELECT relname, reltuples FROM pg_class WHERE relname = 'truc';
 relname | reltuples 
---------+-----------
 truc    |         0
(1 row)

reltuples n'est pas à jour, il vaut toujours zéro alors que la table contient 4 lignes !

Comme indiqué dans la documentation, reltuples est mis à jour lors d'un ANALYSE ou d'autres opérations de type DDL :

db=# ANALYZE truc;
ANALYZE
db=# SELECT relname,reltuples FROM pg_class WHERE relname = 'truc';
 relname | reltuples 
---------+-----------
 truc    |         4
(1 row)

Et voilà, le passage du ANALYSE à permis de mettre à jour la valeur de reltuples pour la table.

Conclusion, si on désire supprimer une table parce qu'elle est vide, le moyen sûr est de compter le nombre de lignes dans la table et non pas se baser sur la valeur de reltuples.

vendredi 25 février 2011

libedit vs. libreadline dans les packages PostgreSQL de Debian

Pour éditer sa ligne de commande dans psql, on peut soit compiler Postgres avec le support de readline, soit libedit. Pour Debian, les questions de licences sont très importantes, et ils pensent qu'il y a une incompatibilité entre la licence de libreadline (GPLv3) et celle de Postgres (BSD), c'est pourquoi ils ont décidé de compiler Postres avec la libedit. Malheureusement, la version de la libedit est vieille dans Debian et surtout limitée : on ne peut taper que des caractères ASCII, ce qui est une belle regression du point de vu du l'utilisation.

Il y avait deux solutions :

  • Ne taper que des caractères ASCII
  • Recompiler les packages Postgres avec le support de readline, c'est ce que j'ai fait.

Un workaround a été trouvé récemment, en ayant la libreadline installée, on peut s'arranger pour l'utiliser grâce à un LD_PRELOAD bien placé. C'est ce que fait pg_wrapper (le truc qui permet d'avoir plusieurs versions de Postgres en même temps sous Debian) depuis la version 114 des packages postgresql-common et postgresql-client-common.

De plus, la priorité a été mise plus haute sur cette mise à jour, qui corrige aussi d'autres problèmes, et cette version est déjà dans testing.

Enfin, j'ai refais les backports que je maintiens pour fournir diverses versions de Postgres sur Debian, non disponibles dans les dépôts officiels : c'est sur le dépôt http://debian.dalibo.org.

mardi 22 février 2011

Recréer une séquence

On peut faire ça dans 2 cas :

  • On a fait n'importe quoi, et la séquence a disparu :-(
  • On veut transformer une colonne en « SERIAL »
SET ROLE owner_de_la_table;
SELECT max(id)+1 FROM latable;
--  ?column? 
-- ----------
--       155
-- (1 row)
-- On prend donc « max(id)+1 » comme valeur de départ de la séquence
BEGIN;
CREATE SEQUENCE latable_id_seq START 155 OWNED BY latable.id;
COMMIT;

Pour lier la séquence à la table (et ainsi l'utiliser lors d'INSERT par exemple) :

BEGIN;
ALTER TABLE schema.latable ALTER COLUMN id SET DEFAULT NEXTVAL('latable_id_seq'::regclass);
COMMIT;