mardi 31 décembre 2013

systemd: création répertoire dans /run

Nous voulons que notre serveur postgresql utilise un socket situé /run/postgresql.
Mais ce n'est pas ce qui est prévu au niveau de la distribution: /run/postgresql n'existe pas et donc notre serveur se plante.
Créer /run/postgresql par mkdir est d'une utilité toute relative car /run se trouve en mémoire vive et le répertoire aura disparu au prochain démarrage.
Il faudrait donc créer ce répertoire au démarrage.
Voici comment procéder si la distribution utilise systemd.
Dans /usr/lib/tmpfiles.d/, créer un fichier postgresql.conf contenant:

d /run/postgresql 2755 postgres postgres -

Par exemple comme ceci:



(Il reste à appuyer sur CTRL-D pour procéder)

Il faut ensuite s'assurer que le service postgresql sera bien activé au prochain démarrage:

[root@rigel ~]# systemctl enable postgresql.service

Bien sûr: ce est écrit ici peut être utilisé dans d'autre cas.
Il est également possible de créer des fichiers.

Pour plus de détails: man tmpfiles.d 

dimanche 29 décembre 2013

Postgresql upgrade

Lors du passage de Fedora 19 à Fedora 20, postgresql passe de la version 9.2 à la version 9.3
Aussi nous devons procéder manuellement à la migration des données postgresql.
Avant toute chose vérifions que la variable d'environnement PGDATA existe et qu'elle contient ce qui convient:

[toto@rigel ~]$ echo $PGDATA
/var/lib/pgsql/data

Ceci sera le cas si nous avons placé dans /etc/profile.d  un fichier pgsql.sh comme expliqué ici.

Installons postgresql-upgrade (si celui-ci ne l'est pas):

[toto@rigel ~]$ su -
Mot de passe :
[root@rigel ~]# yum install postgresql-upgrade

Nous serons amené à agir en tant qu'utilisateur postgres. Le plus simple pour ce faire est de procéder dans une autre fenêtre.


[toto@rigel ~]$ su -
Mot de passe :
[root@rigel ~]# su - postgres
-bash-4.2$ mv /var/lib/pgsql/data/ /var/lib/pgsql/data_9.2/

Nous avons renommé les anciennes données.
Ensuite (en tant que root):

[root@rigel ~]# postgresql-setup initdb

Puis dans la fenêtre où l'on est postgres:

-bash-4.2$ cp /var/lib/pgsql/data_9.2/pg_hba.conf /var/lib/pgsql/data/
-bash-4.2$ pg_upgrade -b /usr/lib64/pgsql/postgresql-9.2/bin/ -B /usr/bin/ -d data_9.2/ -D data

Tout semble se dérouler à merveille.
Et soudain l'erreur fatale:

lc_collate cluster values do not match:  old "fr_FR.UTF-8", new "fr_FR.utf8"

Il faut recommencer après avoir remplacé utf8 par UTF-8 dans /etc/locale.conf

[root@rigel ~]# sed -i 's/utf8/UTF-8/' /etc/locale.conf
[root@rigel ~]# rm -rf /var/lib/pgsql/data
[root@rigel ~]# postgresql-setup initdb

Cette fois, l'étape suivant ne pose aucun problème.
Ne pas oublier de recopier pg_ident.conf (si celui-ci est utilisé):

-bash-4.2$ cp /var/lib/pgsql/data_9.2/pg_ident.conf /var/lib/pgsql/data/

et aussi d'adapter éventuellement le fichier postgresql.conf suivant par exemple ce qui est expliqué ici.
Reste à démarrer le service:

[root@rigel]# systemctl start postgresql.service

mardi 10 décembre 2013

Le piège de la mise à jour cachée

Pignouf Anatole (matricule 1007) a obtenu au sein de l'entreprise une belle promotion: il passe du service "Relations publiques" au service "Recherche" avec à la clef une augmentation de salaire dont le calcul s'effectuera dorénavant sur base de l'échelle 11r.
Il s'occupera du projet "Lave-linge à pédales", projet porteur puisque synonyme d'économie d'énergie.
Reste à mettre à jour les données informatique via un formulaire libreoffice connecté à la base de données PostgreSQL utilisée par l'entreprise:


Dans la table qui sera mise à jour, "Service" et "Projet" apparaissent sous forme de code, mais l'utilisation de listes déroulantes fait que ces codes sont transparents pour l'utilisateur.
Ce qui est affiché à l'écran sous forme d'un seul formulaire est en fait composé d'un formulaire maître "Sélection" et de deux sous-formulaires: "Employés" et "Gestion".


La table "sélection" source de données de "Sélection" contient un matricule qui est sans arrêt mis à jour et qui sert à appeler le dossier désiré. La façon dont tout cela fonctionne est expliqué ici.
Après avoir procédé, la personne chargée d'actualiser les données informatiques en ce qui concerne "Service" et "Projet", clique sur la petite disquette (qui a pris des couleurs) de la barre d'outils:


  ce qui donne:


Il constate alors que depuis un autre poste de travail les données "Ancienneté" et "Échelle de traitement" ont été modifiées (ce qui montre d'ailleurs que chaque enregistrement s'accompagne d'une relecture des données).
Parfait: de toute façon ce n'était pas son job de le faire!
Supposons maintenant que au lieu de cliquer sur l'icône "disquette", l'encodeur appuie sur "Enter" autant de fois qu'il le faut (3 fois) pour amener le focus sur la zone de saisie "matricule". Dès l'instant où le focus quitte le sous-formulaire "Gestion", les données s'enregistrent et nous arrivons à ceci:


L'ancienneté affichée est bien 9, par contre en ce qui concerne l'échelle de traitement on a 12.
Et voilà, le piège s'est refermé! Le piège de la mise à jour cachée!
L'échelle de traitement a été dans un premier temps portée à la valeur '11r' depuis un autre poste, mais ensuite  elle est revenue à la valeur '12'.
Mais pour notre encodeur l'échelle de traitement a toujours été à '12' et il n'a touché à rien. il s'agit donc bien d'un piège et d'une mise à jour cachée.
D'autre part, la personne qui a porté l'échelle de traitement à la valeur '11r' croit avoir fait son travail et elle n'y reviendra plus.
Pour éviter le piège, on peut choisir "Tabulation = non" au niveau des propriétés du contrôle:


ce qui n'empêchera pas que l'on puisse mettre à jour la donnée, à moins de choisir "En lecture seule = OUI".

NB: le piège a été testé avec une grandeur de type character ce qui est le cas de l'échelle de traitement.

samedi 7 décembre 2013

Transfert de colonne

La table "employés" telle que considérée jusqu'à présent (par exemple ici) présente une anomalie, une atteinte à la normalité: la colonne "service" (qui donne sous forme d'un code le service auquel appartient l'employé) est la seule qui contienne des données se rapportant à l'entreprise.

Transfert vers une nouvelle table

On peut envisager de créer une nouvelle table destinée à contenir les données qui sont à la fois relatives à l'employé et à l'entreprise :

CREATE TABLE gestion (
    matricule INTEGER PRIMARY KEY REFERENCES employés,
    service CHAR(2) REFERENCES services,
    projet CHAR(4) REFERENCES projets,
    barème CHAR(4),
    ancienneté SMALLINT
);

Étant donné les relations  d'intégrité référentielle qui figurent dans l'instruction ci-dessus, la table "projets" destinée à contenir en clair le nom des projets doit bien sûr avoir été préalablement créée:

CREATE TABLE projets (
    projet_id CHAR(4) PRIMARY KEY,
    dénomination CHAR VARYING(25)
);

Avant de supprimer la colonne "service" de la table employés, il faut d'abord en transférer les données vers la nouvelle table gestion.
Nous pouvons procéder avec cette instruction SQL:

insert into gestion (matricule, service)
 select matricule, service
 from employés
 order by matricule
 ;

ou dans un terminal psql (nous utilisons PostgreSQL) avec la méta-commande \copy:

\copy employés (matricule, service) to matr-serv
\copy gestion (matricule, service) from matr-serv

Se connectant  en tant que super-utilisateur postgresql, nous pouvons utiliser la commande COPY:


Notons que le fichier de destination sera créé sur le serveur (même si sur la machine client existe un /var/lib/postgresql).
Ce fichier aura comme propriétaire postgres: il faut donc indiquer un chemin conduisant là où postgres a le droit d'écrire. 
Si le volume de données à traiter est très important, cette solution est à privilégier puisqu'elle évite qu'un nombre de données important ne transite par la connexion client/serveur. 

Transfert vers une table existante

Nous avons transféré le contenu de la colonne "service"  depuis la table "employés" vers une table nouvellement créée.
Nous pouvons aussi considérer que la table cible (gestion) existe déjà.
Ajoutons-y la colonne service:

ALTER TABLE gestion
ADD COLUMN service CHAR(2) REFERENCES services
;

Les relations d'intégrité référentielle existantes impliquent que tous les matricules de la table "gestion" correspondent à des matricules de la table "employés", mais l'inverse n'est pas nécessairement vrai.
Il convient tout d'abord d'insérer dans "gestion" les matricules qui n'y existent pas:

insert into gestion (matricule, service)
 select matricule, service
 from employés
 where not exists
 (select *
  from gestion
  where gestion.matricule = employés.matricule)
 order by matricule
 ;

Les rangées nouvellement insérées dans la table gestion possèdent la bonne valeur de "service" (celle de la table employés). Pour toutes les autres rangées, "service" a la valeur NULL.
Il faut donc effectuer une mise à jour, avec par exemple l'instruction SQL:

UPDATE gestion 
SET service = (SELECT service
FROM employés
WHERE employés.matricule = gestion.matricule)
 ;

Mais alors toutes les rangées (dont les nouvelles) seraient concernées par la mise à jour.
Aussi nous préférons agir plus finement avec cette instruction:

UPDATE gestion
SET service = employés.service
FROM employés
WHERE employés.matricule = gestion.matricule
AND   employés.service <> gestion.service

;

(Remarque: l'utilisation de la clause FROM dans une instruction UPDATE n'est pas autorisée dans le langage SQL standard)

Procédons (après avoir mis l'instruction SQL dans le fichier update-gestion.sql):


Une seule rangée mise à jour! Il y a un problème: nous sommes tombés dans le piège du null.
La seule mise à jour effectuée est due certainement à ce que "service" a changé dans la table "employés" pour une des rangées insérées.
Recommençons après avoir modifié l'instruction pour éviter le piège:



Tout est maintenant en ordre: il reste à supprimer la colonne "service" de la table "employés":

ALTER TABLE employés
DROP service
;

Adaptation des formulaires libreoffice

Évidemment dès cet instant le nom du service ne s'affiche plus dans les formulaires que nous avons considérés  ici et :


C'est normal puisque la zone de liste (listbox) qui fournit en clair le nom du service dépend du sous-formulaire "Employés" dont la source de données est la table "employés":


Nous allons régler le problème en cliquant droit sur l'icône du formulaire "Employés", en vue d'y rattacher un nouveau formulaire



que nous appellerons "Gestion"


La source de données en sera la table "gestion" et le lien avec "Employés" sera basé sur "matricule"


Il reste à effectuer un glisser-déposer des icônes qui conviennent vers le formulaire "Gestion":


Et voilà: