Récupérer les binaires récents ici
Personnellement j'ai décompressé l'archive dans un dossier créé pour l'occasion, sous
/usr/local/sapdb/interfaces/python et, par conséquent réglé mon PYTHONPATH ainsi:
export PYTHONPATH=".:/usr/lib/python2.2:$SAPDB/interfaces/python"
J'ai récupéré une base d'adresses courriel qui tourne sous BerkeleyDB et j'ai chargé une table sous MaxDB avec, et j'utilise l'API Python pour m'y connecter et effectuer différentes opérations dessus. La table s'appelle ADRESSES, les deux champs "nom" et "adresse". J'ai choisi d'utiliser l'API car on peut considérer que c'est plus "portable" d'un SGBD à un autre.
On trouve les spécifications des méthodes de l'API dans le fichier sapdb/dbapi.py du dossier
principal cité ci-dessus. Car c'est l'API Python que j'utilise, mais MaxDB propose un module
sapdb.sql qui a l'air sympa aussi bien documenté dans la doc téléchargeable (voir 1 pour les
liens)
Les spécifications de la DB API Python peuvent se lire ici
Ce ne sera pas précisé dans les lignes qui suivent mais il faut les imports suivants:
import sapdb.dbapi # api python 2.0 import sapdb.sql # pour les erreurs
Une ligne suffit:
session = sapdb.dbapi.connect (user, password, database, host)
Gaffe à la casse sur le nom de l'utilisateur. L'utilisation de string.upper () fait des
miracles...
On peu passer d'autres paramètres de connexion au constructeur:
session = sapdb.dbapi.connect (user, password, database, host,isolation='15')
Jeter un oeil à la documentation fournie avec MaxDB (Interfaces, Python module, Method connect)
On ferme la session en appelant sa méthode close ():
session.close ()
On doit d'abord ouvrir un curseur:
curseur = session.cursor ()
Puis envoyer l'ordre à exécuter. Il est possible d'envoyer une seule instruction, ou une série.
On fermera le curseur avec sa méthode close ().
Voici deux manières possibles pour envoyer une instruction simple:
execute () par une moyen ou un autre:
try:
curseur.execute ("DELETE FROM JM.ADRESSES WHERE adresse = '%s'" % adresse)
except sapdb.dbapi.ProgrammingError, e:
print 'Erreur programme (position %s): %s' % (e.errorPos, e.message)
session.commit ()
curseur.close ()
La valeur à passer est contenue dans la variable adresse de la chaine de formatage.
L'exception ProgrammingError est levée lorsque l'authentification user/password est mauvaise
pour l'action demandée. En effet mon user par défaut n'a qu'un droit de SELECT.
paramstyle qui donne
la syntaxe du passage. Elle est définie en tant que 'named' (voir les spécifications de l'API pour
les précisions concernant les différents paramstyle) dans le module sapdb.dbapi.
try:
curseur.execute ("INSERT INTO JM.ADRESSES VALUES (:nom, :adresse)", (nom, adresse))
except sapdb.sql.SQLError, e:
print 'Erreur SQL (position %s): %s' % (e.errorPos, e.message)
session.commit ()
curseur.close ()
Là on donne un tuple, une paire dans ce cas, en paramètre supplémentaire à la méthode
execute()
curseur = self.cursor ()
try:
curseur.executemany ("CALL pc_insert(?, ?)", loadList)
except sapdb.sql.SQLError, e:
print 'Erreur SQL (position %s): %s' % (e.errorPos, e.message)
self.commit ()
curseur.close ()
D'une pierre, deux coups, on voit là qu'on peut demander l'exécution d'une procédure (voir cette
procédure à 5.5) avec la méthode execute (). La variable loadList que je
passe en paramètre à executemany () est une séquence (liste) de tuples (paires nom, adresse)
qui sont envoyés successivement à la procédure stockée.
On appelle la méthode callproc () du curseur:
curseur.callproc ('pc_insert', paireArgs)
où paireArgs est une paire de variables (nom, adresse).
Il y a trois méthodes pour la récupération d'un résultat:
fetchall ()
fetchone ()
fetchmany ()
Les noms indiquent véritablement ce que font les méthodes donc on pourra récupérer un résultat (on supposera un curseur ouvert) avec:
fp = file (fichier, "w")
resultat = curseur.fetchall ()
for tuple in resultat:
fp.write (tuple[1])
fp.write ('\t')
fp.write (tuple[0])
fp.write ('\n')
Ce qui écrira dans le fichier ouvert avec file () dont on aura obtenu fp.
Ou bien:
fp = file (fichier, "w")
while 1:
row = curseur.fetchone ()
if row == None:
break
fp.write (row[1])
fp.write ('\t')
fp.write (row[0])
fp.write ('\n')
fetchone () va chercher plusieurs enregistrements par passe, donc les performances devraient
être équivalentes à fetchmany ().
curs1 = session.cursor ()
curs1.execute ("SELECT * FROM PRODUCTEUR FOR UPDATE OF NOMPRODUCTEUR,TAILLEPRODUCTEUR")
originel = curs1._Cursor__cursor
originel.setFetchSize (1)
nomCurseur = originel.cursorName ()
originel.first ()
curs2 = session.cursor ()
curs2.execute ("UPDATE PRODUCTEUR \
SET NOMPRODUCTEUR='%s', TAILLEPRODUCTEUR=%.3f \
WHERE CURRENT OF %s" % \
(nom,taille,nomCurseur))
Alors, comment ça marche ? Le premier curseur (curs1) est un objet de classe Cursor. A l'exécution
de sa méthode execute(), l'implémentation de la dbapi génère un objet de classe
SapDB_ResultSet, qui est l'attribut __cursor du curseur. Étant un attribut privé au sens
de python, on l'adresse avec une syntaxe _Cursor__cursor. En fait les méthodes setFetchSize() et
cursorName() ne sont pas (encore ?) documentées dans la doc MaxDB mais peuvent être atteintes par:
dir(orig) orig.cursorName.__doc__Il faut obligatoirement fixer la taille du déplacement du curseur originel à 1 par setFetchSize() ou l'attribut fetchSize du curseur de résultat.
Le module dbapi permet de faire des conversions à la volée à l'aide d'un dictionnaire, mais cette
possibilité ne semble pas documentée dans la Python Database API Specification.
Voilà comment on peut procéder, pour traduire une date en format français (le format de date
original de SapDB est INTERNAL, c'est à dire AAAAMMJJ).
# on implémente une fonction traductrice
def dateUS2FR (sapdbdate):
return "%s-%s-%s" % (str (sapdbDate[-2:]), str (sapdbDate[4:6]), str (sapdbDate[0:4]))
# on définit un dictionnaire traducteur pour le type sql DATE
traducteur = {'Date':dateUS2FR}
# on initialise le traducteur, par une méthode de l'objet Connection
session.setTypeTranslation (traducteur)
Et puis c'est tout. Les dates seront formatées selon la fonction de conversion. On peut définir plusieurs fonctions dans le dictionnaire. Voir le code du module pour ça.
Mais bon, on peut implémenter une fonction sous MaxDB (version supérieure à 7.5 donc) pour le faire: voir 5.6
fetchmany (). Il y a un bug;
curseur.rowcount.
jean-michel OLTRA 2004-07-06