<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Christophe Nowicki &#187; PostgreSQL</title>
	<atom:link href="http://www.csquad.org/tag/postgresql/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.csquad.org</link>
	<description>Just for fun</description>
	<lastBuildDate>Thu, 29 Jul 2010 19:42:40 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Système de synchronisation conditionnel  pour PostgreSQL</title>
		<link>http://www.csquad.org/2008/05/30/systeme-de-synchronisation-conditionnel-pour-postgresql/</link>
		<comments>http://www.csquad.org/2008/05/30/systeme-de-synchronisation-conditionnel-pour-postgresql/#comments</comments>
		<pubDate>Fri, 30 May 2008 13:30:33 +0000</pubDate>
		<dc:creator>cscm</dc:creator>
				<category><![CDATA[Work]]></category>
		<category><![CDATA[MAVISE]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[PostgreSQL]]></category>

		<guid isPermaLink="false">http://www.csquad.org/?p=54</guid>
		<description><![CDATA[Il existe des outils tel que SQLSync permettant de synchroniser les données d&#8217;une base PostgreSQL.  Mais cette outil, ne permet pas de faire une copie partiel des données.
Dans la plus part des cas, il faut programmer la synchronisation soit même, l&#8217;objectif de mon article est de vous proposer une solution simple et  élégante.
La [...]]]></description>
			<content:encoded><![CDATA[<p>Il existe des outils tel que <a href="http://silvercoders.com/index.php?page=sqlsync">SQLSync</a> permettant de synchroniser les données d&#8217;une base PostgreSQL.  Mais cette outil, ne permet pas de faire une copie partiel des données.<br />
Dans la plus part des cas, il faut programmer la synchronisation soit même, l&#8217;objectif de mon article est de vous proposer une solution simple et  élégante.</p>
<h3>La problèmatique</h3>
<table>
<tr>
<td><img src='/wp-content/sync.png' alt='' /></td>
<td valign='top'>Des données sensibles se trouvent dans la base principale et il ne faut synchoniser qu&#8217;une partie de ces données vers les autres bases. </p>
<p>L&#8217;architecture dispose des caractèristiques suivantes :</p>
<ul>
<li>Le schèma est identique sur chaque base ;</li>
<li>Les opèrations d&#8217;écriture se font seulement sur la base principale ;</li>
<li>Les bases sont accessibles en réseau.</li>
</ul>
</td>
</tr>
</table>
<h3>La solution</h3>
<p>
Pour résoudre le problème, j&#8217;ai testé plusieurs approches avant de retenir l&#8217;utilisation d&#8217;un fonction de hachage dont voici le principe :</p>
<ul>
<li>chaque table doit disposer d&#8217;un identifiant unique. (champ id de type SERIAL) ;</li>
<li>un programme récupère le couple <em> id</em> et la sum de <em>hachage</em> pour chaque table de chaque base qui doit être synchroniseés ; </li>
<li>il compare le couple et si celui-ci est diffèrent alors les données sont mise à jour.</li>
</ul>
<h3>Implementation au niveau de la base de données</h3>
</p>
<p>
 Le calcul de la somme de hachage se fait un niveau de la base de données à l&#8217;aide d&#8217;un fonction :</p>
<blockquote>
<pre>
SELECT id,md5 FROM get_table_md5('nom_de_la_table');
 id |               md5
----+----------------------------------
  4 | 9f3bcd2fae528244669613ae0466cc3c
  5 | 1f3bcd2fa24528244669613ae66cc3czd
...
(42 row)
</pre>
</blockquote>
<p>Voici le code de la function PL/Perl:</p>
<blockquote>
<pre>
CREATE LANGUAGE plperl;
CREATE TYPE table_md5 AS (id INTEGER, md5 TEXT);
CREATE OR REPLACE FUNCTION get_table_md5(varchar) RETURNS SETOF table_md5 AS $$
    my ($rv, $status, $nrows, $row);
    # Get Table OID
    $rv = spi_exec_query("SELECT c.oid AS oid FROM pg_catalog.pg_class c
        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
        WHERE c.relname LIKE '$_[0]';");
    $status = $rv->{status};
    $nrows = $rv->{processed};
    return undef if ($nrows != 1);
    my $oid = $rv->{rows}[0]->{oid};
    # Get Table attributs
    $rv = spi_exec_query("SELECT attname,atttypid FROM pg_catalog.pg_attribute a
        WHERE a.attrelid = $oid AND a.attnum > 0 AND NOT a.attisdropped");
    $status = $rv->{status};
    $nrows = $rv->{processed};
    return undef if ($nrows < 1);
    my $atts;
    my $atts_not_null;
    foreach my $rn (0 .. $nrows - 1) {
        if ($rv->{rows}[$rn]->{atttypid} == 16) {
            $atts .= " (CASE WHEN $rv->{rows}[$rn]->{attname} THEN 't' ELSE 'f' END) AS $rv->{rows}[$rn]->{attname}";
        } else {
            $atts .= " $rv->{rows}[$rn]->{attname}::text";
        }
        $atts_not_null .= " (CASE WHEN $rv->{rows}[$rn]->{attname} IS NULL THEN '' ELSE $rv->{rows}[$rn]->{attname} END)";
        $atts .= ',' if ($rn != $nrows - 1);
        $atts_not_null .= ' || ' if ($rn != $nrows - 1);
    }
    # Calc MD5
    my $sql = "SELECT id, MD5($atts_not_null) AS md5 FROM (SELECT $atts FROM $_[0]) AS $_[0];";
    $rv = spi_exec_query($sql);
    $nrows = $rv->{processed};
    foreach my $rn (0 .. $nrows - 1) {
        return_next({
            id  => $rv->{rows}[$rn]->{id},
            md5 => $rv->{rows}[$rn]->{md5}
        });
    }
    return undef;
$$ LANGUAGE plperl;
</pre>
</blockquote>
<p>Pour utiliser cette fonction vous avez besoin du support du language de programmation PL/Perl dans Postgres, celui-ci se trouve dans le paquet Debian :  <em>postgresql-plperl-<version></version></em>
</p>
<h3>La gestion des conditions</h3>
<p>
Le choix des données à synchroniser se fait individuellement pour chaque table à l&#8217;aide de la clause <em>WHERE</em> de la manère suivante :</p>
<blockquote>
<pre>
SELECT id,md5 FROM get_table_md5('nom_de_la_table') WHERE id IN(SELECT id FROM nom_de_la_table WHERE nom_de_la_table.champ LIKE 'sync');
 id |               md5
----+----------------------------------
  1 | fdd56eabd4bb997e453e33f0022d46c1
(1 row)
</pre>
</blockquote>
<h3>Implementation du script de synchronisation</h3>
<p>
La synchronisation des données peut être réalisée à l&#8217;aide de n&#8217;importe quel language de programmation,<br />
il suffit juste de disposer d&#8217;un accèss à la base de données.
</p>
<p>
Voici un <a href="http://svn.csquad.org/misc/synchro_data/synchro_data.php">exemple de script</a> écrit par  <em>Emmanuel Saracco</em> en PHP5 qui se repose sur les modules <a href="http://pear.php.net">PEAR</a> suivants :</p>
<ul>
<li><a href="http://pear.php.net/package/DB">DB</a> : pour l&#8217;accès à la base de donnée.</li>
<li><a href="http://pear.php.net/package/Console_Getopt">Console_Getopt</a> : pour la gestion de la ligne de commande.</li>
</ul>
<p>Ce script nécessite un fichier de configuration, en voici un <a href="http://svn.csquad.org/misc/synchro_data/synchro_data.ini">exemple</a>.<br />
Ce fichier contiens la configuration des diffèrentes base de données et les conditions sous-forme de clauses where.
</p>
<h3>Conclusion</h3>
<p>Cette méthode de synchronisation fonctionne, elle est en production chez un client pour une base de données de plus de 100 tables, elle permet de synchronisé le contenu d&#8217;une base vers deux autres.<br />
Les avantages de cette méthode sont les suivants :</p>
<ul>
<li>le fait de pouvoir choisir le contenu de la clause <em>where</em> permet de réalisé des <em>régles de synchronisation très complexes</em> ;</li>
<li>les performances sont très bonnes car les transfert réseau sont <em>limités</em> ;</li>
<li>il est possible d&#8217;écrire le script de synchronisation avec n&#8217;importe quel language de programmation.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.csquad.org/2008/05/30/systeme-de-synchronisation-conditionnel-pour-postgresql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
