gleu's blog

Aller au contenu | Aller au menu | Aller à la recherche

« Gomorra »

Une histoire de mafia. Pas celles des chefs dans leur palace mais celle des rues, celle des petites mains qui s'entretuent, se déchirent, travaillent, suent sang et eau pour gagner peu et vivre autant. Autant dire que c'est gore. Mais c'est extrêmement bien fait. C'est simplement réaliste.

À montrer aux gamins qui veulent faire partie de la mafia.

Développons une nouvelle fonctionnalité pour PostgreSQL (6/?)

Mise à jour : plusieurs corrections de Thomas, Merci !

Désolé pour l'interruption des programmes, j'ai été plutôt occupé ces derniers temps.

Avant tout, n'oubliez pas de récupérer la mise à jour des sources et à compiler le tout.

On en était à vouloir réellement implémenter le déplacement des fichiers. Malheureusement, c'est peut-être encore un peu trop tôt pour cela. Avant de déplacer les fichiers, il faut d'abord savoir quels fichiers déplacer. Et avant cela, il faut aussi savoir si on a le droit de le faire.

Commençons par les droits. Il existe une autre fonction qui touche à tous les fichiers : celle de suppression d'une base de données. La fonction se trouve là-aussi dans le fichier backend/commands/dbcommands.c, elle s'appelle dropdb. Le mieux est de la dupliquer et de renommer la nouvelle en movedb. L'argument de cette nouvelle fonction est le nom de la base. Il faut aussi supprimer certains des tests inutiles dans notre cas : celui sur les bases de données modèles, celui qui empêche la suppression de la base de données où l'utilisateur est connecté. D'autres appels de fonctions sont aussi à supprimer car ils n'ont rien à voir avec notre fonction. Voici le résultat de l'élagage :

 /*
  * ALTER DATABASE SET TABLESPACE
  */
 void
 movedb(const char *dbname)
 {
 	Oid			db_id;
 	bool		db_istemplate;
 	Relation	pgdbrel;
 	int			notherbackends;
 	int			npreparedxacts;
 
 	/*
 	 * Look up the target database's OID, and get exclusive lock on it. We
 	 * need this to ensure that no new backend starts up in the target
 	 * database while we are moving it, and that no one is
 	 * using it as a CREATE DATABASE template or trying to delete it for
 	 * themselves.
 	 */
 	pgdbrel = heap_open(DatabaseRelationId, RowExclusiveLock);
 
 	if (!get_db_info(dbname, AccessExclusiveLock, &db_id, NULL, NULL,
 					 &db_istemplate, NULL, NULL, NULL, NULL, NULL, NULL))
 	{
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_DATABASE),
 				 errmsg("database \"%s\" does not exist", dbname)));
 	}
 
 	/*
 	 * Permission checks
 	 */
 	if (!pg_database_ownercheck(db_id, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
 					   dbname);
 
       /* Obviously can't move the tables of another database than my own */
       if (db_id != MyDatabaseId)
           ereport(ERROR,
                  (errcode(ERRCODE_OBJECT_IN_USE),
                    errmsg("cannot move the relations of another database than the currently open one")));  
 	/*
 	 * Check for other backends in the target database.  (Because we hold the
 	 * database lock, no new ones can start after this.)
 	 *
 	 * As in CREATE DATABASE, check this after other error conditions.
 	 */
 	CountOtherDBBackends(db_id, &notherbackends, &npreparedxacts);
 	if (notherbackends > 1 || npreparedxacts > 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_IN_USE),
 				 errmsg("database \"%s\" is being accessed by other users",
 						dbname),
 				 errdetail_busy_db(notherbackends, npreparedxacts)));
 
 	/*
 	 * Force a checkpoint to make sure the bgwriter has received the message
 	 * sent by ForgetDatabaseFsyncRequests. On Windows, this also ensures that
 	 * the bgwriter doesn't hold any open files, which would cause rmdir() to
 	 * fail.
 	 */
 	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
 
 	/*
 	 * Close pg_database, but keep lock till commit (this is important to
 	 * prevent any risk of deadlock failure while updating flat file)
 	 */
 	heap_close(pgdbrel, NoLock);
 
 	/*
 	 * Set flag to update flat database file at commit.  Note: this also
 	 * forces synchronous commit, which minimizes the window between removal
 	 * of the database files and commital of the transaction. If we crash
 	 * before committing, we'll have a DB that's gone on disk but still there
 	 * according to pg_database, which is not good.
 	 */
 	database_file_update_needed();
 }

Nous devons déclarer cette fonction dans l'en-tête. Ce fichier se trouve dans le répertoire src/include/commands et se nomme dbcommands.h. Il faut ajouter cette ligne de déclaration :

 extern void movedb(const char *dbname);

Maintenant, il faut appeler cette fonction. Pour cela, retrouvons le message qu'on envoyait dans le log (à savoir, « set tablespace ! »). Il suffit d'ajouter l'appel à la fonction. J'en profite aussi pour changer le message et obtenir quelque chose de plus intéressant, ce qui me donne le résultat suivant :

 else if (strcmp(defel->defname, "tablespace") == 0)
 {
   elog(NOTICE,
     "alter tablespace %s set tablespace %s!", stmt->dbname, strVal(defel->arg));
   movedb(stmt->dbname);
 }

Après toute ces modifications, compilons pour tester le résultat. Et enfin testons. Je connecte un psql sur db1. Puis je lance un autre psql sur la base postgres. J'exécute le ALTER DATABASE à partir de ce deuxième psql et j'obtiens :

 postgres=# alter database db1 tablespace mon_espace_de_stockage;
 NOTICE:  alter tablespace db1 set tablespace mon_espace_de_stockage!
 ERROR:  database "db1" is being accessed by other users
 DETAIL:  There are 1 other session(s) using the database.

La fonction a bien détecté l'autre client connecté et a empêché la suite des opérations. Déconnectons cet utilisateur et tentons de nouveau l'opération :

 postgres=# alter database db1 tablespace mon_espace_de_stockage;FATAL:  role "postgres" does not exist
 NOTICE:  alter tablespace db1 set tablespace mon_espace_de_stockage!
 ALTER DATABASE

Ça passe sans problème. Je vous laisse tester le reste. Je préfère passer à la suite, à savoir récupérer la liste des tables. Un petit soucis pointe son nez. Pour récupérer la liste des tables, je vais exécuter une requête sur pg_class. Or cette dernière ne va me donner des informations que sur la base où je suis connecté. Il faut donc modifier les tests pour s'assurer qu'il n'y a qu'un seul client connecté et que ce client est moi.

Ceci fait, la récupération des tables de la base devient notre priorité. Pour cela, nous allons utiliser la fonction heap_beginscan qui permet de réaliser un parcours d'une table. Elle demande plusieurs arguments dont la table à parcourir et un filtre optionnel. La table doit d'abord est ouverte, ce qui se fait avec la fonction heap_open, qui prend deux arguments, à savoir le nom de la table et le type de verrou). Quant au filtre, nous voulons récupéré toutes les lignes représentant des relations (constante Anum_pg_class_relkind). Ceci fait, nous avons donc ouvert la table, initié un parcours de table. Reste à récupérer les valeurs. La fonction heap_getnext va nous aider pour cela. Il nous suffit de l'emballer dans une boucle et le tour est joué. Tout ceci nous donne le code suivant :

 /* Process all plain relations listed in pg_class */
 ScanKeyInit(&key,
   Anum_pg_class_relkind,
   BTEqualStrategyNumber, F_CHAREQ,
   CharGetDatum(RELKIND_RELATION));
 
 pgclass = heap_open(RelationRelationId, AccessShareLock);
 
 scan = heap_beginscan(pgclass, SnapshotNow, 1, &key);
 
 while ( ( tuple = heap_getnext(scan, ForwardScanDirection ) ) != NULL)
 {
   elog(NOTICE, "relation %s", NameStr->relname));
 }
 
 heap_endscan(scan);
 heap_close(pgclass, AccessShareLock);

Reprenons notre cycle habituel : compilation, installation, tests...

 db1=# alter database db1 tablespace mon_espace_de_stockage;
 NOTICE:  alter tablespace db1 set tablespace mon_espace_de_stockage!
 NOTICE:  relation pg_type                                           
 NOTICE:  relation sql_implementation_info                           
 NOTICE:  relation sql_languages                                     
 [... coupé parce qu'inintéressant ...]
 NOTICE:  relation pg_namespace
 NOTICE:  relation pg_conversion
 NOTICE:  relation pg_depend
 NOTICE:  relation t1
 ALTER DATABASE

Et voilà, c'est parfait. Il ne reste plus qu'à donner l'ordre de déplacer la table.

« Entre les murs »

J'ai toujours du mal à aller voir un documentaire au cinéma. J'ai toujours la peur de m'ennuyer. Je peux dire que ça n'a pas du tout été le cas avec celui-là. Sans dire qu'il y a un suspens, j'étais quand même avide de savoir ce qu'il allait se passer, que ce soit pour les profs ou que ce soit pour les élèves. Mes sentiments ont variés fréquemment, passant du « bande de petits cons » à « ouah, ils sont géniaux ». Bref, c'est vivant et c'est vraiment sympa. Moi, j'ai beaucoup aimé.

PGDayFr 2008, un succès mérité

Je reviens tout juste de Toulouse. La première édition de PGDay France vient de se terminer. L'expérience fut bonne. J'ai tout particulièrement apprécié la prestation de Guillaume Smet. Mais plutôt que de partir moi-aussi dans la description de toutes les conférences, je vous conseille de lire le billet de Thomas Petazzoni. Bien que peu intéressé par le domaine des bases de données, il a participé fréquemment aux discussions de préparation de cette journée en tant que membre du LUG local. Il a aussi passé sa journée à filmer toutes les conférences, ce qui nous permettra de mettre rapidement en ligne celles-ci pour ceux qui n'ont pas pu venir. Bref, il était là et ses commentaires sur la journée rejoignent fortement mes impressions.

J'ai pris un certain nombre de photos que vous trouverez ici dans un premier temps. Elles sont brutes, aucune retouche, y compris pour les yeux rouges, ni même une simple suppression des photos floues. Je suis trop fatigué pour m'en occuper ce soir. Si certains ne souhaitent pas apparaître, qu'ils me laissent un simple commentaire et je supprimerais la photo du site.

Dernier point, je tiens à remercier toutes les personnes qui ont participé à la mise en place de PGDayFr 2008, et notamment Jean-Christophe Arnu, qui, par sa proximité géographique, a réalisé de très nombreuses démarches pour que cet événement ait lieu. (Et je ne me remercie pas vu que je n'ai pratiquement pas participé... préparer ce genre d'événements n'est pas quelque chose qui me motive suffisament).

Prévisions pour octobre 2008

Il n'y a pas grand chose de bien ces temps-ci :

  • « Gomora », jeudi 9 octobre ;
  • « Christophe Colomb, l'énigme », lundi 13 octobre ;
  • « Vicky, Cristina, Barcelona », jeudi 16 octobre ;
  • « Les grandes personnes », vendredi 17 ;
  • « Le sel de la mer », lundi 27 octobre ;
  • « Faubourg 36 », jeudi 30 octobre ;
  • « Le crime est notre affaire », lundi 3 novembre.

Article « Les vues systèmes sous PostgreSQL 8.3 » sur dalibo.org

Je viens tout juste de placer cet article sur dalibo.org, initialement paru sous le numéro 106 de GNU/Linux Magazine France, et maintenant disponible sous licence Creative Commons.

J'ai aussi envoyé mon tout dernier article à Denis Bodor, le rédacteur-en-chef du magazine. J'attends son verdict ;) Pour infos, cela porte sur la recherche plein texte, et plus précisément sur la configuration de cette fonctionnalité.

Pour les suivants, j'avoue que je commence à être un peu à court d'idées... si quelqu'un a une proposition, je suis preneur :) Dans les articles possibles, j'ai :

  • nouveautés SQL et PL/pgsql en 8.3 ;
  • gestion des processus avec PostgreSQL ;
  • nouveautés sur les performances.

Le premier me plaît moyen mais il permettrait de faire un article simple avec des nouveautés, donc dans l'esprit de ce que je fais depuis le premier article. Le second me plaît plus, on met beaucoup plus les mains dans le cambouis. Le dernier me plaît beaucoup aussi mais va demander beaucoup plus de travail que ce que je peux faire actuellement.

Bref, une autre idée serait la bienvenue...

« Le silence de Lorna »

Le plus étonnant est la fin, très (trop ?) ouverte mais le reste est un excellent film des frères Dardenne dont j'aime suivre la filmographie. J'aime leur façon de filmer, même si cela a un peu changer avec ce film, beaucoup moins « dans le mouvement » que « Rosetta par exemple. J'aime aussi leur écriture avec un scénario où on trouve toujours une femme forte. J'avais adoré « Rosetta » par exemple, film pourtant vraiment difficile. Ce film n'est clairement pas facile, mais il l'est plus que « Rosetta ». Et je pense qu'il me marquera moins. Néanmoins, c'est un excellent film, tout d'abord grâce à un scénario béton. Mais aussi par ses acteurs. Arta Dobroshi est vraiment douée, c'est encore une fois une excellente trouvaille des frères Dardennes. Jérémie Rénier est lui-aussi impressionnant dans son rôle de camé. Et puis, j'ai encore une fois pu apprécier l'apparition rapide d'Olivier Gourmet. En fait, je crois qu'on peut dire qu'Arta est le film. Je n'arrive pas à imaginer ce film sans elle.

Quelques billets intéressants sur ce film :

Petite minute d'autopromotion

Dur matin aujourd'hui... mal réveillé, pas en super forme après le déménagement de ce week-end... Pfiou que de mails en attente, et de billets de blogs non lus. C'est vrai que ça fait cinq jours que je suis à fond dans le boulot et le déménagement. Bref.

Je viens de lire un billet qui m'a fait beaucoup plaisir. Leo Hsu et Regina Obe (les auteurs du Postgres Online Journal) ont testé la version de développement de pgAdmin (la 1.9) et viennent de publier leurs impressions. En fait, ils citent les cinq nouvelles fonctionnalités qui les ont le plus marqué. En tout premier, le support de FTS. J'en ai déjà (trop ?) longuement parlé. Ensuite le concepteur de requêtes graphiques. Enfin, le dimensionnement modifiable des fenêtres, la gestion de l'héritage des tables et le champ SQL modifiable.

Autrement dit, sur les cinq fonctionnalités qu'ils ont choisi de mettre en avant, quatre ont été codées par moi :) Hmmm... vous ne pouvez pas vous plaindre, je vous avais prévenu dans le titre :)

De la création d'index pour une fonction

Au cas où vous seriez en train de créer un index pour accélérer la rapidité d'une requête dans une fonction PL/pgsql, pensez à (re-)créer votre fonction après avoir créé l'index.

En effet, le plan de la fonction est créé lors de la création de la fonction, si bien que toute amélioration possible après coup (comme par exemple l'ajout d'un index) n'est pas pris en compte. Recréer la fonction recréera le plan d'exécution... et vous pourriez vous retrouver comme moi avec une fonction passant de 58 secondes de durée d'exécution à 2 secondes. Un gain vraiment appréciable :)

Valide en version 8.1... pas sûr que cela soit encore nécessaire en 8.2 ou 8.3.

Mise à jour : merci à Thomas pour avoir trouvé une erreur dans ce billet... qui est maintenant corrigé.

Développons une nouvelle fonctionnalité pour PostgreSQL (5/?)

continuons à améliorer l'interface

Je n'ai pas trop le temps, donc on va faire deux petites modifs rapides : ajouter l'aide dans psql et gérer la complétion automatique toujours dans psql.

Je laisse de côté les étapes de récupération des sources mises à jour (cvs -q update -d), la compilation/installation (make && make install), la possible ré-initialisation du cluster (j'en ai encore eu besoin... les développeurs bossent beaucoup ces temps-ci :) ), etc.

Testons la méta-commande \h sur la commande ALTER DATABASE :

guillaume@laptop$ psql postgres
psql (8.4devel)
Type "help" for help.

postgres=# \h alter database
Command:     ALTER DATABASE
Description: change a database
Syntax:
ALTER DATABASE name [ [ WITH ] option [ ... ] ]

where option can be:

    CONNECTION LIMIT connlimit

ALTER DATABASE name RENAME TO newname

ALTER DATABASE name OWNER TO new_owner

ALTER DATABASE name SET configuration_parameter { TO | = } { value | DEFAULT }
ALTER DATABASE name SET configuration_parameter FROM CURRENT
ALTER DATABASE name RESET configuration_parameter
ALTER DATABASE name RESET ALL

Il manque notre option. Le fichier à modifier, on l'a déjà aperçu dans un épisode précédent, est src/bin/psql/sql_help.h. On aperçoit dans les toutes premières lignes :

* *** Do not change this file by hand. It is automatically
* *** generated from the DocBook documentation.

Ah, dommage. Bon, on va quand même le modifier juste pour la forme :)

En lisant rapidement ce fichier, on arrive à la ligne 36 qui indique ceci :

    { "ALTER DATABASE",
      N_("change a database"),
      N_("ALTER DATABASE name [ WITH option ... ]\n\nwhere option can be:\n\n    CONNECTION LIMIT connlimit\n\nALTER DATABASE name RENAME TO newname\n\nALTER DATABASE name OWNER TO new_owner\n\nALTER DATABASE name SET configuration_parameter { TO | = } { value | DEFAULT }\nALTER DATABASE name SET configuration_parameter FROM CURRENT\nALTER DATABASE name RESET configuration_parameter\nALTER DATABASE name RESET ALL") },

Il suffit de modifier la troisième ligne en ajoutant le texte suivant après le mot connlimit : \n TABLESPACE tablespace_name. Après avoir modifié le fichier, il nous reste plus qu'à compiler et tester :

guillaume@laptop$ make && make install
[... coupé ...]
make1: quittant le répertoire « /home/guillaume/postgresql_src/pgsql/config »
PostgreSQL installation complete.
guillaume@laptop$ psql postgres
psql (8.4devel)
Type "help" for help.

postgres=# \h alter database
Command:     ALTER DATABASE
Description: change a database
Syntax:
ALTER DATABASE name [ [ WITH ] option [ ... ] ]

where option can be:

    CONNECTION LIMIT connlimit
    TABLESPACE tablespace_name

ALTER DATABASE name RENAME TO newname

ALTER DATABASE name OWNER TO new_owner

ALTER DATABASE name SET configuration_parameter { TO | = } { value | DEFAULT }
ALTER DATABASE name SET configuration_parameter FROM CURRENT
ALTER DATABASE name RESET configuration_parameter
ALTER DATABASE name RESET ALL

Yes ! ça fonctionne :) mais pas bien compliqué en fait.

Maintenant, la gestion de la complétion automatique. Actuellement, on a ceci :

guillaume@laptop$ psql postgres
psql (8.4devel)
Type "help" for help.

postgres=# alter DATABASE guillaume<tab><tab>
CONNECTION LIMIT  OWNER TO          RENAME TO         RESET             SET
postgres=# alter DATABASE guillaume

<tab> étant à remplacer par une tabulation. On voit là-aussi que le mot clé TABLESPACE ne s'y trouve pas... allez hop, on se remonte les manches. Là-aussi, on connaît déjà le fichier, il s'agit de src/bin/psql/tab-complete.c. On peut chercher ALTER DATABASE pour trouver à la première occurence cette partie :

	/* ALTER DATABASE <name> */
	else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 &&
			 pg_strcasecmp(prev2_wd, "DATABASE") == 0)
	{
		static const char *const list_ALTERDATABASE =
		{"RESET", "SET", "OWNER TO", "RENAME TO", "CONNECTION LIMIT", NULL};

		COMPLETE_WITH_LIST(list_ALTERDATABASE);
	}

Les options semblent être toutes dans la variable list_ALTERDATABASE. Modifions-la pour ajouter l'option TABLESPACE. On compile de nouveau, et on teste :

guillaume@laptop$ psql postgres
psql (8.4devel)
Type "help" for help.

postgres=# alter database guillaume<tab><tab>
CONNECTION LIMIT  OWNER TO          RENAME TO         RESET             SET               TABLESPACE
postgres=# alter database guillaume

Ça fonctionne \o/

Bref, pas grand chose de nouveau, mais il fallait de toute façon le faire. On fera plus passionnant la prochaine fois.

Au fait, au dernier numéro, j'ai ajouté un fichier patch au billet sans expliquer comment je l'ai fait. C'est très simple, il suffit d'exécuter la commande suivante :

guillaume@laptop$ cvs diff > ~/tablespace2.patch

Le fichier obtenu correspond à celui en pièce jointe de ce fichier. Vous pouvez appliquer ce patch en vous plaçant dans le répertoire des sources et en exécutant la commande suivante :

guillaume@laptop$ patch -Np0 < chemin/vers/tablespace2.patch

Développons une nouvelle fonctionnalité pour PostgreSQL (4/?)

un peu de code

Encore une fois, commençons par mettre à jour les sources :

guillaume@laptop$ cd postgresql_src/pgsql/
guillaume@laptop$ cvs -q update -d
[... coupé parce que trop long ...]
P src/interfaces/libpq/fe-exec.c
P src/interfaces/libpq/libpq-events.c
P src/interfaces/libpq/libpq-events.h
P src/interfaces/libpq/libpq-fe.h
P src/interfaces/libpq/libpq-int.h

Et on compile rapidement :

guillaume@laptop$ make && make install
[... coupé parce que trop long ...]
PostgreSQL installation complete.

Lançons PostgreSQL :

guillaume@laptop$ export PGDIR=/home/guillaume/postgresql_devel
guillaume@laptop$ export PGDATA=$PGDIR/data
guillaume@laptop$ export PATH=$PGDIR/bin:$PATH
guillaume@laptop$ pg_ctl start
server starting
FATAL:  database files are incompatible with server
DETAIL:  The database cluster was initialized with CATALOG_VERSION_NO 200809151, but the server was compiled with CATALOG_VERSION_NO 200809191.
HINT:  It looks like you need to initdb.

Oups. Tiens, la version du catalogue a changé. Il faut de nouveau exécuter initdb... c'est parti :

guillaume@laptop$ rm -rf $PGDATA/* /home/guillaume/postgresql_tblspc/*
guillaume@laptop$ initdb
The files belonging to this database system will be owned by user "guillaume".
This user must also own the server process.
[... coupé ...]
Success. You can now start the database server using:

    postgres -D /home/guillaume/postgresql_devel/data
or
    pg_ctl -D /home/guillaume/postgresql_devel/data -l logfile start

guillaume@laptop$ pg_ctl start
server starting
LOG:  database system was shut down at 2008-09-22 19:48:45 CEST
LOG:  autovacuum launcher started
LOG:  database system is ready to accept connections

C'est bon ! On doit recréer la base de données, la table et le tablespace :

guillaume@laptop$ createdb guillaume
guillaume@laptop$ psql guillaume
psql (8.4devel)
Type "help" for help.

guillaume=# CREATE TABLE t1 (id integer);
CREATE TABLE
guillaume=# CREATE TABLESPACE mon_espace_de_stockage LOCATION '/home/guillaume/postgresql_tblspc';
CREATE TABLESPACE

Voilà. On est prêt à plonger dans le code.

La première chose à faire est de modifier la syntaxe. On a vu la dernière fois que cela se passe dans le fichier src/backend/parser/gram.y (gram pour grammaire). En regardant la syntaxe déjà proposée par ALTER DATABASE, on voit qu'il est déjà possible d'ajouter une option, l'option « connection limit ». On va donc chercher cette syntaxe dans le fichier gram.y. Cherchons l'instruction ALTER DATABASE. Première occurence, le ALTER DATABASE pour le renommage d'une base de données. En fait ce code est intégré aux autres instructions de renommage des objets. Ça ne nous intéresse pas. Occurence suivante, le ALTER DATABASE pour changer le propriétaire. Là aussi, ce code est intégré aux autres instructions de changement de propriétaire. Ça ne nous intéresse toujours pas. Occurence suivante. Ah, une partie complète sur ALTER DATABASE. Le jeton AlterDatabaseStmt est celui qui nous intéresse. « ALTER DATABASE database_name opt_with alterdb_opt_list » correspond au « ALTER DATABASE database_name WITH CONNECTION LIMIT x ». « alterdb_opt_list » correspond à la liste des options, pour l'instant qui se limite à une, mais nous devons en ajouter une deuxième. Cherchons donc maintenant « alterdb_opt_list ». Nous trouvons ce code :

alterdb_opt_list:
			alterdb_opt_list alterdb_opt_item		{ $$ = lappend($1, $2); }
			| /* EMPTY */							{ $$ = NIL; }
		;

Cela déclare « tout simplement » une liste. Nous devons maintenant chercher « alterdb_opt_item », et nous trouvons ceci :

alterdb_opt_item:
			CONNECTION LIMIT opt_equal SignedIconst
				{
					$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
				}
		;

Si on essaie d'analyser cela, on comprend que la chaîne précise les mots fixes (« CONNECTION LIMIT »), un jeton pour le symbole d'égalité et un SignedIconst qui est un jeton correspondant à une constante entière signée (Signed Integer Constant). Il nous faut ajouter la syntaxe pour le tablespace :

alterdb_opt_item:
			CONNECTION LIMIT opt_equal SignedIconst
				{
					$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
				}
			| TABLESPACE name
				{
					$$ = makeDefElem("tablespace", (Node *)makeString($2));
				}
		;

C'est-à-dire le mot fixe TABLESPACE suivi du nom du tablespace. « name » n'est pas juste la traduction bête, mais un jeton représentant le nom d'un objet PostgreSQL.

Et maintenant que doit faire cette nouvelle syntaxe ? Nous avons vu la dernière fois que le fichier backend/commands/tablecmds.c gérait tout ce qui concernait les commandes relatives aux tables. Peut-être existe-il la même chose pour les bases. Cherchons ça :

guillaume@laptop$ ll src/backend/commands/ | grep base
guillaume@laptop$ ll src/backend/commands/ | grep db
-rw-rr 1 guillaume guillaume  46550 2008-09-22 22:37 dbcommands.c
-rw-rr 1 guillaume guillaume  22084 2008-09-22 22:37 dbcommands.o

backend/commands/dbcommands.c semble être le bon fichier. Ouvrons le fichier et cherchons le texte « ALTER DATABASE ». On arrive directement sur une fonction qui prend en argument une structure AlterDatabaseStmt. Hmmm, mais c'est le jeton que nous avons modifié... excellent, ça. En lisant le code de la fonction, on voit cette partie :

if (strcmp(defel->defname, "connectionlimit") == 0)
{
  if (dconnlimit)
    ereport(ERROR,
      (errcode(ERRCODE_SYNTAX_ERROR),
      errmsg("conflicting or redundant options")));
    dconnlimit = defel;
}

C'est le code qui s'occupe de l'option CONNECTION LIMIT. Il nous reste à ajouter le code pour l'option TABLESPACE. Nous allons seulement ajouter un code qui envoie un message dans les journaux applicatifs pour savoir si nous avons bien réussi à intégrer une nouvelle syntaxe. Cela donne ce code :

else if (strcmp(defel->defname, "tablespace") == 0)
{
  elog(NOTICE, "set tablespace !");
}

Simple, non ?

Voyons déjà ce que donne ces modifications. On compile, installe et redémarre PostgreSQL :

guillaume@laptop$ make && make install
[... coupé de nouveau ...]
make1: quittant le répertoire « /home/guillaume/postgresql_src/pgsql/config »
PostgreSQL installation complete.
guillaume@laptop$ pg_ctl restart
waiting for server to shut down....
LOG:  received smart shutdown request
LOG:  autovacuum launcher shutting down
LOG:  shutting down
LOG:  database system is shut down
 done
server stopped
server starting
LOG:  database system was shut down at 2008-09-22 22:36:28 CEST
LOG:  autovacuum launcher started
LOG:  database system is ready to accept connections

Lançons psql :

guillaume@laptop$ psql postgres
psql (8.4devel)
Type "help" for help.

postgres=# alter database guillaume with tablespace mon_espace_de_stockage ;
NOTICE:  set tablespace !
ALTER DATABASE

L'ajout de la syntaxe a fonctionné \o/

Maintenant, il va falloir ajouter un code plus intéressant dans cette fonction.

Développons une nouvelle fonctionnalité pour PostgreSQL (3/?)

un peu de recherche

Nous venons de voir qu'une table est un fichier dont le nom dépend du relfilenode de la table et qui se trouve dans le sous-répertoire correspondant à l'OID de la base de données, lui même appartenant au répertoire du tablespace.

Déplacer une table d'un tablespace à un autre doit simplement revenir à faire un mv du fichier vers le nouveau tablespace. Vérifions cela.

Avant toute action sur les sources, commençons par les mettre à jour :

guillaume@laptop$ cd postgresql_src/pgsql/
guillaume@laptop$ cvs -q update -d
[... coupé parce que trop long ...]
P src/timezone/data/leapseconds
P src/timezone/data/northamerica
P src/timezone/data/southamerica
P src/timezone/data/zone.tab
P src/tools/msvc/Install.pm

OK. Plaçons-nous maintenant dans le répertoire src où se concentrent tous les fichiers sources de PostgreSQL (pour ceux qui connaissent le répertoire contrib, il contient bien des sources, mais ces sources ne font pas partis du moteur).

guillaume@laptop$ cd src

Recherchons toutes les occurences de « set tablespace », mais filtrons les fichiers intermédiaires de compilation (suffixe .o), les fichiers de traduction (suffixe .po) et les fichiers binaires.

guillaume@laptop$ grep -ri "set tablespace" * | grep -v "\.po" | grep -v "\.o" | grep -v "Fichier binaire"
backend/parser/gram.y:                  /* ALTER TABLE <name> SET TABLESPACE <tablespacename> */
backend/parser/gram.y:                  | SET TABLESPACE name
backend/commands/tablecmds.c:           case AT_SetTableSpace:  /* SET TABLESPACE */
backend/commands/tablecmds.c:           case AT_SetTableSpace:  /* SET TABLESPACE */
backend/commands/tablecmds.c:                    * If we had SET TABLESPACE but no reason to reconstruct tuples,
backend/commands/tablecmds.c: * ALTER TABLE SET TABLESPACE
backend/commands/tablecmds.c:                            errmsg("cannot have multiple SET TABLESPACE subcommands")));
backend/commands/tablecmds.c: * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
bin/psql/sql_help.h:      N_("ALTER INDEX name RENAME TO new_name\nALTER INDEX name SET TABLESPACE tablespace_name\nALTER INDEX name SET ( storage_parameter = value , ... )\nALTER INDEX name RESET ( storage_parameter , ... )") },
bin/psql/sql_help.h:      N_("ALTER TABLE ONLY name *\n    action , ...\nALTER TABLE ONLY name *\n RENAME COLUMN column TO new_column\nALTER TABLE name\n    RENAME TO new_name\nALTER TABLE name\n    SET SCHEMA new_schema\n\nwhere action is one of:\n\n    ADD COLUMN column type column_constraint [ ... ]\n    DROP COLUMN column  RESTRICT \n    ALTER COLUMN column TYPE type USING expression\n    ALTER COLUMN column SET DEFAULT expression\n    ALTER COLUMN column DROP DEFAULT\n    ALTER COLUMN column { SET | DROP } NOT NULL\n ALTER COLUMN column SET STATISTICS integer\n    ALTER COLUMN column SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }\n    ADD table_constraint\n    DROP CONSTRAINT constraint_name  RESTRICT \n    DISABLE TRIGGER  trigger_name \n    ENABLE TRIGGER  trigger_name \n    ENABLE REPLICA TRIGGER trigger_name\n ENABLE ALWAYS TRIGGER trigger_name\n    DISABLE RULE rewrite_rule_name\n    ENABLE RULE rewrite_rule_name\n    ENABLE REPLICA RULE rewrite_rule_name\n    ENABLE ALWAYS RULE rewrite_rule_name\n    CLUSTER ON index_name\n    SET WITHOUT CLUSTER\n    SET WITHOUT OIDS\n    SET ( storage_parameter = value , ... )\n    RESET ( storage_parameter , ... )\n INHERIT parent_table\n    NO INHERIT parent_table\n    OWNER TO new_owner\n    SET TABLESPACE new_tablespace") },
bin/psql/tab-complete.c:                {"SET TABLESPACE", "OWNER TO", "RENAME TO", "SET", "RESET", NULL};
bin/psql/tab-complete.c:        /* If we have TABLE <sth> SET TABLESPACE provide a list of tablespaces */
include/nodes/parsenodes.h:     AT_SetTableSpace,                       /* SET TABLESPACE */
interfaces/ecpg/preproc/preproc.y:              /* ALTER <name> SET TABLESPACE <tablespacename> */
interfaces/ecpg/preproc/preproc.y:              | SET TABLESPACE name
interfaces/ecpg/preproc/preproc.y:                      { $$ = cat2_str(make_str("set tablespace"), $3); }
interfaces/ecpg/preproc/preproc.c:    { (yyval.str) = cat2_str(make_str("set tablespace"), (yyvsp(3) - (3).str)); ;}
test/regress/input/tablespace.source:ALTER TABLE testschema.atable SET TABLESPACE testspace;
test/regress/input/tablespace.source:ALTER INDEX testschema.anindex SET TABLESPACE testspace;
test/regress/output/tablespace.source:ALTER TABLE testschema.atable SET TABLESPACE testspace;
test/regress/output/tablespace.source:ALTER INDEX testschema.anindex SET TABLESPACE testspace;

Les deux premières lignes concernent un fichier nommé gram.y. Le suffixe .y indique qu'il s'agit d'un fichier bison. Bison est un outil permettant de créer facilement un langage (pour les anciens Unixiens, Bison est la version GNU de yacc). C'est d'ailleurs ce fichier qu'il nous faudra modifier pour ajouter notre variante à l'instruction « ALTER TABLE ». Le fichier tablecmds.c a l'air de s'occuper de l'exécution concrète des commandes sur les tables. Son emplacement est d'ailleurs significatif. Le répertoire backends contient tous les sources du serveur, le sous-répertoire commands tous les sources concernant l'exécution des commandes SQL. Le fichier sql_help.h semble contenir l'aide proposée par psql via la commande \\h. Son emplacement est significatif là-aussi. Le répertoire bin contient tous les sources des outils binaires de PostgreSQL (psql, initdb, pg_dump, etc.). Le nom du fichier bin/psql/tab-complete.c nous donne une indication sur son utilité : il s'agit des sources pour la gestion de la complétion automatique dans psql. Quant au reste des fichiers, ils nous intéressent peu. Cela touche à ECPG et aux tests de régression. Beurk :)

Bref, le fichier qui nous intéresse le plus actuellement, c'est tablecmds.h. Ouvrons-le et recherchons la première occurence de « SET TABLESPACE ».

La première occurence correspond au début d'une instruction case :

case AT_SetTableSpace:	/* SET TABLESPACE */
  ATSimplePermissionsRelationOrIndex(rel);
  /* This command never recurses */
  ATPrepSetTableSpace(tab, rel, cmd->name);
  pass = AT_PASS_MISC;	/* doesn't actually matter */
  break;

Deux fonctions sont appelées. D'après leur nom, on imagine facilement que la première va vérifier les droits de la table ou de l'index, et que la second prépare l'exécution du « SET TABLESPACE ». Si on regarde dans quelle fonction se trouve ces instructions (simplement en remontant un peu), on trouve l'en-tête suivant :

/*
 * ATPrepCmd
 *
 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
 * recursion and permission checks.
 *
 * Caller must have acquired AccessExclusiveLock on relation already.
 * This lock should be held until commit.
 */

Cette fonction traite la phase 1 de l'instruction « ALTER TABLE », qui laisse penser qu'il y a d'autres phases et que cette phase 1 est la phase préparatoire.

Bon, deuxième occurence :

case AT_SetTableSpace:	/* SET TABLESPACE */
  /*
   * Nothing to do here; Phase 3 does the work
   */
  break;

Ce code ne fait rien. On apprend juste que le travail se fait en trois phases et que seule la phase 3 fait le boulot.

Occurence suivante :

/*
 * If we had SET TABLESPACE but no reason to reconstruct tuples,
 * just do a block-by-block copy.
 */
if (tab->newTableSpace)
  ATExecSetTableSpace(tab->relid, tab->newTableSpace);

Là-aussi, peu de choses. On apprend seulement que le « SET TABLESPACE » semble fonctionner dans un mode copie bloc par bloc. Ce qui serait assez logique, tout PostgreSQL fonctionne par bloc.

Occurence suivante :

/*
 * ALTER TABLE SET TABLESPACE
 */
static void
ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, char *tablespacename)

Tiens, c'est la fonction appelée à la phase 1. Je ne vais pas mettre tout le code ici, mais voici l'explication du code de cette fonction. La fonction commence par vérifier si le tablespace existe. Dans le cas contraire, elle envoie un message « tablespace "%s" does not exist » avec un niveau ERROR. Ensuite, les droits sont vérifiés. En fait, un seul, celui de création sur le tablespace cible. Enfin, des informations sont sauvegardées pour la fameuse phase 3.

Allez, courage, occurence suivante :

/*
 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
 * rewriting to be done, so we just want to copy the data as fast as possible.
 */
static void
ATExecSetTableSpace(Oid tableOid, Oid newTableSpace)

Cette fois, c'est l'occurence 3 qui fait appel à cette fonction. Pareil, je ne mets pas le code, mais une explication de son travail. Joie, c'est là que le boulot se fait. La fonction commence par poser un verrou AccessExclusiveLock sur la table. Les tables systèmes partagées ne sont pas déplaçables, donc elle vérifie aussi ce cas. Le tablespace cible est vérifié : il ne faut pas que ce soit pg_global, qui est le tablespace des objets systèmes non partagés. La fonction vérifie aussi le statut de la table : si elle est temporaire, elle n'est pas déplaçable. Dernière vérification, le tablespace cible est-il le tablespace où se trouve déjà la table ? Une fois toutes ces vérifications terminées, si le déplacement est possible, le boulot de copie va commencer. Tout d'abord, la fonction force l'enregistrement des blocs modifiés de cette table du cache de PostgreSQL (shared_buffers) sur le disque. Ensuite, la fonction exécute une autre fonction pour chaque fichier de la table source (en effet, une table est stockée sur plusieurs fichiers de 1 Go). Cette autre fonction, de nom copy_relation_data, s'occupe de la copie physique des données. En la parcourant rapidement, on s'aperçoit que le fichier source est copié bloc par bloc, c'est-à-dire 8 Ko par 8 Ko. Chaque copie de bloc est tracée dans les journaux de transactions dans le cas où l'archivage des journaux est activé. Les blocs ne passent pas par le cache disque de PostgreSQL, on ne risque donc pas de dévalider l'entièreté du cache simplement parce qu'on déplace des fichiers.

Faisons un petit résumé. Le fichier backend/commands/tablecmds.c contient toutes ls instructions permettant d'exécuter le déplacement d'une table/index d'un tablespace vers un autre. La copie est elle-même est tout simple : une copie de chaque bloc, un par un.

On sait maintenant comment PostgreSQL réagit à l'ordre de déplacement d'une table. Essayons d'extrapoler. Si on veut déplacer une base de données complète vers un autre tablespace, cela revient à récupérer la liste des tables et index et à les envoyer un par un vers le nouveau tablespace. En fait, plus exactement, cela revient à déplacer les fichiers qui sont dans le tablespace par défaut de la base de données vers le nouveau tablespace. En effet, si d'autres objets ont été déplacés volontairement par l'utilisateur, il ne faut pas les prendre en compte.

Du coup, on peut écrire un pseudo code :

Pour toutes les tables et index compris dans le tablespace par défaut de la base de données ciblée,
 Faire l'équivalent d'un ALTER TABLE la table SET TABLESPACE le nouveau tablespace
Enregistrer que le tablespace par défaut est le tablespace cible.

Donc, le gros du travail est déjà fait. Ça nous simplifie considérablement la tâche. La prochaine étape est donc de faire accepter la nouvelle instruction à PostgreSQL en déclenchant l'exécution d'une fonction. Enfin ! On va écrire un peu de code :)

Développons une nouvelle fonctionnalité pour PostgreSQL (2/?)

... la réflexion avant l'action ...

Avant de se lancer dans le code, commençons par nous demander ce qu'est un tablespace. Un tablespace est tout simplement un espace de stockage. Par défaut, après l'initdb, il existe déjà deux tablespaces « virtuels ».

guillaume@laptop$ export PGDIR=/home/guillaume/postgresql_devel
guillaume@laptop$ export PGDATA=$PGDIR/data
guillaume@laptop$ export PATH=$PGDIR/bin:$PATH
guillaume@laptop$ pg_ctl start
server starting
LOG:  database system was shut down at 2008-09-16 23:50:01 CEST
LOG:  autovacuum launcher started
LOG:  database system is ready to accept connections
guillaume@laptop$ psql -c "SELECT oid, spcname, spclocation FROM pg_tablespace" postgres
 oid  |  spcname   | spclocation
- - - +- - - - - - +- - - - - - -
 1663 | pg_default |
 1664 | pg_global  |
(2 rows)

Pourquoi virtuel ? parce qu'ils n'ont pas d'emplacement (colonne « Location » vide). En fait, ils désignent tous les deux le répertoire du cluster, c'est-à-dire /home/guillaume/postgresql_devel/data dans mon cas.

Créons un répertoire qui va servir à notre futur tablespace :

guillaume@laptop$ mkdir postgresql_tblspc

Ensuite, créons le tablespace en précisant le répertoire comme emplacement de stockage :

guillaume@laptop$ psql postgres
psql (8.4devel)
Type "help" for help.

postgres=# CREATE TABLESPACE mon_espace_de_stockage LOCATION '/home/guillaume/postgresql_tblspc';
CREATE TABLESPACE

Enfin, créons une table... ah, non, on va commencer par créer une base et on va s'y connecter :

postgres=# CREATE DATABASE guillaume;
CREATE DATABASE
postgres=# \c guillaume
psql (8.4devel)
You are now connected to database "guillaume".

Créons enfin cette table :

guillaume=# CREATE TABLE t1 (id integer);
CREATE TABLE

Où est stockée cette table ?

guillaume=# SELECT relname, reltablespace, relfilenode FROM pg_class WHERE relname='t1';
 relname | reltablespace | relfilenode
- - - - -+- - - - - - - -+- - - - - - -
 t1      |             0 |       16386
(1 row)

Le tablespace de la table correspond à l'identifiant 0, donc une première réponse serait : dans le tablespace par défaut de la base de données.

guillaume=# SELECT oid, datname, dattablespace FROM pg_database WHERE datname='guillaume';
  oid  |  datname  | dattablespace
- - - -+- - - - - -+- - - - - - - -
 16384 | guillaume |          1663
(1 row)

Le tablespace de la base de données correspond lui à l'identifiant 1663. C'est le tablespace pg_default (voir plus haut). Logique, nous n'avons pas spécifié un tablespace particulier pour la base de données que nous venons de créer. Donc notre table se trouve dans le fichier nommé 16386 (ie le relfilenode), fichier qui se trouve dans le répertoire 16384 (OID de la base de données) du répertoire base du répertoire /home/guillaume/postgresql_devel/data. Autrement dit, il s'agit du fichier /home/guillaume/postgresql_devel/data/base/16384/16386 :

guillaume@laptop$ ll /home/guillaume/postgresql_devel/data/base/16384/16386
-rw--- 1 guillaume guillaume 0 2008-09-17 20:18 /home/guillaume/postgresql_devel/data/base/16384/16386

Très bien. Et si on déplaçait cette table dans notre tablespace :

guillaume=# ALTER TABLE t1 SET TABLESPACE mon_espace_de_stockage;
ALTER TABLE
guillaume=# SELECT relname, reltablespace, relfilenode FROM pg_class WHERE relname='t1';
 relname | reltablespace | relfilenode
- - - - -+- - - - - - - -+- - - - - - -
 t1      |         16385 |       16386
(1 row)

guillaume=# SELECT * FROM pg_tablespace WHERE oid=16385;
        spcname         | spcowner |            spclocation            | spcacl
- - - - - - - - - - - - +- - - - - +- - - - - - - - - - - - - - - - - -+- - - - 
 mon_espace_de_stockage |       10 | /home/guillaume/postgresql_tblspc |
(1 row)

Très bien, Donc ma table doit être stockée dans le répertoire 16384 du répertoire du tablespace :

guillaume@laptop$ ll /home/guillaume/postgresql_tblspc/16384/16386
-rw--- 1 guillaume guillaume 0 2008-09-17 21:47 /home/guillaume/postgresql_tblspc/16384/16386

Parfait.

Je ne vais pas faire une démo aussi pour les index, mais le comportement est le même.

En résumant tout ça, un tablespace n'est qu'un simple répertoire contenant un répertoire par base de données ayant des objets dans ce tablespace. Chaque objet de ce tablespace, table et index, sont stockées dans le sous répertoire correspondant à l'OID de la base de données.

Maintenant qu'on sait physiquement ce qu'est un tablespace, on va pouvoir regarder un peu le code.

Nouveau site web pg.fr

.. ou plutôt nouveaux sites web.

Comme l'a annoncé Damien vendredi dernier sur la liste de discussion française, le site www.postgresqlfr.org a été entièrement revu. Plutôt que d'utiliser Drupal pour les news, le forum et certainement d'autres choses, nous sommes passés à plusieurs outils :

Ils ont tous le même en-tête, la même feuille de style... on pourrait croire qu'il s'agit d'un seul et même site).

Il a aussi été décidé qu'ils auront chacun leur alias :

  • blog.postgresql.fr pour les news ;
  • docs.postgresql.fr pour la documentation ;
  • forums.postgresql.fr pour les forums ;
  • asso.postgresql.fr pour le wiki ;
  • planete.postgresql.fr pour la planète des blogueurs PostgreSQL français ;
  • etc.

Le boulot qui reste est de transférer le contenu du Drupal dans Dotclear et le wiki. Ça ne devrait pas être long et ça permettra de se débarrasser du Drupal. Joie :)

Je suis vraiment très content du résultat. Si on regarde finement le codage, il y a encore du travail pour tout factoriser. Mais le résultat est convaincant. J'avais initié le travail sur les nouveaux sites le samedi 6 septembre. 11 jours de travail, à raison d'une ou deux heures en moyenne par jour, c'est rien du tout comparé au résultat. Oui, ça fait un peu auto-congratulation mais j'assume ;)

En tout cas, je vous invite à utiliser ces nouveaux sites et à m'indiquer tout dysfonctionnement. Je vais essayer de réagir promptement à tout problème constaté.

UPDATE: correction du titre (merci Damien :) )

Développons une nouvelle fonctionnalité pour PostgreSQL (1/?)

... get the source, Luke ...

Commençons par récupérer les sources. Pour cela, nous devons passer par l'outil cvs. Choisissez un répertoire de travail, placez-vous y :

guillaume@laptop$ mkdir /home/guillaume/postgresql-src
guillaume@laptop$ cd /home/guillaume/postgresql-src

Lancez la commande de connexion à CVS :

guillaume@laptop$ export CVSROOT=:pserver:anoncvs@anoncvs.postgresql.org:/projects/cvsroot
guillaume@laptop$ cvs login
Logging in to :pserver:anoncvs@anoncvs.postgresql.org:2401/projects/cvsroot
CVS password:

Un mot de passe vous sera demandé. Saisissez ce que vous voulez, mais pas rien.

Puis récupérez les sources de la version de développement (appelé HEAD dans l'argot CVS) :

guillaume@laptop$ cvs -z3 co -P pgsql
[...]
U pgsql/src/tutorial/funcs_new.c
U pgsql/src/tutorial/syscat.source
cvs checkout: Updating pgsql/src/tutorial/C-code
cvs checkout: Updating pgsql/src/utils
cvs checkout: Updating pgsql/src/win32

Après un bon moment et des dizaines de lignes affichées plus tard, vous vous trouverez avec un répertoire pgsql contenant les sources de la prochaine version de PostgreSQL. Vous pouvez le renommer si vous le souhaitez.

On va en profiter pour compiler cette version. Tout d'abord, il faut lancer l'étape de configuration :

guillaume@laptop$ cd pgsql
guillaume@laptop$ export PGDIR=/home/guillaume/postgresql_devel
guillaume@laptop$ ./configure --prefix=$PGDIR
[...]
config.status: linking ./src/backend/port/sysv_shmem.c to src/backend/port/pg_shmem.c
config.status: linking ./src/backend/port/dynloader/linux.h to src/include/dynloader.h
config.status: linking ./src/include/port/linux.h to src/include/pg_config_os.h
config.status: linking ./src/makefiles/Makefile.linux to src/Makefile.port

L'option --prefix me permet de spécifier le répertoire où sera installé la version compilée de PostgreSQL. Changez-le par le répertoire qui vous intéresse.

Enfin, lancez la compilation et l'installation :

guillaume@laptop$ make && make install
[...]
make[1]: quittant le répertoire « /home/guillaume/postgresql_src/pgsql/config »
PostgreSQL installation complete.

Après quelques minutes de compilation (dépendant de votre machine), vous disposez d'une version pre-8.4 de PostgreSQL :)

Créons le cluster :

guillaume@laptop$ export PGDATA=$PGDIR/data
guillaume@laptop$ $PGDIR/bin/initdb
[...]
Success. You can now start the database server using:

    /home/guillaume/postgresql_devel/bin/postgres -D /home/guillaume/postgresql_devel/data
or
    /home/guillaume/postgresql_devel/bin/pg_ctl -D /home/guillaume/postgresql_devel/data -l logfile start

Et lançons PostgreSQL pour vérifier qu'il fonctionne bien :

guillaume@laptop$ $PGDIR/bin/pg_ctl -l logfile start
server starting
guillaume@laptop$ $PGDIR/bin/psql postgres
psql (8.4devel)
Type "help" for help.

postgres=# select version();
                                              version
---
 PostgreSQL 8.4devel on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu1)
(1 row)

Youhou, on a une version fonctionnelle \o/

(C'est une façon rapide de présenter la compilation de la version de développement PostgreSQL. N'hésitez pas à consulter l'annexe H, « Dépôt CVS », pour plus d'informations.)

Développons une nouvelle fonctionnalité pour PostgreSQL (0/?)

Bonsoir,

Ça fait un petit moment que ça trotte dans ma petite tête de malade.Pas le fait d'écrire un patch implantant une nouvelle fonctionnalité pour PostgreSQL, mais le fait d'en faire une série. Voilà c'est dit. Ce billet est le premier d'une (pas trop longue, j'espère) série sur le développement d'une nouvelle fonctionnalité pour PostgreSQL.

Évidemment, j'aurais pu me plonger dans la liste des fonctionnalités manquantes, mais j'avais déjà mon idée. Je l'avais depuis le début de l'été, c'est dire :) Ce projet est donc d'ajouter la possibilité de modifier le tablespace d'une base de données. Je vous entends déjà dire que je suis à côté de mes pompes, que la fonctionnalité des tablespaces est présent depuis la version 8.0. Et vous avez raison. Enfin, pour le deuxième point en tout cas :) En effet, les tablespaces sont présents depuis la version 8.0. Il est possible de créer, modifier et supprimer un tablespace. Il est possible de créer une relation (donc table ou index) dans un tablespace particulier, il est possible de la déplacer dans un autre. Pareil pour les bases de données. Pareil ? non, pas vraiment. Il est possible de créer une base de données dans un tablespace mais pas de déplacer le contenu d'une base de données existante dans un autre tablespace en une seule commande. J'avoue que quand j'ai appris cela au début de l'été, je suis tombé des nues. Je ne comprenais pas pourquoi on n'avait pas cette fonctionnalité qui semble découler tout logiquement du reste. J'ai une petite idée du pourquoi maintenant, mais je préfère garder le suspens :)

Donc, voilà. Le but est d'ajouter la fonctionnalité proposée par la commande : ALTER DATABASE nom_base SET TABLESPACE nom_tablespace.

Autant le dire tout de suite, je ne sais pas si je vais arriver réellement à coder cette fonctionnalité. Je ne sais pas non plus si, une fois développée, elle sera acceptée par l'équipe de développement de PostgreSQL. Et en fait, peu importe. Le but est d'apprendre. Il est aussi de montrer comment on peut (facilement) développer une nouvelle fonctionnalité. OK, pour le « facilement », je m'avance certainement un peu :)

Pourquoi ne pas en avoir fait un article pour GLMF ? tout simplement parce que je veux montrer ma progression au jour le jour, pas à pas. Faire un résumé ne me paraissait pas intéressant. Et dans ce cas, seul un blog permet de faire cela « à ma sauce ». Cela ne m'empêche pas de continuer à écrire pour GLMF. D'ailleurs, Denis vient de m'annoncer que « Opérations de maintenance pour PostgreSQL » sera dans le numéro 109 (à paraître fin septembre), et je suis en train de travailler (humm...) sur le prochain.

Bon, alors, prêt à suivre ?

GSoC pgAdmin : pgScript

Deuxième GSoC de pgAdmin, l'intégration de pgScript. Ce dernier est un outil permettant de réaliser de petits scripts mêlés avec des requêtes SQL. En effet, les langages style PL/pgsql demandent forcément la création d'une fonction. Or il se trouve des cas où créer une fonction n'a pas réellement un grand intérêt. Prenons un exemple que je viens tout juste d'avoir sur le canal IRC #postgresqlfr. Une personne a demandé comment modifier automatiquement tous les propriétaires des tables d'un schéma. Il n'existe pas vraiment de solutions en dehors d'exécuter la commande ALTER TABLE sur chaque table. Le plus simple est de créer un petit script Bash ou Python pour cela. Maintenant, il sera aussi possible de créer un script pgscript.

Tout se passe encore une fois dans l'outil de requêtage :

gsoc_pgscript.png

Le script se rédige au même endroit que les requêtes SQL (bloc 1 dans la capture d'écran). Le code que j'ai placé se comprend assez facilement. Je lance la requête sur pg_tables, récupère toutes les lignes dans la variable tables, et parcours cette table dans une boucle while. Oui, je suis malheureusement obligé de faire aussi un count(*) pour récupérer le nombre de résultats, c'est très con et malheureusement c'est pas le seul truc très con dans cet outil. Ensuite je lance un ALTER TABLE pour chaque table récupérée.

Le bloc 2 indique la nouvelle icône qui permettra d'exécuter un pgScript. En cliquant dessus, le script est exécuté et le résultat s'affiche dans le bloc 3.

Voilà, c'est tout simple. Quel intérêt par rapport à Python ou Perl ou $SCRIPTLANGUAGE ? aucune idée. Peut-être plus simple pour l'installation (il suffit d'installer pgAdmin, pas le langage Python, le module pyscopg2? etc). Mais ça fait un langage de plus à apprendre... alors quel intérêt ? J'avoue que je ne comprends pas vraiment. Il me semble bien plus simple de lancer un petit script bash du style :

 psql -AtF ";" -c "select tablename from pg_tables where schemaname='public'" test | while read table
 do
   echo "alter table $table owner to postgres;"
 done | psql test

On pourrait aussi se dire que, après tout, c'est un langage de plus, donc du positif. Pas si sûr... parce que, maintenant, il va falloir le maintenir.

PS : Oui, je sais qu'une réponse plus intéressante pour cet utilisateur est d'utiliser REASSIGN OWNED. C'est d'ailleurs ce que j'ai fait.

Conférence traduc.org chez Parinux

Jean-Philippe nous a fait une jolie conférence mercredi soir (oui, j'ai du retard, c'est rien de le dire). Il a principalement abordé la traduction d'application, avec un joli programme en python, traduisible avec gettext. J'ai bien aimé. Mon seul regret est qu'il n'a pas parlé de la traduction de document, et notamment de Docbook. C'est d'autant plus dommage que c'est le principal travail de traduc.org. Par contre, il a parlé du glossaire de traduc.org et c'est bien. C'est un outil qui devrait avoir bien plus de publicité. Bref, bonne conf. Les slides seront bientôt sur le site de traduc.org.

On a ensuite été mangé dans un restaurant libanais près de l'Opéra. Excellent. Première fois que j'allais dans un resto libanais et c'est une excellente expérience.

« Un monde à nous »

Édouard Baer est étonnant. Dans ce rôle, il n'est plus du tout le dandy habituel et en devient difficilement reconnaissable en papa inquiet, complètement paranoïaque. Chabat quant à lui est super reconnaissable. Il est à mourir de rire dans son rôle de prof d'anglais dictant à ses élèves... Bizarre comme scène d'ailleurs, parce que c'est clairement pas un film drôle. C'est plutôt oppressant, ça rend rapidement paranoiaque. Et paf, la scène avec Chabat où je me pliais de rire (rien que le voir suffit à me faire rire, ça doit être nerveux :) ).

Mais le meilleur de tous, c'est le petit jeune, Anton Balekdjian, le fils du réalisateur. Fils à papa ? loin de là, il est impressionnant. Il est affolant de naturel.

Bref, rien que pour le môme et Edouard Baer, allez-le voir. Et pour ceux qui aiment Chabat, ses dix secondes de présence m'ont bien fait rire.

« Le premier jour du reste de ta vie »

J'ai adoré. Difficile de dire pourquoi en fait. Rob parle d'un petit miracle et je suis vraiment d'accord. Pour Pascale, c'est même providentiel.

Après les avoir cités, je ne sais pas trop quoi dire de plus.

On suit une famille pendant douze ans, mais uniquement sur cinq journées phares de la vie de la famille. On pourrait croire qu'on ne reconnaitra personne, qu'on perdra le fil à chaque transition. Il n'en est rien. On passe d'un membre de la famille à un autre. On voit grandir les enfants, et vieillir grands-parents et parents. Un mariage, des enterrements, des coups de gueule et de l'amour... C'est filmer d'une façon assez déroutante, mais pas désagréable du tout. La bande son est pleine de vieux souvenirs. Tous les acteurs sont excellents, vraiment.

Quitte à me redire, j'ai adoré. À voir absolument.

- page 1 de 51