Difference between revisions of "SQLite/fr"

From Free Pascal wiki
Jump to navigationJump to search
 
(21 intermediate revisions by 3 users not shown)
Line 10: Line 10:
  
 
=== Accès direct à SQLite ===
 
=== Accès direct à SQLite ===
Vous pouvez utiliser une manière simple pour vous connecter à SQLite avec Lazarus. Les SQLite Data Access Components (LiteDAC) sont une bibliothèque de composants qui fournissent une connectivité native à SQLite depuis Lazarus (et Free Pasacal) sur Windows, Mac OS X, iOs, Androïd, Linux et FreeBSD à la fois pour les plates-formes 32-bit et 64-bit. LiteDAC est conçu pour les programmeurs pour développer des applications de bases de données SQLite de bureau ou mobiles réellement multi plate-forme sans nécessité de déployer des bibliothèque supplémentaires.
+
Vous pouvez utiliser une manière simple pour vous connecter à SQLite avec Lazarus. Les SQLite Data Access Components (LiteDAC) sont une bibliothèque de composants qui fournissent une connectivité native à SQLite depuis Lazarus (et Free Pasacal) sur Windows, Mac OS X, iOs, Androïd, Linux et FreeBSD à la fois pour les plates-formes 32-bit et 64-bit. LiteDAC est conçu pour les programmeurs pour développer des applications de bases de données SQLite de bureau ou mobiles réellement multi plate-forme sans nécessité de déployer des bibliothèques supplémentaires.
  
 
Vous pouvez télécharger une version d'essai du produit commercial depuis [https://www.devart.com/litedac/download.html Composants Lazarus].
 
Vous pouvez télécharger une version d'essai du produit commercial depuis [https://www.devart.com/litedac/download.html Composants Lazarus].
Line 31: Line 31:
 
==== sqlite3backup ====
 
==== sqlite3backup ====
 
sqlite3backup est une unité avec FPC (pas dasn Lazarus pais peut être utilisé par programmation) qui fournit une fonctionnalité de sauvegarde/restauration pour SQLite3. Elle utilise sqlite3conn de SQLdb.
 
sqlite3backup est une unité avec FPC (pas dasn Lazarus pais peut être utilisé par programmation) qui fournit une fonctionnalité de sauvegarde/restauration pour SQLite3. Elle utilise sqlite3conn de SQLdb.
 +
 +
===Cas de Debian Jessie===
 +
(Décrit depuis Lazarus 1.8RC3 + FPC 3.0.3, à vérifier pour d'autres versions)
 +
L'installation du paquet sqlite3laz.0.4 depuis la Debian Jessie pose problème. En effet, lors de la reconstruction de l'EDI, une erreur générique de liaison bloquante survient alors. Il s'avère que le fichier /usr/share/fpcsrc/3.0.3/packages/sqlite/src/sqlite3.inc attend la nom de bibliothèque libsqlite3.so mais la Debian Jessie ne propose que le lien symbolique libsqlite3.so.0.
 +
Deux solutions :
 +
* soit changer le nom dans le source du fichier .inc, mais cela est risqué et devra être fait à nouveau à chaque réinstallation de Lazarus et induit une différence non pertinente avec la version officielle.
 +
* soit créer un lien symbolique avec ce nom dans le répertoire des bibliothèques (/usr/lib/x86_64-linux-gnu pour mon cas).
 +
sudo ln -s libsqlite3.so.0 libsqlite3.so
 +
La contrainte réside dans le déploiement, il faut en effet s'assurer que le lien ou nom de fichier direct existe sur le système cible et au besoin le créer.
 +
 +
Cela résoud aussi les problèmes rencontrés lors de l'installation de la bibliotèque ZeosLib qui provoquait la même erreur.
  
 
=== Zeos ===
 
=== Zeos ===
Line 53: Line 64:
  
 
== Utilisation des composants SQLdb avec SQLite ==
 
== Utilisation des composants SQLdb avec SQLite ==
These instructions are focused on SQLDB (the TSQLite3Connection) specifics for SQLite. For a general overview, have a look at [[SqlDBHowto|SqlDBHowto]] which has some useful information about the SQLdb components.
+
Ces directives font le point sur les spécificités de SQLdb pour SQLDB (le composant TSQLite3Connection). Pour une vue d'ensemble, jetez un œil sur [[SqlDBHowto/fr|SqlDBHowto]] qui continet des informations intéressantes sur les composants SQLdb.
 
 
See [[SQLdb_Tutorial1]] for a tutorial on creating a GUI database-enabled program that is written for SQLite/SQLDB (as well as for Firebird/SQLDB, PostgreSQL/SQLDB, basically any RDBMS SQLDB supports).
 
  
We will use a combination of three components from the Lazarus SQLdb tab: TSQLite3Connection, TSQLTransaction and TSQLQuery. The TSQLQuery acts as our TDataset; in the simplest case it just represents one of our tables. For the sake of simplicity: make sure you already have an existing SQLite database file and don't need to create a new one now.
+
Voyez le [[SQLdb_Tutorial1/fr|SQLdb_Tutorial1]] pour un tutoriel sur la création d'un programme IHM relié aux données qui est écrit pour SQLite/SQLDB (et tout aussi bien pour Firebird/SQLDB, PostgreSQL/SQLDB, et d'une façon générale pour tout SGBDR que SQLDB prend en charge).
TSQLite3Connection can be found in the ''sqlite3conn'' unit, if you want to declare it yourself or are working in FreePascal.
 
  
The three components are connected with each other as usual: In the TSQLQuery set the properties Database and Transaction, in the TSQLTransaction set the property Database. There is not much to do in the Transaction and Connection components, most of the interesting things will be done in the TSQLQuery. Configure the components as follows:
+
Nous utiliserons une combinaison de trois composants dans l'onglet SQLdb : TSQLite3Connection, TSQLTransaction et TSQLQuery. Le TSQLQuery agit comme notre TDataSet ; dans le cas le plus simple il représente juste une de nos tables. Par simplicité, assurez-vous d'avoir une base de données SQLite et de ne pas avoir besoin d'en créer une maintenant. TSQLite3Connection peut être trouvé dans l'unité ''sqlite3conn'', si vous voulez le déclarez vous-mêmes ou si vous travaillez avec FreePascal.
  
TSQLite3Connection:
+
Les trois composants sont connectés entre eux comme d'habitude : dans l'ensemble TSQLQuery, définissez les propriétés Database et Transaction, dans le TSQLTransaction, définissez la propriété Database. Il n'y a pas grand chose d'autre à faire dans les composants Transaction et Connection, les choses les plus intéressantes seront faites dans le TSQLQuery. Configurez les composants comme suit :
* DatabaseName: Set this property to the file name (absolute path!) of your SQLite file. Unfortunately, you cannot simply use a relative path that works unchanged at designtime and at runtime ***is this still true? Can't you just copy the db file in a post-build shell script or symlink it?***. You should make sure that at application start the correct path to the file is always set programmatically, no matter what it contained at designtime.
 
  
Note: To set the full library path (if you place your sqlite dll/so/dylib in a place where the OS won't find it, like the application directory on Linux/OSX), you can set the ''SQLiteLibraryName'' property (BEFORE any connection is established e.g. in the OnCreate event of the main form), like this:
+
TSQLite3Connection :
<syntaxhighlight>
+
* DatabaseName : mettez-y le nom (avec un chemin absolu!) de votre fichier SQLite. Malheureusement, vous ne pouvez pas simplement utiliser un chemin relatif qui resterait inchangé à la conception et à l'exécution *** est-ce toujours vrai ? Ne pouvez-vous pas seulement copier le fichier de base de données à l'aide d'un script post-construction ou définir un lien symbolique ? ***. Vous devrez vous assurer que quand l'application démarre, le chemin vers le fichier est défini correctement par programmation, sans tenir compte de la valeur de conception.
 +
Note : Pour définir le chemin d'accès complet à la bibliothèque (si vous placez votre dll/so/dylib de SQLite où votre système ne le trouvera pas, telle que le dossier de l'application dans Linux ou OS/X), vous pouvez employer la propriété ''SQLiteLibraryName'' (avant qu'une connexion ne soit établie p.ex. dans l'événement OnCreate de la fiche principale), comme ceci :
 +
<syntaxhighlight lang=pascal>
 
SQLiteLibraryName:='./sqlite3.so';
 
SQLiteLibraryName:='./sqlite3.so';
 
</syntaxhighlight>
 
</syntaxhighlight>
  
TSQLQuery:
+
TSQLQuery :
* SQL: Set it to some simple select query on one of your tables. For example, if you have a table 'foo' and want this dataset to represent this table then just use the following: <syntaxhighlight lang="SQL">SELECT * FROM foo</syntaxhighlight>
+
* SQL : placez-y une simple rerquête SELECT sur l'une de vos tables. P.ex., si vous avez une table 'foo' et voulez que le DataSet représente cette table, mettez-y seulement : <syntaxhighlight lang="SQL">SELECT * FROM foo</syntaxhighlight>
  
* Active: Set this to True from within the IDE to test whether it is all set up correctly. This will also automatically activate the transaction and the connection objects. If you receive an error then either the DatabaseName of the connection is not correct or the SQL query is wrong. Later, when we are done adding the fields (see below) set them all to inactive again, we don't want the IDE to lock the SQLite database (single user!) when testing the application.
+
* Active : Mettez-la à True depuis l'EDI pour voir si tout marche correctement. Cela active automatiquement les objets transaction et connexion. Si vous recevez une erreur, soit le Databasename de la connection n'est pas correct ou la requête SQL est fausse. Plus tard, lorsque nous finissons d'ajouter les champs (voir ci-dessous), tous les objets sont de nouveau inactifs, nous ne voulons pas que l'IDE verrouille la base de données SQLite (un seul utilisateur!) Lors du test de l'application.
  
* ''Probably not necessary for proper operation - will need to be checked (June 2012)'' Now we can add Fields to our TSQLQuery. While the components are still set to active do a right click and "edit fields...". Click the "+" button and add fields. It will list all fields your SQL query returned. Add every field you will need, you can also add lookup fields here; in this case just make sure you have already defined all needed fields in the other datasets before you start adding lookup fields that refer to them. If your table has many columns and you don't need them all you can just leave them out, you can also make your SQL a bit more specific.  
+
* ''Sans doute pas nécessaire pour une opération propre - devra être contrôlé (juin 2012)'' Maintenant nous pouvons ajouter des champs à notre TSQLQuery. Pendant que le composant est toujours actif , cliquez-droit et "Editer les champs..." . Cliquez sur le le bouton "+" et ajoutez les champs. Cela ajoutera tous les champs que votre requêtes SQL a retourné. Ajouté chaque chaque dont vous avez besoin, vous pouvez aussi ajouter des champs de référence (''lookup fields'') ici ; dans ce cas assurez d'avoir ajouter tous les champs requis dans les autres DataSets avant d'ajouter les champs qui leur feront référence. Si votre table a des champs qui ne vous intéresse pas, vous pouvez les enlever, vous pouvez aussi rendre votre requête un peu plus spécifique (remplacez SELECT * par un SELECT avec la liste des champs voulus).
  
* In your code you need to call SQLQuery.ApplyUpdates and SQLTransaction.Commit, TSQLQuery.AfterPost and AfterInsert events are a good place for this when using it with data aware controls but of course you can also postpone these calls to a later time. If you don't call them, the database will not be updated.
+
* Dans votre code, vous avez besoin d'appeler SQLQuery.ApplyUpdates et SQLTransaction.Commit, les événements TSQLQuery.AfterPost et AfterInsert sont les bons endroits pour cela quand vous utilisez des contrôles sensibles aux données mais bien sûr, vous pouvez aussi remettre à plus tard ces appels. Si vous ne les appelez pas, la base de données ne sera pas mise à jour.
  
* "Database is locked": The IDE might still be locking the database (SQLite is a single user database), you probably forgot to set the components to inactive and disconnected again after you were done defining all the fields of your TSQLQuery objects. Use the Form's OnCreate event to set the path and activate the objects at runtime only. Most of the things you set in the TSQLQuery from within the IDE don't require (and some don't even allow) them to be active at design time, the only exception is defining the fields where it wants to read the table design, so inactive at design time should be the normal state.
+
* Database is locked" : L'EDI pourrait encore bloquer la base de données (SQLite est une base de données mono-utilisateur), vous avez sans doute oublié de mettre les composants à inactif et de vous déconnecter après avoir défini tous les champs de vos objets TSQLQuery. Utilisez l'événement OnCreate de la fiche pour définir le chemin d'accès et activer les objets à l'exécution seulement. La plupart des choses que vous réglez dans TSQLQuery depuis l'EDI ne nécessitent pas (et même ne le permettent pas pour certaines) qu'il soit actif en conception, la seule exception est quand vous voulez lire la structure de la table, ainsi l'état normal en conception est inactif.
  
* Your tables should all have a primary key and you must make sure that the corresponding field has pfInKey and nothing else in its PoviderFlags (these flags control how and where the field is used when automatically constructing the update and delete queries).
+
* Vos tables devraient toutes avoir une clé primaire et vous devez vous assurez que les champs correspondants ont pfInKey et rien d'autre dans leurs PoviderFlags (ces drapeaux contrôlent comment et où le champ est utilisé en cas de construction automatique de requêtes de mise à jour et de suppression).
  
* If you are using lookup fields
+
* Si vous utilisez des champs de référence :
** make sure the ProviderFlags for the lookup field is completely empty so it won't attempt to use its name in an update query. The lookup field itself is not a data field, it only acts on the value of another field, the corresponding key field, and only this key field will later be used in the update queries. You can set the key field to hidden because usually you don't want to see it in your DBGrid but it needs to be defined.
+
** Assurez-vous que le ProviderFlags pour le champ de référence est complétement vide, comme ça, son nom ne sera pas utilisé dans une requête de mise à jour. Le champ de référence lui-même n'est pas un champ de données, il agit seulement sur la valeur d'un autre champ, le champ clé correspondant, et seulement ce champ clé sera plus tard utilisé dans les requêtes de mise à jour. Vous pouvez masquer le champ clé car la plupart du temps vous ne voudrez pas le voir dans votre interface utilisateur mais il doit être défini.
** LookupCache must be set to True. At the time of this writing for some reason the lookup field will not display anything otherwise (but still work) and strangely the exact opposite is the case when working with the TSQLite3Dataset or other TXXXDataset components, here it must be set to False. I'm not yet sure whether this is intended behavior or a bug.
+
** LookupCache doit être mise à True. Au moment de la rédaction, pour certaines raisons, le champ Lookup n'affichera rien d'autre (mais fonctionnera encore) et étrangement à l'exact opposé du TSQLite3Dataset ou d'autres composants TXXXDataset pour lesquels elle doit être mise à False. Je ne suis pas encore sûr de savoir si cela est un comportement intentionnel ou un bug.
  
* Usually with simple tables you won't need to set any of the InsertSQL, UpdateSQL and DeleteSQL properties, just leave them empty. If you have the ProviderFlags of all your fields set correctly it should be able to create the needed SQL on the fly. For more details on InsertSQL, UpdateSQL and DeleteSQL, see [[Working_With_TSQLQuery#TSQLQuery.InsertSQL.2C_TSQLQuery.UpdateSQL_and_TSQLQuery.DeleteSQL:_Basic_Use_of_Parameters]].
+
* Habituellement avec de simples tables, vous n'aurez besoin de définir aucune des propriétés InsertSQL, UpdateSQL et DeleteSQL, il suffit de les laisser vides. Si vous avez défini correctement les propriétés ProviderFlags de tous vos champs, les requêtes seront créées à la volée. Pour plus de détails sur InsertSQL, UpdateSQL et DeleteSQL, voyez [[http://wiki.lazarus.freepascal.org/Working_With_TSQLQuery/fr#InsertSQL.2C_UpdateSQL_et_DeleteSQL_-_Utilisation_basique_des_param.C3.A8tres]].
  
After the above is all set up correctly, you should now be able to use the TSQLQuery like any other TDataset, either by manipulating its data programmatically or by placing a TDatasouce on the Form, connecting it to the TSQLQuery and then using data contols like TDBGrid etc.
+
Après que ce qui précède est correctement mis en place, vous devriez maintenant être capable d'utiliser le TSQLQuery comme tout autre TDataSet, soit en le manipulant par code soit en plaçant un TDataSource sur la fiche, le connectant à un TDataSet et en utilisant un contrôle comme une grille etc.
  
 
=== Création d'une base de données ===
 
=== Création d'une base de données ===
The [http://www.freepascal.org/docs-html/fcl/sqldb/tsqlconnection.createdb.html TSQLite3Connection.CreateDB] method inherited from the parent class actually does nothing; to create a database if no file exists yet, you simply have to write table data as in the following example:
+
La méthode [http://www.freepascal.org/docs-html/fcl/sqldb/tsqlconnection.createdb.html TSQLite3Connection.CreateDB] héritée de la classe parent ne fait actuellement rien ; pour créer une base de données si aucun fichier n'existe, vous devez simplement écrire les données de la table comme dans l'exemple suivant :
  
(Code extracted from sqlite_encryption_pragma example that ships with Lazarus 1.3 onwards)
+
(Code extrait de l'exemple sqlite_encryption_pragma fourni avec Lazarus 1.3)
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
var
 
var
 
   newFile : Boolean;
 
   newFile : Boolean;
Line 138: Line 147:
  
 
=== Création de collations personnalisées ===
 
=== Création de collations personnalisées ===
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
// utf8 case-sensitive compare callback function
 
// utf8 case-sensitive compare callback function
 
function UTF8xCompare(user: pointer; len1: longint; data1: pointer; len2: longint; data2: pointer): longint; cdecl;
 
function UTF8xCompare(user: pointer; len1: longint; data1: pointer; len2: longint; data2: pointer): longint; cdecl;
Line 171: Line 180:
  
 
=== Création de fonctions personnalisées ===
 
=== Création de fonctions personnalisées ===
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
// example overloading default LOWER() function with user supplied function
 
// example overloading default LOWER() function with user supplied function
 
procedure UTF8xLower(ctx: psqlite3_context; N: cint; V: ppsqlite3_value); cdecl;
 
procedure UTF8xLower(ctx: psqlite3_context; N: cint; V: ppsqlite3_value); cdecl;
Line 186: Line 195:
  
 
=== SQLite3 et les dates ===
 
=== SQLite3 et les dates ===
* SQLite 3 doesn't store dates as a special DateTime value. It can stores them as strings, doubles or integers - see http://www.sqlite.org/datatype3.html#datetime.  
+
* SQLite 3 ne stocke pas les dates comme des valeurs spéciales. Il peut les stocker comme des String, de Double or Integer - voyez [http://www.sqlite.org/datatype3.html#datetime].
* In strings, the  date separator is '-' as per SQL standard/ISO 8601. Thus, if you do an INSERT using the built-in DATE function, it will store it as something like 'YYYY-MM-DD'.
+
* Dans les String, le séparateur de date est '-' comme dans la norme SQL ISO 8601. Ainsi, vous pouvez faire un INSERT en utilisant la fonction intégrée DATE, ce sera stocké en quelque chose comme 'YYYY-MM-DD'.
* Reading a DateTime value can cause problems for DataSets if they are stored as strings: the .AsDateTime qualifier can stall on an SQLite 'string date' but this can be overcome by using something like <tt>strftime(''%d/%m/%Y'',recdate) AS sqlite3recdate</tt> in your SQL SELECT statement, which forces SQLite3 to return the date record in a specified format. (the format string %d/%m/%d corresponds to your locale date format which .AsDateTime will understand) '''==> Please open a bug report with an example application demonstrating the problemif this is the case'''
+
* La lecture d'une valeur DateTime peut causer des problèmes pour DataSets si elles sont enregistrées sous forme de String : la propriété .AsDateTime peut se bloquer sur une 'string date' de SQLite, mais cela peut être surmonté en utilisant quelque chose comme <tt>strftime(''%d/%m/%Y'',recdate) AS sqlite3recdate</tt> dans votre ordre SQL, ce qui force SQLite3 à retourner l'enregistrement Date dans un format spécifique (la chaîne de format %d/%m/%d correspond à votre configuration locale de date que .AsDateTime comprendra) '''==> veuillez ouvrir une rapport de bug avec un exemple d'application démontrant le problème si cela est la cas'''.
* When comparing dates stored as strings (using for example the BETWEEN function) remember that the comparison will always be a <b>string</b> comparison, and will therefore depend on how you have stored the date value.
+
* En comparant des dates enregistrées en chaîne (en utilisant p.ex. la fonction BETWEEN), souvenez-vous que la comparaison sera toujours une comparaison de <b>string</b>, et donc dépendra de comment vous avez enregistré la valeur de date.
 +
 
 
==== Valeurs par défaut en temps local au lieu de UTC ====
 
==== Valeurs par défaut en temps local au lieu de UTC ====
CURRENT_TIME, CURRENT_DATE and CURRENT_TIMESTAMP return current UTC date and/or time. For local date and/or times we can use:
+
CURRENT_TIME, CURRENT_DATE et CURRENT_TIMESTAMP retournent la date UTC et/ou l'heure. Pour des dates et/ou heures locales, vous pouvez utilisez :
   DEFAULT (datetime('now','localtime')) for datetime values formated YYYY-MM-DD HH:MM:SS
+
   DEFAULT (datetime('now','localtime')) pour les valeurs datetime formatées en YYYY-MM-DD HH:MM:SS
   DEFAULT (date('now','localtime')) for date value formated YYYY-MM-DD
+
   DEFAULT (date('now','localtime')) pour les valeurs date formatées en YYYY-MM-DD
   DEFAULT (time('now','localtime')) for time value formated HH:MM:SS
+
   DEFAULT (time('now','localtime')) pour les valeurs time formatées en HH:MM:SS.
  
 
=== Résolution de problème sur SQLDB et SQLite ===
 
=== Résolution de problème sur SQLDB et SQLite ===
* Keep in mind that for designtime support to work (fields etc) Lazarus must find sqlite3.dll too.
+
* Gardez à l'esprit que pour pouvoir fonctionner en conception (champs, inidex etc), Lazarus doit trouver sqlite3.dll aussi.
* The same goes for the database filename. Always use absolute path if you use components to extract e.g. fieldnames at designtime. Otherwise the IDE will create an empty file in its directory. In case of trouble, check if the lazarus/ directory doesn't hold a zero byte copy of the database file.
+
* La même chose pour le nom de fichier de la base de données. Utilisez toujours des chemins absolus si vous utilisez des composants pour extraire p.exe les noms des champs en conception. Sinon, l'EDI créera un fichier vide dans son dossier. En cas de problème, vérifiez si le dossier /lazarus détient une copie vide du fichier de base de données.
* If you have master/detail relationship, you need to refresh master dataset after each insert, in order to get value for slave dataset foreign key field. You can do that in AfterPost event of the master dataset, by calling one of the following overloaded procedures:
+
* Si vous avez une relation maître/détail, vous avez besoin de rafraichir le DataSet maître après chaque insertion, en vue d'obtenir la valeur de la clé étrangère pour le DataSet détail. Vous pouvez faire cela dans l'événement AfterPost du DataSet maître, en appelant l'une de ces méthodes redéfinies :
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
interface
 
interface
 
     procedure RefreshADatasetAfterInsert(pDataSet: TSQLQuery);overload;
 
     procedure RefreshADatasetAfterInsert(pDataSet: TSQLQuery);overload;
Line 255: Line 265:
  
 
=== Vacuum et autres opérations qui doivent être faites en dehors d'une transaction ===
 
=== Vacuum et autres opérations qui doivent être faites en dehors d'une transaction ===
 
+
SQLdb semble demander toujours une connexion, mais certaines opérations comme Pragma et Vacuum doivent être faite en dehors de toute transaction. L'astuce consiste à mettre fin à la transaction, exécuter ce que vous devez et redémarrer la transaction (ainsi SQLdb ne sera pas perdu) :
SQLDB seems to always require a connection, but some operations like Pragma and Vacuum must
+
<syntaxhighlight lang=pascal>
be done outside a transaction. The trick is to end transaction, execute what you must and start
 
transaction again (so that sqldb doesn't get confused:)
 
 
 
<syntaxhighlight>
 
 
   // commit any pending operations or use a "fresh" sqlconnection
 
   // commit any pending operations or use a "fresh" sqlconnection
 
   Conn.ExecuteDirect('End Transaction');  // End the transaction started by SQLdb
 
   Conn.ExecuteDirect('End Transaction');  // End the transaction started by SQLdb
Line 268: Line 274:
  
 
== En utilisant TSQLite3Dataset ==
 
== En utilisant TSQLite3Dataset ==
This section details how to use the TSQLite2Dataset and TSQLite3Dataset components to access SQlite databases.
+
Cette section détaille comment utiliser les composants TSQLite2Dataset et TSQLite3Dataset pour accéder aux bases de données SQLite.  
by Luiz Américo luizmed(at)oi(dot)com(dot)br
+
par Luiz Américo luizmed(at)oi(dot)com(dot)br
  
 
==== Pré-requis ====
 
==== Pré-requis ====
* For sqlite2 databases (legacy):
+
* Pour les bases de données sqlite2 (système "patrimonial") :
** FPC 2.0.0 or higher
+
** FPC 2.0.0 ou supérieur
** Lazarus 0.9.10 or higher
+
** Lazarus 0.9.10 ou supérieur
** SQLite runtime library 2.8.15 or above*
+
** Bibliothèque d'exécution SQLite 2.8.15 ou supérieure
  
* Sqlite2 is not maintained anymore and the binary file cannot be found in the sqlite site
+
* Sqlite2 n'est plus maintenu et le binaire ne peut pas être trouvé sur le site Web SQLite.
  
* For sqlite3 databases:
+
* Pour les bases de données sqlite3 :
** FPC 2.0.2 or higher
+
** FPC 2.0.2 ou supérieur
** Lazarus 0.9.11 (svn revision 8443) or higher
+
** Lazarus 0.9.11 (révision svn 8443) ou supérieur
** sqlite runtime library 3.2.1 or higer (get it from [http://www.sqlite.org www.sqlite.org])
+
** Bibliothèque d'exécution SQLite 3.2.1 ou supérieure (obtenez-la depuis [http://www.sqlite.org www.sqlite.org])
  
'''Before initiating a lazarus project, ensure that:'''
+
'''Avant de commencer un projet Lazarus, assurez-vous que :'''
* the sqlite library is either 
+
* la bibliothèque sqlite est soit
** in the system PATH or
+
** dans le chemin système PATH ou
** in the executable output directory and Lazarus (or current project) directories - this option might work on Windows only
+
** dans le dossier de sortie de l'exécutable et dans le dossier de Lazarus (ou dans le projet courant) - cette option pourrait marcher dans Windows seulement.
* under Linux, put cmem as the first unit in uses clause of the main program
+
* Sous Linux, mettre <code>cmem</code> comme première unité dans la clause Uses de votre programme principal.
** In Debian, Ubuntu and other Debian-like distros, in order to build Lazarus IDE you must install the packages libsqlite-dev/libsqlite3-dev, not only sqlite/sqlite3 (Also applies to OpenSuSe)
+
** Dans Debian, Ubuntu et autre distro dérivée de Debian, en vue de construire l'EDI de Lazarus, vous devez ajouter le paquet libsqlite-dev/libsqlite3-dev, et pas seulement sqlite/sqlite3 (s'applique aussi à OpenSuse).
  
 
==== Comment l'utiliser (Usage basique) ====
 
==== Comment l'utiliser (Usage basique) ====
Install the package found at /components/sqlite directory (see instructions [[Install_Packages|here]])
+
Installez le paquet trouvé dans le répertoire /components/sqlite directory (voir les instructions [[Install_Packages/fr|ici]]).
 +
 
 +
En conception, définissez les propriétés suivantes :
  
At design time, set the following properties:
+
* FileName : chemin vers le fichier sqlite [requis]
 +
* TableName : nom de la table utilisée dans l'ordre SQL [requis]
 +
* SQL : un ordre SELECT en SQL [optionnel]
 +
* SaveOnClose : La valeur par défaut est False, ce qui signifie que les changements ne sont pas enregistrés. On peut le changer à vrai. [optionnel]
 +
* Active : Demande à être mis à True en conception ou au démarrage du programme. [requis]
  
* FileName: path of the sqlite file [required]
+
'''Création d'une table (Dataset)'''
* TableName: name of the table used in the sql statement [required]
 
* SQL: a SQL select statement [optional]
 
* SaveOnClose: The default value is false, which means that changes are not saved. One can change it to true. [optional]
 
* Active: Needs to be set at design time or at program startup. [required]
 
  
'''Creating a Table (Dataset)'''
+
Double-cliquez sur l'icône du composant ou utilisez l'entrée 'Create Table' du menu surgissant (clic droit).
 +
Un simple éditeur de table intuitif sera affiché.
  
Double-click the component icon or use the 'Create Table' item of the popup menu that appears when clicking the right mouse button.
+
Voici tous les types de champ pris en charge par TSqliteDataset et TSqlite3Dataset :
A simple self-explaining table editor will be shown.
 
  
Here are all field types supported by TSqliteDataset and TSqlite3Dataset:
 
 
 
 
* Integer
 
* Integer
 
* AutoInc
 
* AutoInc
Line 322: Line 328:
 
* Currency
 
* Currency
 
    
 
    
'''Retrieving the data'''
+
'''Récupération des données'''
 +
 
 +
Après avoir créer la table ou avec une table déjà créée, ouvrez le DataSet avec la méthode Open.
 +
 
 +
Si la propriété SQL n'était pas définie alors tous les champs de tous les enregistrement seront récupérés, comme si vous aviez passé l'ordre SQL :
  
After creating the table or with a previously created Table, open the dataset with the Open method.
+
<syntaxhighlight lang=pascal>SQL := 'Select * from TABLENAME';</syntaxhighlight>
If the SQL property was not set then all records from all fields will be retrieved, the same if you set the SQL to:
 
  
<syntaxhighlight>SQL := 'Select * from TABLENAME';</syntaxhighlight>
 
  
'''Applying changes to the underlying datafile'''
 
  
To use the ApplyUpdates function, the dataset must contain at least one field that fulfills the requirements for a Primary Key (values must be UNIQUE and not NULL)
+
'''Application des mises à jour au fichier de données sous-jacent'''
  
It's possible to do that in two ways:
+
Pour utiliser la fonction ApplyUpdates, le DataSet doit contenir au moins un champ qui remplit les conditions pour une clé primaire (ses valeurs doivent être UNIQUES et non NULLES).
  
* Set PrimaryKey property to the name of a Primary Key field
+
Il est possible de faire ceci de deux façons :
* Add an AutoInc field (This is easier since the TSqliteDataSet automatically handles it as a Primary Key)
+
* Définissez la propriété PrimaryKey avec le nom du champ de clé primaire.
 +
* Ajoutez un champ AutoInc (ce qui est plus facile depuis que le TSqliteDataSet le manipule automatiquement comme une clé primaire).
  
If one of the two conditions is set, just call
+
Si l'une de ces conditions est satisfaite, appelez seulement :
 
 
<syntaxhighlight>ApplyUpdates;</syntaxhighlight>
 
  
{{Note|If both conditions are set, the field corresponding to PrimaryKey is used to apply the updates.}}
+
<syntaxhighlight lang=pascal>ApplyUpdates;</syntaxhighlight>
  
{{Note|Setting PrimaryKey to a field that is not a Primary Key will lead to loss of data if ApplyUpdates is called, so ensure that the chosen field contains not Null and Unique values before using it.}}
+
{{Note|Si les deux conditions sont satisfaites, le champ correspondant à la clé primaire est utilisé pour appliquer les mise à jour.}}
 +
{{Note|Définir PrimaryKey à un champ qui n'est pas une clé primaire amènera à des pertes de données si ApplyUpdates est appelée, assurez-vous donc que le champ choisi contient bien des valeurs uniques et non nulles avant de l'utiliser.}}
  
 
===== Exemple de relation maître/détail =====
 
===== Exemple de relation maître/détail =====
Various examples of master/detail relations (e.g. the relation between customer and orders):
+
Divers exemples de realtions maître/détail (p.ex. la relation entre client et commandes) :
* the generic SQLDB way of doing this using the <code>DataSource</code> property: [[MasterDetail]]
+
* la manière générique de SQLdb pour faire ceci utilise le [[TDataSource/fr|TDataSource]] et la propriété [[MasterDetail/fr|MasterDetail]]
* TSQLite3 specific example using locate: [[TSqlite3 Master Detail Example]].
+
* un exemple spécifique TSQLite3 utilisant locate: [[TSqlite3 Master Detail Example/frf|Exemple de maître/détail avec TSqlite3]].
  
 
==== Remarques ====
 
==== Remarques ====
* Although it has been tested with 10,000 records and worked fine, TSqliteDataset keeps all the data in memory, so remember to retrieve only the necessary data (especially with Memo Fields).
+
* Bien que cela ait été testé avec 10 000 enregistrements et ait fonctionné très bien, TSqliteDataset conserve toutes les données en mémoire, aussi gardez en mémoire de ne récupérer que les données nécessaire en mémoire (en particulier avec les champs Memo).  
* The same datafile (Filename property) can host several tables/datasets
+
* Le même fichier de données (propriété FileName) peut héberger plusieurs tables/DataSets.
* Several datasets (different combinations of fields) can be created using the same table simultaneously
+
* Plusieurs DataSets (différentes combinaisons de champs) peurvent être créés en utilisant en même temps la même table (ce sont des copies des données en mémoire).
* It's possible to filter the data using WHERE statements in the sql, closing and reopening the dataset (or calling RefetchData method). But in this case, the order and number of fields must remain the same
+
* Il est possible de filtrer les données en utilisant les clauses WHERE dans le SQL, en fermant et réouvrant le DataSet (ou en appelant la méthode RefetchData). Mais dans ce cas, l'ordre et le nombre des champs doit rester le même (i.e. on ne "bricole" la liste de champs du SELECT).
* It's also possible to use complex SQL statements using aliases, joins, views in multiple tables (remember that they must reside in the same datafile), but in this case ApplyUpdates won't work. If someone wants to use complex queries and to apply the updates to the datafile, mail me and i will give some hints how to do that
+
* Il est possible d'utiliser des ordres SQL complexes en utilisant des alias, des jointures, des vues pour de multiples tables (rappelez-vous qu'elle doivent résider dans le même fichier de données), mais dans ce cas ApplyUpdates ne fonctionnera pas.
* Setting filename to a sqlite datafile not created by TSqliteDataset and opening it is allowed but some fields won't have the correct field type detected. These will be treated as string fields.
+
* La définition du nom du fichier vers une fichier de données SQLite mais pas créé par TSqliteDataset et l'ouverture est permise mais le type de certains champs ne sera pas détecté correctement. Ils seront traités comme des champs de type String.
  
Generic examples can be found at fpc/fcl-db/src/sqlite SVN directory
+
Des exemples génériques peuvent être trouvés dans le répertoire SVN fpc/fcl-db/src/sqlite.
  
 
== Voir aussi ==
 
== Voir aussi ==
 +
 
* [[SQLdb_Tutorial1/fr|Tutoriel 1 sur SQLdb]] Tutoriel pour Sqlite
 
* [[SQLdb_Tutorial1/fr|Tutoriel 1 sur SQLdb]] Tutoriel pour Sqlite
 
* [[SqlDBHowto/fr|SqlDBHowto]] Information générale sur SQLDB
 
* [[SqlDBHowto/fr|SqlDBHowto]] Information générale sur SQLDB
 
* [[Working With TSQLQuery/fr|Travailler avec le TSQLQquery]] Informations sur le TSQLQuery de SQLDB.
 
* [[Working With TSQLQuery/fr|Travailler avec le TSQLQquery]] Informations sur le TSQLQuery de SQLDB.
 
* [http://www.sqlite.org Site SQLite]
 
* [http://www.sqlite.org Site SQLite]
<br/>
 

Latest revision as of 22:23, 9 September 2021

English (en) español (es) français (fr) 日本語 (ja) polski (pl) русский (ru) 中文(中国大陆)‎ (zh_CN)

Portail de la base de données

Références:

Tutoriels/articles pratiques :

Bases de données

Advantage - MySQL - MSSQL - Postgres - Interbase - Firebird - Oracle - ODBC - Paradox - SQLite - dBASE - MS Access - Zeos

SQLite et prise en charge par FPC/Lazarus

SQLite est une base de données mono-utilisateur incorporée (sans server) qui peut être utilisée dans des applications FPC et Lazarus (mais pas seulement). Divers pilotes peuvent être utilisés pour accéder à SQLite depuis des programmes FPC/Lazarus. Tous les pilotes demandent la bibliothèque/dll SQLite dans le répertoire de l'application qui, pour fonctionner, peut être votre répertoire de projet ou p.ex. (projectdir)/lib/architecture/ selon vos réglages de projet Lazarus et distribué avec votre exécutable.

Win64: veuillez voir l'avertissement ici sur l'impossibilité d'utiliser certaines versions de FPC/Lazarus Win64.

Accès direct à SQLite

Vous pouvez utiliser une manière simple pour vous connecter à SQLite avec Lazarus. Les SQLite Data Access Components (LiteDAC) sont une bibliothèque de composants qui fournissent une connectivité native à SQLite depuis Lazarus (et Free Pasacal) sur Windows, Mac OS X, iOs, Androïd, Linux et FreeBSD à la fois pour les plates-formes 32-bit et 64-bit. LiteDAC est conçu pour les programmeurs pour développer des applications de bases de données SQLite de bureau ou mobiles réellement multi plate-forme sans nécessité de déployer des bibliothèques supplémentaires.

Vous pouvez télécharger une version d'essai du produit commercial depuis Composants Lazarus.

SQLDB intégré

FPC/Lazarus offre des composants SQLdb intégrés qui incluent la prise en charge des bases de données SQLite (TSQLite3Connection) depuis l'onglet SQLdb de la palette de composants, ce qui vous permet par exemple de créer des IHM avec des composants de base de données tels que des TDBGrids. L'avantage est qu'il devient très facile de passer à une autre base de données telle que Firebird ou PostgreSQL sans trop changer votre programme. Voir plus bas pour des détails.

Prise en charge de Spatialite

Spatialite sont des extensions SIG à SQLite que vous pouvez utiliser à partir de SQLdb. Voyez Spatialite.

Prise en charge du chiffrement

Dans les versions récentes de FOC (implémentée en mas 2012), SQLdb inclut la prise en charge de certaines versions étendues de SQLite qui crypte le fichier de base de données de SQLite en utilisant l'algorithme AES. Utilisez la propriété Password pour définir le clé de chiffrement.

Exemples:

  • SQLCipher : open source, mais les binaires ne sont pas libres (vous devez les compilez vous-mêmes).
  • System.Data.SQLite : open source, binaires Windows (32, 64, CE) disponibles, téléchargez p.ex. un des binaires précompilés et renommez SQLite.Interop.dll en sqlite3.dll (Si vous en utilisez un statiquement lié, vous devriez probablement renommer system.Data.SQLite.DLL en sqlite3.dll).
  • wxSQLite3: open source, certains binaires Linux sont disponibles (ex: https://launchpad.net/ubuntu/oneiric/+package/libwxsqlite3-2.8-0)

sqlite3backup

sqlite3backup est une unité avec FPC (pas dasn Lazarus pais peut être utilisé par programmation) qui fournit une fonctionnalité de sauvegarde/restauration pour SQLite3. Elle utilise sqlite3conn de SQLdb.

Cas de Debian Jessie

(Décrit depuis Lazarus 1.8RC3 + FPC 3.0.3, à vérifier pour d'autres versions) L'installation du paquet sqlite3laz.0.4 depuis la Debian Jessie pose problème. En effet, lors de la reconstruction de l'EDI, une erreur générique de liaison bloquante survient alors. Il s'avère que le fichier /usr/share/fpcsrc/3.0.3/packages/sqlite/src/sqlite3.inc attend la nom de bibliothèque libsqlite3.so mais la Debian Jessie ne propose que le lien symbolique libsqlite3.so.0. Deux solutions :

  • soit changer le nom dans le source du fichier .inc, mais cela est risqué et devra être fait à nouveau à chaque réinstallation de Lazarus et induit une différence non pertinente avec la version officielle.
  • soit créer un lien symbolique avec ce nom dans le répertoire des bibliothèques (/usr/lib/x86_64-linux-gnu pour mon cas).
sudo ln -s libsqlite3.so.0 libsqlite3.so 

La contrainte réside dans le déploiement, il faut en effet s'assurer que le lien ou nom de fichier direct existe sur le système cible et au besoin le créer.

Cela résoud aussi les problèmes rencontrés lors de l'installation de la bibliotèque ZeosLib qui provoquait la même erreur.

Zeos

Zeos

SQLitePass

Composants SqlitePass . Etat: inconnu.

TSQLite3Dataset et TSQLiteDataset

Il y a des paquets séparés pour TSQLiteDataset (unité sqliteds) et TSQLite3Dataset (unité sqlite3ds) ; voyez ci-dessous pour une description sur la façon de les utiliser. Visitez la sqlite4fpc page d'accueil pour trouver l'API de référence et plus de tutoriels.

TSqliteDataset et TSqlite3Dataset sont des descendants de TDataSet qui accèdent, respectivement aux bases de données SQLite 2.8.x et 3.x.x. Pour de nouveaux projets, vous supposeriez utiliser TSQlite3Dataset en tant que version actuelle de SQLite 3.x.

Ci-dessous est une liste des principaux avantages et inconvénients comparés à d'autres méthodes d'accès/pilotes SQLite pour FPC/Lazarus :

Avantages :

  • Flexible : Les programmeurs peuvent choisir d'utiliser ou pas le langage SQL, leur permettant de travailler avec un schéma de simple table ou des schémas plus compliqués tels que SQLite le permet.

Inconvénients :

  • Le changement vers une autre base de données est plus difficile si vous utilisez les composants SQLDB ou Zeos.
Light bulb  Remarque: Selon ci-dessus, des utilisateurs utiliseront SQLdb ou Zeos pour leurs avantages à moins d'avoir besoin des accès bas-niveaux de la bibliothèque SQLite

Utilisation des composants SQLdb avec SQLite

Ces directives font le point sur les spécificités de SQLdb pour SQLDB (le composant TSQLite3Connection). Pour une vue d'ensemble, jetez un œil sur SqlDBHowto qui continet des informations intéressantes sur les composants SQLdb.

Voyez le SQLdb_Tutorial1 pour un tutoriel sur la création d'un programme IHM relié aux données qui est écrit pour SQLite/SQLDB (et tout aussi bien pour Firebird/SQLDB, PostgreSQL/SQLDB, et d'une façon générale pour tout SGBDR que SQLDB prend en charge).

Nous utiliserons une combinaison de trois composants dans l'onglet SQLdb : TSQLite3Connection, TSQLTransaction et TSQLQuery. Le TSQLQuery agit comme notre TDataSet ; dans le cas le plus simple il représente juste une de nos tables. Par simplicité, assurez-vous d'avoir une base de données SQLite et de ne pas avoir besoin d'en créer une maintenant. TSQLite3Connection peut être trouvé dans l'unité sqlite3conn, si vous voulez le déclarez vous-mêmes ou si vous travaillez avec FreePascal.

Les trois composants sont connectés entre eux comme d'habitude : dans l'ensemble TSQLQuery, définissez les propriétés Database et Transaction, dans le TSQLTransaction, définissez la propriété Database. Il n'y a pas grand chose d'autre à faire dans les composants Transaction et Connection, les choses les plus intéressantes seront faites dans le TSQLQuery. Configurez les composants comme suit :

TSQLite3Connection :

  • DatabaseName : mettez-y le nom (avec un chemin absolu!) de votre fichier SQLite. Malheureusement, vous ne pouvez pas simplement utiliser un chemin relatif qui resterait inchangé à la conception et à l'exécution *** est-ce toujours vrai ? Ne pouvez-vous pas seulement copier le fichier de base de données à l'aide d'un script post-construction ou définir un lien symbolique ? ***. Vous devrez vous assurer que quand l'application démarre, le chemin vers le fichier est défini correctement par programmation, sans tenir compte de la valeur de conception.

Note : Pour définir le chemin d'accès complet à la bibliothèque (si vous placez votre dll/so/dylib de SQLite où votre système ne le trouvera pas, telle que le dossier de l'application dans Linux ou OS/X), vous pouvez employer la propriété SQLiteLibraryName (avant qu'une connexion ne soit établie p.ex. dans l'événement OnCreate de la fiche principale), comme ceci :

SQLiteLibraryName:='./sqlite3.so';

TSQLQuery :

  • SQL : placez-y une simple rerquête SELECT sur l'une de vos tables. P.ex., si vous avez une table 'foo' et voulez que le DataSet représente cette table, mettez-y seulement :
    SELECT * FROM foo
    
  • Active : Mettez-la à True depuis l'EDI pour voir si tout marche correctement. Cela active automatiquement les objets transaction et connexion. Si vous recevez une erreur, soit le Databasename de la connection n'est pas correct ou la requête SQL est fausse. Plus tard, lorsque nous finissons d'ajouter les champs (voir ci-dessous), tous les objets sont de nouveau inactifs, nous ne voulons pas que l'IDE verrouille la base de données SQLite (un seul utilisateur!) Lors du test de l'application.
  • Sans doute pas nécessaire pour une opération propre - devra être contrôlé (juin 2012) Maintenant nous pouvons ajouter des champs à notre TSQLQuery. Pendant que le composant est toujours actif , cliquez-droit et "Editer les champs..." . Cliquez sur le le bouton "+" et ajoutez les champs. Cela ajoutera tous les champs que votre requêtes SQL a retourné. Ajouté chaque chaque dont vous avez besoin, vous pouvez aussi ajouter des champs de référence (lookup fields) ici ; dans ce cas assurez d'avoir ajouter tous les champs requis dans les autres DataSets avant d'ajouter les champs qui leur feront référence. Si votre table a des champs qui ne vous intéresse pas, vous pouvez les enlever, vous pouvez aussi rendre votre requête un peu plus spécifique (remplacez SELECT * par un SELECT avec la liste des champs voulus).
  • Dans votre code, vous avez besoin d'appeler SQLQuery.ApplyUpdates et SQLTransaction.Commit, les événements TSQLQuery.AfterPost et AfterInsert sont les bons endroits pour cela quand vous utilisez des contrôles sensibles aux données mais bien sûr, vous pouvez aussi remettre à plus tard ces appels. Si vous ne les appelez pas, la base de données ne sera pas mise à jour.
  • Database is locked" : L'EDI pourrait encore bloquer la base de données (SQLite est une base de données mono-utilisateur), vous avez sans doute oublié de mettre les composants à inactif et de vous déconnecter après avoir défini tous les champs de vos objets TSQLQuery. Utilisez l'événement OnCreate de la fiche pour définir le chemin d'accès et activer les objets à l'exécution seulement. La plupart des choses que vous réglez dans TSQLQuery depuis l'EDI ne nécessitent pas (et même ne le permettent pas pour certaines) qu'il soit actif en conception, la seule exception est quand vous voulez lire la structure de la table, ainsi l'état normal en conception est inactif.
  • Vos tables devraient toutes avoir une clé primaire et vous devez vous assurez que les champs correspondants ont pfInKey et rien d'autre dans leurs PoviderFlags (ces drapeaux contrôlent comment et où le champ est utilisé en cas de construction automatique de requêtes de mise à jour et de suppression).
  • Si vous utilisez des champs de référence :
    • Assurez-vous que le ProviderFlags pour le champ de référence est complétement vide, comme ça, son nom ne sera pas utilisé dans une requête de mise à jour. Le champ de référence lui-même n'est pas un champ de données, il agit seulement sur la valeur d'un autre champ, le champ clé correspondant, et seulement ce champ clé sera plus tard utilisé dans les requêtes de mise à jour. Vous pouvez masquer le champ clé car la plupart du temps vous ne voudrez pas le voir dans votre interface utilisateur mais il doit être défini.
    • LookupCache doit être mise à True. Au moment de la rédaction, pour certaines raisons, le champ Lookup n'affichera rien d'autre (mais fonctionnera encore) et étrangement à l'exact opposé du TSQLite3Dataset ou d'autres composants TXXXDataset pour lesquels elle doit être mise à False. Je ne suis pas encore sûr de savoir si cela est un comportement intentionnel ou un bug.
  • Habituellement avec de simples tables, vous n'aurez besoin de définir aucune des propriétés InsertSQL, UpdateSQL et DeleteSQL, il suffit de les laisser vides. Si vous avez défini correctement les propriétés ProviderFlags de tous vos champs, les requêtes seront créées à la volée. Pour plus de détails sur InsertSQL, UpdateSQL et DeleteSQL, voyez [[1]].

Après que ce qui précède est correctement mis en place, vous devriez maintenant être capable d'utiliser le TSQLQuery comme tout autre TDataSet, soit en le manipulant par code soit en plaçant un TDataSource sur la fiche, le connectant à un TDataSet et en utilisant un contrôle comme une grille etc.

Création d'une base de données

La méthode TSQLite3Connection.CreateDB héritée de la classe parent ne fait actuellement rien ; pour créer une base de données si aucun fichier n'existe, vous devez simplement écrire les données de la table comme dans l'exemple suivant :

(Code extrait de l'exemple sqlite_encryption_pragma fourni avec Lazarus 1.3)

var
  newFile : Boolean;
begin

  SQLite3Connection1.Close; // Ensure the connection is closed when we start

  try
    // Since we're making this database for the first time,
    // check whether the file already exists
    newFile := not FileExists(SQLite3Connection1.DatabaseName);

    if newFile then
    begin
      // Create the database and the tables
      try
        SQLite3Connection1.Open;
        SQLTransaction1.Active := true;

        // Here we're setting up a table named "DATA" in the new database
        SQLite3Connection1.ExecuteDirect('CREATE TABLE "DATA"('+
                    ' "id" Integer NOT NULL PRIMARY KEY AUTOINCREMENT,'+
                    ' "Current_Time" DateTime NOT NULL,'+
                    ' "User_Name" Char(128) NOT NULL,'+
                    ' "Info" Char(128) NOT NULL);');

        // Creating an index based upon id in the DATA Table
        SQLite3Connection1.ExecuteDirect('CREATE UNIQUE INDEX "Data_id_idx" ON "DATA"( "id" );');

        SQLTransaction1.Commit;

        ShowMessage('Succesfully created database.');
      except
        ShowMessage('Unable to Create new Database');
      end;
    end;
  except
    ShowMessage('Unable to check if database file exists');
  end;
 end;

Création de collations personnalisées

// utf8 case-sensitive compare callback function
function UTF8xCompare(user: pointer; len1: longint; data1: pointer; len2: longint; data2: pointer): longint; cdecl;
var S1, S2: AnsiString;
begin
  SetString(S1, data1, len1);
  SetString(S2, data2, len2);
  Result := UnicodeCompareStr(UTF8Decode(S1), UTF8Decode(S2));
end;

// utf8 case-insensitive compare callback function
function UTF8xCompare_CI(user: pointer; len1: longint; data1: pointer; len2: longint; data2: pointer): longint; cdecl;
var S1, S2: AnsiString;
begin
  SetString(S1, data1, len1);
  SetString(S2, data2, len2);
  Result := UnicodeCompareText(UTF8Decode(S1), UTF8Decode(S2));
end;

// register collation using SQLite3 API (requires sqlite3dyn unit):
sqlite3_create_collation(SQLite3.Handle, 'UTF8_CI', SQLITE_UTF8, nil, @UTF8xCompare_CI);
// or using method of TSQLite3Connection:
CreateCollation('UTF8_CI',1,nil,@UTF8xCompare_CI);  

// now we can use case-insensitive comparison in SQL like:
// SELECT * FORM table1 WHERE column1 COLLATE UTF8_CI = 'á'

// but this does not work for LIKE operator
// in order to support also LIKE operator we must overload default LIKE function using sqlite3_create_function()
// http://www.sqlite.org/lang_corefunc.html#like

Création de fonctions personnalisées

// example overloading default LOWER() function with user supplied function
procedure UTF8xLower(ctx: psqlite3_context; N: cint; V: ppsqlite3_value); cdecl;
var S: AnsiString;
begin
  SetString(S, sqlite3_value_text(V[0]), sqlite3_value_bytes(V[0]));
  S := UTF8Encode(AnsiLowerCase(UTF8Decode(S)));
  sqlite3_result_text(ctx, PAnsiChar(S), Length(S), sqlite3_destructor_type(SQLITE_TRANSIENT));
end;

// register function LOWER() using SQLite3 API (requires sqlite3dyn unit):
sqlite3_create_function(SQLite3.Handle, 'lower', 1, SQLITE_UTF8 or SQLITE_DETERMINISTIC, nil, @UTF8xLower, nil, nil);

SQLite3 et les dates

  • SQLite 3 ne stocke pas les dates comme des valeurs spéciales. Il peut les stocker comme des String, de Double or Integer - voyez [2].
  • Dans les String, le séparateur de date est '-' comme dans la norme SQL ISO 8601. Ainsi, vous pouvez faire un INSERT en utilisant la fonction intégrée DATE, ce sera stocké en quelque chose comme 'YYYY-MM-DD'.
  • La lecture d'une valeur DateTime peut causer des problèmes pour DataSets si elles sont enregistrées sous forme de String : la propriété .AsDateTime peut se bloquer sur une 'string date' de SQLite, mais cela peut être surmonté en utilisant quelque chose comme strftime(%d/%m/%Y,recdate) AS sqlite3recdate dans votre ordre SQL, ce qui force SQLite3 à retourner l'enregistrement Date dans un format spécifique (la chaîne de format %d/%m/%d correspond à votre configuration locale de date que .AsDateTime comprendra) ==> veuillez ouvrir une rapport de bug avec un exemple d'application démontrant le problème si cela est la cas.
  • En comparant des dates enregistrées en chaîne (en utilisant p.ex. la fonction BETWEEN), souvenez-vous que la comparaison sera toujours une comparaison de string, et donc dépendra de comment vous avez enregistré la valeur de date.

Valeurs par défaut en temps local au lieu de UTC

CURRENT_TIME, CURRENT_DATE et CURRENT_TIMESTAMP retournent la date UTC et/ou l'heure. Pour des dates et/ou heures locales, vous pouvez utilisez :

 DEFAULT (datetime('now','localtime')) pour les valeurs datetime formatées en YYYY-MM-DD HH:MM:SS
 DEFAULT (date('now','localtime')) pour les valeurs date formatées en YYYY-MM-DD
 DEFAULT (time('now','localtime')) pour les valeurs time formatées en HH:MM:SS.

Résolution de problème sur SQLDB et SQLite

  • Gardez à l'esprit que pour pouvoir fonctionner en conception (champs, inidex etc), Lazarus doit trouver sqlite3.dll aussi.
  • La même chose pour le nom de fichier de la base de données. Utilisez toujours des chemins absolus si vous utilisez des composants pour extraire p.exe les noms des champs en conception. Sinon, l'EDI créera un fichier vide dans son dossier. En cas de problème, vérifiez si le dossier /lazarus détient une copie vide du fichier de base de données.
  • Si vous avez une relation maître/détail, vous avez besoin de rafraichir le DataSet maître après chaque insertion, en vue d'obtenir la valeur de la clé étrangère pour le DataSet détail. Vous pouvez faire cela dans l'événement AfterPost du DataSet maître, en appelant l'une de ces méthodes redéfinies :
interface
    procedure RefreshADatasetAfterInsert(pDataSet: TSQLQuery);overload;
    procedure RefreshADatasetAfterInsert(pDataSet: TSQLQuery; pKeyField: string);overload;  
 
implementation
 
procedure RefreshADatasetAfterInsert(pDataSet: TSQLQuery; pKeyField: string);
//This procedure refreshes a dataset and positions cursor to last record
//To be used if Dataset is not guaranteed to be sorted by an autoincrement primary key
var
  vLastID: Integer;
  vUpdateStatus : TUpdateStatus;
begin
  vUpdateStatus := pDataset.UpdateStatus;
  //Get last inserted ID in the database
  pDataset.ApplyUpdates;
  vLastID:=(pDataSet.DataBase as TSQLite3Connection).GetInsertID;
  //Now come back to respective row
  if vUpdateStatus = usInserted then begin
    pDataset.Refresh;
    //Refresh and go back to respective row
    pDataset.Locate(pKeyField,vLastID,[]);
  end;
end;
 
procedure RefreshADatasetAfterInsert(pDataSet: TSQLQuery);
//This procedure refreshes a dataset and positions cursor to last record
//To be used only if DataSet is guaranteed to be sorted by an autoincrement primary key
var
  vLastID: Integer;
  vUpdateStatus : TUpdateStatus;
begin
  vUpdateStatus := pDataset.UpdateStatus;
  pDataset.ApplyUpdates;
  vLastID:=(pDataSet.DataBase as TSQLite3Connection).GetInsertID;
  if vUpdateStatus = usInserted then begin
    pDataset.Refresh;
    //Dangerous!
    pDataSet.Last;
  end;
end;

procedure TDataModule1.SQLQuery1AfterPost(DataSet: TDataSet);
begin
  RefreshADatasetAfterInsert(Dataset as TSQLQuery); //If your dataset is sorted by primary key
end;  

procedure TDataModule1.SQLQuery2AfterPost(DataSet: TDataSet);
begin
  RefreshADatasetAfterInsert(Dataset as TSQLQuery, 'ID'); //if you are not sure that the dataset is always sorted by primary key
end;

Vacuum et autres opérations qui doivent être faites en dehors d'une transaction

SQLdb semble demander toujours une connexion, mais certaines opérations comme Pragma et Vacuum doivent être faite en dehors de toute transaction. L'astuce consiste à mettre fin à la transaction, exécuter ce que vous devez et redémarrer la transaction (ainsi SQLdb ne sera pas perdu) :

  // commit any pending operations or use a "fresh" sqlconnection
  Conn.ExecuteDirect('End Transaction');  // End the transaction started by SQLdb
  Conn.ExecuteDirect('Vacuum');
  Conn.ExecuteDirect('Begin Transaction'); //Start a transaction for SQLdb to use

En utilisant TSQLite3Dataset

Cette section détaille comment utiliser les composants TSQLite2Dataset et TSQLite3Dataset pour accéder aux bases de données SQLite. par Luiz Américo luizmed(at)oi(dot)com(dot)br

Pré-requis

  • Pour les bases de données sqlite2 (système "patrimonial") :
    • FPC 2.0.0 ou supérieur
    • Lazarus 0.9.10 ou supérieur
    • Bibliothèque d'exécution SQLite 2.8.15 ou supérieure
  • Sqlite2 n'est plus maintenu et le binaire ne peut pas être trouvé sur le site Web SQLite.
  • Pour les bases de données sqlite3 :
    • FPC 2.0.2 ou supérieur
    • Lazarus 0.9.11 (révision svn 8443) ou supérieur
    • Bibliothèque d'exécution SQLite 3.2.1 ou supérieure (obtenez-la depuis www.sqlite.org)

Avant de commencer un projet Lazarus, assurez-vous que :

  • la bibliothèque sqlite est soit
    • dans le chemin système PATH ou
    • dans le dossier de sortie de l'exécutable et dans le dossier de Lazarus (ou dans le projet courant) - cette option pourrait marcher dans Windows seulement.
  • Sous Linux, mettre cmem comme première unité dans la clause Uses de votre programme principal.
    • Dans Debian, Ubuntu et autre distro dérivée de Debian, en vue de construire l'EDI de Lazarus, vous devez ajouter le paquet libsqlite-dev/libsqlite3-dev, et pas seulement sqlite/sqlite3 (s'applique aussi à OpenSuse).

Comment l'utiliser (Usage basique)

Installez le paquet trouvé dans le répertoire /components/sqlite directory (voir les instructions ici).

En conception, définissez les propriétés suivantes :

  • FileName : chemin vers le fichier sqlite [requis]
  • TableName : nom de la table utilisée dans l'ordre SQL [requis]
  • SQL : un ordre SELECT en SQL [optionnel]
  • SaveOnClose : La valeur par défaut est False, ce qui signifie que les changements ne sont pas enregistrés. On peut le changer à vrai. [optionnel]
  • Active : Demande à être mis à True en conception ou au démarrage du programme. [requis]

Création d'une table (Dataset)

Double-cliquez sur l'icône du composant ou utilisez l'entrée 'Create Table' du menu surgissant (clic droit). Un simple éditeur de table intuitif sera affiché.

Voici tous les types de champ pris en charge par TSqliteDataset et TSqlite3Dataset :

  • Integer
  • AutoInc
  • String
  • Memo
  • Bool
  • Float
  • Word
  • DateTime
  • Date
  • Time
  • LargeInt
  • Currency

Récupération des données

Après avoir créer la table ou avec une table déjà créée, ouvrez le DataSet avec la méthode Open.

Si la propriété SQL n'était pas définie alors tous les champs de tous les enregistrement seront récupérés, comme si vous aviez passé l'ordre SQL :

SQL := 'Select * from TABLENAME';


Application des mises à jour au fichier de données sous-jacent

Pour utiliser la fonction ApplyUpdates, le DataSet doit contenir au moins un champ qui remplit les conditions pour une clé primaire (ses valeurs doivent être UNIQUES et non NULLES).

Il est possible de faire ceci de deux façons :

  • Définissez la propriété PrimaryKey avec le nom du champ de clé primaire.
  • Ajoutez un champ AutoInc (ce qui est plus facile depuis que le TSqliteDataSet le manipule automatiquement comme une clé primaire).

Si l'une de ces conditions est satisfaite, appelez seulement :

ApplyUpdates;
Light bulb  Remarque: Si les deux conditions sont satisfaites, le champ correspondant à la clé primaire est utilisé pour appliquer les mise à jour.
Light bulb  Remarque: Définir PrimaryKey à un champ qui n'est pas une clé primaire amènera à des pertes de données si ApplyUpdates est appelée, assurez-vous donc que le champ choisi contient bien des valeurs uniques et non nulles avant de l'utiliser.
Exemple de relation maître/détail

Divers exemples de realtions maître/détail (p.ex. la relation entre client et commandes) :

Remarques

  • Bien que cela ait été testé avec 10 000 enregistrements et ait fonctionné très bien, TSqliteDataset conserve toutes les données en mémoire, aussi gardez en mémoire de ne récupérer que les données nécessaire en mémoire (en particulier avec les champs Memo).
  • Le même fichier de données (propriété FileName) peut héberger plusieurs tables/DataSets.
  • Plusieurs DataSets (différentes combinaisons de champs) peurvent être créés en utilisant en même temps la même table (ce sont des copies des données en mémoire).
  • Il est possible de filtrer les données en utilisant les clauses WHERE dans le SQL, en fermant et réouvrant le DataSet (ou en appelant la méthode RefetchData). Mais dans ce cas, l'ordre et le nombre des champs doit rester le même (i.e. on ne "bricole" la liste de champs du SELECT).
  • Il est possible d'utiliser des ordres SQL complexes en utilisant des alias, des jointures, des vues pour de multiples tables (rappelez-vous qu'elle doivent résider dans le même fichier de données), mais dans ce cas ApplyUpdates ne fonctionnera pas.
  • La définition du nom du fichier vers une fichier de données SQLite mais pas créé par TSqliteDataset et l'ouverture est permise mais le type de certains champs ne sera pas détecté correctement. Ils seront traités comme des champs de type String.

Des exemples génériques peuvent être trouvés dans le répertoire SVN fpc/fcl-db/src/sqlite.

Voir aussi