diff --git a/.gitattributes b/.gitattributes index 296a55b3da..a562335442 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4551,6 +4551,12 @@ examples/database/sqldbtutorial3/readme.txt svneol=native#text/plain examples/database/sqldbtutorial3/sqldbtutorial3.ini svneol=native#text/plain examples/database/sqldbtutorial3/sqldbtutorial3.lpi svneol=native#text/plain examples/database/sqldbtutorial3/sqldbtutorial3.lpr svneol=native#text/pascal +examples/database/sqlite_encryption_pragma/README.txt svneol=native#text/plain +examples/database/sqlite_encryption_pragma/project1.lpi svneol=native#text/plain +examples/database/sqlite_encryption_pragma/project1.lpr svneol=native#text/pascal +examples/database/sqlite_encryption_pragma/project1.res -text +examples/database/sqlite_encryption_pragma/unit1.lfm svneol=native#text/plain +examples/database/sqlite_encryption_pragma/unit1.pas svneol=native#text/pascal examples/database/sqlite_mushrooms/ImageTest.db3 -text examples/database/sqlite_mushrooms/Mushroom_Report.lrf svneol=native#text/plain examples/database/sqlite_mushrooms/Readme.txt svneol=native#text/plain diff --git a/examples/database/sqlite_encryption_pragma/README.txt b/examples/database/sqlite_encryption_pragma/README.txt new file mode 100644 index 0000000000..cb2b153675 --- /dev/null +++ b/examples/database/sqlite_encryption_pragma/README.txt @@ -0,0 +1,200 @@ +//////////////////////////////////////////////////// +// // +// Simple SQLite3 Demo with Encryption and Pragma // +// // +//////////////////////////////////////////////////// +// // +// CONTENTS // +// // +// 1. OVERVIEW OF THIS DEMONSTRATION // +// 2. REQUIREMENTS FOR SQLITE ENCRYPTION // +// 3. SQLITE PRAGMA STATEMENTS // +// 4. ADDITIONAL SQLITE RESOURCES // +// // +//////////////////////////////////////////////////// + + + +1. OVERVIEW OF THIS DEMONSTRATION + +This application very simply demonstrates the following capabilities: +- Creation of an SQLite3 Database +- Encrypting the database using a key +- Changing (or setting if not initially set) the encryption key for the + database after it has been created +- Creation of a database table +- Creating an Index +- Adding a row of data to the table +- Performing a very basic query +- Setting and reading various database metadata (Pragma) + +The application makes a new database file "new.db" within the local +directory. + +I highly recommend using a third party SQLite Database Management Tool to +verify the table and index creation and encryption of your database. +You'll want to use one that supports SQLite 3.6.8 or later. I use SQLite2009 +Pro Enterprise Manager. This and other tools can be found at: +http://www.sqlite.org/cvstrac/wiki?p=ManagementTools + + + + +2. REQUIREMENTS FOR SQLITE ENCRYPTION + +Since version 3.6.8 SQLite has supported the option of database encryption +(though it must be supported specifically by the version of the sqlite3.dll +you use for your application). The entire database, except for bytes 16 +through 23, will be encrypted. See the additional resources at the end of +this document for more details about the SQLite Database Header, and these +specific bytes. + +Using the following link, you can find a few options for versions of SQLite +that provide support for encryption. Since I'm working mainly on Windows, I +opted for the Open Source System.Data.SQLite +http://wiki.freepascal.org/sqlite#Support_for_SQLite_encryption + +I used the "Precompiled Binaries for 32-bit Windows (.NET Framework 3.5 SP1)" +and renamed SQLite.Interop.dll to sqlite3.dll, but you should be able to use +any version of applicable SQLite DLL you want as long as you have the +required dependencies (.NET Framework and VC++ Redistributables. +http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki + +When selecting the DLL to use, if you have the latest version of the .NET +framework installed on your computer (4.5.1) and the Visual Studio 2013 +Redistributable Package, then you should be able to use this: +http://system.data.sqlite.org/downloads/1.0.93.0/sqlite-netFx451-binary-x64-2013-1.0.93.0.zip +Just extract the SQLite.Interop.dll and rename it to sqlite3.dll and place +it in the local directory. Again, you'll want to download a version that +matches the version of .NET and Visual C++ Runtime on your computer (you +may have many/all the versions of .NET and VC++ Redists installed). + +You can download various versions of the .NET framework at: +http://msdn.microsoft.com/en-us/vstudio/aa496123.aspx + +You can download various versions of the Visual C++ Redistributables at: +http://support.microsoft.com/kb/2019667 + + +Make sure the sqlite3.dll is in the same directory as your application, +or you *will* have errors, and your application will not work! + + + + +3. SQLITE PRAGMA STATEMENTS + +SQLite Pragma are metadata variables and constants that are stored in the +header of an SQLite Database. Most of these values are read-only or are not +recommended to be changed, but a few of them can be set for various purposes +in your application. + +This demonstration application performs a few different Pragma operations: +- sets and reads the application_id Pragma +- sets and reads the user_version Pragma +- sets and re-sets the encryption key + + + +Per the SQLite Documentation (edited for clarity): +The pragma user_version is used to set or get the value of the user-version. +The user-version is a big-endian 32-bit signed integer stored in the database +header at offset 60. +The user-version is not used internally by SQLite. It may be used by +applications for any purpose. +http://www.sqlite.org/pragma.html#pragma_schema_version + +In the demo application, I've set the user_version to a constant value. +You can use any 32-bit Signed Integer value you want: +// must be a 32-bit Signed Integer (LongInt -2147483648 .. 2147483647) + user_version = 23400001; + +When we create the database, we set this value to the database: + SQLite3Connection1.ExecuteDirect('PRAGMA user_version = ' + IntToStr(user_version) + ';'); + +To read the user_version from the database, we can do the following: + SQLQuery1.SQL.Text := 'PRAGMA user_version;'; + SQLQuery1.Open; + ShowMessage(SQLQuery1.fields[0].asString); + + + +Per the SQLite Documentation: +The application_id PRAGMA is used to query or set the 32-bit unsigned big-endian +"Application ID" integer located at offset 68 into the database header. +Applications that use SQLite as their application file-format should set the +Application ID integer to a unique integer so that utilities such as file(1) can +determine the specific file type rather than just reporting "SQLite3 Database". +A list of assigned application IDs can be seen by consulting the magic.txt file +in the SQLite source repository. +http://www.sqlite.org/pragma.html#pragma_application_id + +In the demo application, I've set the application_id to a constant value. +You can use any 32-bit Unsigned Integer value you want. In one of my applications +I use this value to track differences in the database table structure between +different versions of my application, incrementing the application_id each time I +change the table structure with a new application version: +// must be a 32-bit Unsigned Integer (Longword 0 .. 4294967295) + application_id = 1189021115; + +When we create the database, we set this value to the database: + SQLite3Connection1.ExecuteDirect('PRAGMA application_id = ' + IntToStr(application_id) + ';'); + +To read the application_id from the database, we can do the following: + SQLQuery1.SQL.Text := 'PRAGMA application_id;'; + SQLQuery1.Open; + ShowMessage(SQLQuery1.fields[0].asString); + + + +The key pragma is a little different. Using Lazarus' SQLiteConnection Component, +we set the key with the 'password' parameter. + SQLite3Connection1.Password := txtOld.Text; + +We could also use a Pragma statement to set the key initially using the following +when we create the database: + SQLite3Connection1.ExecuteDirect('PRAGMA key = ' + QuotedStr(txtNew.Text) + ';'); + +The benefit of using the password parameter of the SQLiteConnection component +is that it sets the key and if we want to use open and close the database +multiple times while using the application, we don't have to keep specifying a key. + +If you do not want to encrypt the database initially, simply do not provide an +encryption key/password, or leave these values blank: + SQLite3Connection1.Password := ''; + +In order to change the encryption or to remove all encryption (unencrypting the +database) after the database has been created, we use the 'rekey' Pragma as follows: + SQLite3Connection1.ExecuteDirect('PRAGMA rekey = ' + QuotedStr(txtNew.Text) + ';'); + +The double-quotes used here allow the user to leave txtNew empty, which sets the +resulting SQL Statement to: +PRAGMA rekey = ''; + +Which removes all encryption (unencrypts the database). + + + + +4. ADDITIONAL SQLITE RESOURCES + +To read more about the SQLite Encryption Extension (SEE), use the following URL +(Section: How To Compile And Use SEE) +http://www.sqlite.org/see/doc/trunk/www/index.wiki + +For specifics on the key and rekey Pragmas, read the section Using the "key" +PRAGMA at the following URL: +http://www.sqlite.org/see/doc/trunk/www/readme.wiki + +Details about the SQLite File Format (and particularly about the Database Header) +can be found at: +http://www.sqlite.org/fileformat2.html#database_header + +Information about the various standard database PRAGMA (metadata) statements can +be found at: +http://www.sqlite.org/pragma.html + +The methods of passing the key to the database used in this demonstration are very +simplistic. Ideally, we would take a stronger cryptographic approach. +Some helpful info on this topic can be found at: +https://www.owasp.org/index.php/Cheat_Sheets \ No newline at end of file diff --git a/examples/database/sqlite_encryption_pragma/project1.lpi b/examples/database/sqlite_encryption_pragma/project1.lpi new file mode 100644 index 0000000000..1c330535f7 --- /dev/null +++ b/examples/database/sqlite_encryption_pragma/project1.lpi @@ -0,0 +1,85 @@ + + + + + + + + + + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <DestinationDirectory Value="$(ProjPath)\published\"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="3"> + <Item1> + <PackageName Value="SQLDBLaz"/> + </Item1> + <Item2> + <PackageName Value="FCL"/> + </Item2> + <Item3> + <PackageName Value="LCL"/> + </Item3> + </RequiredPackages> + <Units Count="2"> + <Unit0> + <Filename Value="project1.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="project1"/> + </Unit0> + <Unit1> + <Filename Value="unit1.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="Form1"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="Unit1"/> + </Unit1> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="project1"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> +</CONFIG> diff --git a/examples/database/sqlite_encryption_pragma/project1.lpr b/examples/database/sqlite_encryption_pragma/project1.lpr new file mode 100644 index 0000000000..ced6d82558 --- /dev/null +++ b/examples/database/sqlite_encryption_pragma/project1.lpr @@ -0,0 +1,21 @@ +program project1; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX}{$IFDEF UseCThreads} + cthreads, + {$ENDIF}{$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, Unit1 + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource := True; + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. + diff --git a/examples/database/sqlite_encryption_pragma/project1.res b/examples/database/sqlite_encryption_pragma/project1.res new file mode 100644 index 0000000000..e66ecf85fe Binary files /dev/null and b/examples/database/sqlite_encryption_pragma/project1.res differ diff --git a/examples/database/sqlite_encryption_pragma/unit1.lfm b/examples/database/sqlite_encryption_pragma/unit1.lfm new file mode 100644 index 0000000000..ab3e698e0d --- /dev/null +++ b/examples/database/sqlite_encryption_pragma/unit1.lfm @@ -0,0 +1,321 @@ +object Form1: TForm1 + Left = 389 + Height = 740 + Top = 156 + Width = 416 + Caption = 'SQLite DB Demo' + ClientHeight = 740 + ClientWidth = 416 + OnCreate = FormCreate + LCLVersion = '1.2.4.0' + object btnMakeNewDB: TButton + Left = 144 + Height = 25 + Top = 88 + Width = 120 + Caption = 'Make New DB' + OnClick = btnMakeNewDBClick + TabOrder = 0 + end + object txtNew: TEdit + Left = 176 + Height = 23 + Top = 160 + Width = 120 + TabOrder = 1 + end + object btnReKeyDB: TButton + Left = 144 + Height = 25 + Top = 200 + Width = 120 + Caption = 'ReKey DB' + OnClick = btnReKeyDBClick + TabOrder = 2 + end + object Label2: TLabel + Left = 112 + Height = 15 + Top = 164 + Width = 53 + Caption = 'New Pass:' + ParentColor = False + end + object txtPass: TEdit + Left = 176 + Height = 23 + Top = 16 + Width = 120 + TabOrder = 3 + end + object Label3: TLabel + Left = 88 + Height = 15 + Top = 20 + Width = 77 + Caption = 'Database Pass:' + ParentColor = False + end + object Shape1: TShape + AnchorSideLeft.Control = Owner + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + Left = 0 + Height = 1 + Top = 128 + Width = 416 + Anchors = [akTop, akLeft, akRight] + end + object Shape2: TShape + AnchorSideLeft.Control = Owner + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + Left = 0 + Height = 1 + Top = 240 + Width = 416 + Anchors = [akTop, akLeft, akRight] + end + object btnViewAppID: TButton + Left = 280 + Height = 25 + Top = 704 + Width = 120 + Caption = 'View Application_ID' + OnClick = btnViewAppIDClick + TabOrder = 4 + end + object btnViewUserVersion: TButton + Left = 280 + Height = 25 + Top = 658 + Width = 120 + Caption = 'View User_Version' + OnClick = btnViewUserVersionClick + TabOrder = 5 + end + object btnSetAppID: TButton + Left = 152 + Height = 25 + Top = 704 + Width = 120 + Caption = 'Set Application_ID' + OnClick = btnSetAppIDClick + TabOrder = 6 + end + object btnSetUserVersion: TButton + Left = 152 + Height = 25 + Top = 658 + Width = 120 + Caption = 'Set User_Version' + OnClick = btnSetUserVersionClick + TabOrder = 7 + end + object txtUser_Version: TEdit + Left = 24 + Height = 23 + Hint = 'Must be a 32-bit Signed Integer (LongInt -2147483648 .. 2147483647)' + Top = 658 + Width = 120 + ParentShowHint = False + ShowHint = True + TabOrder = 8 + Text = '0' + end + object txtApplication_ID: TEdit + Left = 24 + Height = 23 + Hint = 'Must be a 32-bit Unsigned Integer (Longword 0 .. 4294967295)' + Top = 704 + Width = 120 + ParentShowHint = False + ShowHint = True + TabOrder = 9 + Text = '0' + end + object Label4: TLabel + Left = 24 + Height = 15 + Top = 642 + Width = 70 + Caption = 'User_Version:' + ParentColor = False + end + object Label5: TLabel + Left = 24 + Height = 15 + Top = 688 + Width = 79 + Caption = 'Application_Id:' + ParentColor = False + end + object Label6: TLabel + Left = 16 + Height = 50 + Top = 584 + Width = 384 + AutoSize = False + Caption = 'Database Pragma Settings:'#13#10'user_version originally set to 23400001'#13#10'application_id originally set to 1189021115' + ParentColor = False + WordWrap = True + end + object Label7: TLabel + Left = 15 + Height = 15 + Top = 64 + Width = 88 + Caption = 'Create Database:' + ParentColor = False + end + object Label8: TLabel + Left = 16 + Height = 15 + Top = 136 + Width = 137 + Caption = 'Change the Database Key:' + ParentColor = False + end + object Shape3: TShape + AnchorSideLeft.Control = Owner + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + Left = 0 + Height = 1 + Top = 568 + Width = 416 + Anchors = [akTop, akLeft, akRight] + end + object Label9: TLabel + Left = 16 + Height = 15 + Top = 248 + Width = 117 + Caption = 'Add Info To Database:' + ParentColor = False + end + object txtUser_Name: TEdit + Left = 176 + Height = 23 + Top = 272 + Width = 120 + TabOrder = 10 + end + object txtInfo: TEdit + Left = 176 + Height = 23 + Top = 296 + Width = 120 + TabOrder = 11 + end + object btnAddToDB: TButton + Left = 144 + Height = 25 + Top = 336 + Width = 120 + Caption = 'Add To DB' + OnClick = btnAddToDBClick + TabOrder = 12 + end + object Label10: TLabel + Left = 120 + Height = 15 + Top = 276 + Width = 35 + Caption = 'Name:' + ParentColor = False + end + object Label11: TLabel + Left = 120 + Height = 15 + Top = 300 + Width = 24 + Caption = 'Info:' + ParentColor = False + end + object Shape4: TShape + AnchorSideLeft.Control = Owner + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + Left = 0 + Height = 1 + Top = 376 + Width = 416 + Anchors = [akTop, akLeft, akRight] + end + object Label12: TLabel + Left = 15 + Height = 15 + Top = 384 + Width = 83 + Caption = 'Query Database' + ParentColor = False + end + object btnUpdateGrid: TButton + Left = 64 + Height = 25 + Top = 528 + Width = 120 + Caption = 'Update Grid' + OnClick = btnUpdateGridClick + TabOrder = 13 + end + object DBGrid1: TDBGrid + Left = 8 + Height = 104 + Top = 408 + Width = 400 + Color = clWindow + Columns = <> + ReadOnly = True + TabOrder = 14 + end + object Shape5: TShape + AnchorSideLeft.Control = Owner + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + Left = 0 + Height = 1 + Top = 56 + Width = 416 + Anchors = [akTop, akLeft, akRight] + end + object btnCountRows: TButton + Left = 224 + Height = 25 + Top = 528 + Width = 120 + Caption = 'Count Rows' + OnClick = btnCountRowsClick + TabOrder = 15 + end + object DataSource1: TDataSource + DataSet = SQLQuery1 + left = 376 + top = 8 + end + object SQLQuery1: TSQLQuery + FieldDefs = <> + Database = SQLite3Connection1 + Transaction = SQLTransaction1 + Params = <> + left = 376 + top = 72 + end + object SQLTransaction1: TSQLTransaction + Active = False + Database = SQLite3Connection1 + left = 376 + top = 144 + end + object SQLite3Connection1: TSQLite3Connection + Connected = False + LoginPrompt = False + KeepConnection = False + Transaction = SQLTransaction1 + LogEvents = [] + Options = [] + left = 376 + top = 200 + end +end diff --git a/examples/database/sqlite_encryption_pragma/unit1.pas b/examples/database/sqlite_encryption_pragma/unit1.pas new file mode 100644 index 0000000000..93e44d092b --- /dev/null +++ b/examples/database/sqlite_encryption_pragma/unit1.pas @@ -0,0 +1,455 @@ +unit Unit1; + +//////////////////////////////////////////////////////////////////////////////// +// // +// This is free and unencumbered software released into the public domain. // +// // +// Anyone is free to copy, modify, publish, use, compile, sell, or // +// distribute this software, either in source code form or as a compiled // +// binary, for any purpose, commercial or non-commercial, and by any // +// means. // +// // +// In jurisdictions that recognize copyright laws, the author or authors // +// of this software dedicate any and all copyright interest in the // +// software to the public domain. We make this dedication for the benefit // +// of the public at large and to the detriment of our heirs and // +// successors. We intend this dedication to be an overt act of // +// relinquishment in perpetuity of all present and future rights to this // +// software under copyright law. // +// // +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR // +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // +// OTHER DEALINGS IN THE SOFTWARE. // +// // +// For more information, please refer to <http://unlicense.org/> // +// // +//////////////////////////////////////////////////////////////////////////////// + + +// For this test application, I wanted to very simply try the following +// capabilities which I'll be using in a large application: +// - Creation of a SQLite3 Database +// - Creation of a database table +// - Setting various database metadata (PRAGMA) +// - Optionally encrypt the database using a key +// - Change (or set if not initially set) the encryption key for the database + +// The application makes a new database file "new.db" within the local directory +// See readme.txt for installation instructions and details + + + + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, db, sqldb, sqlite3conn, FileUtil, Forms, Controls, + Graphics, Dialogs, StdCtrls, ExtCtrls, DBGrids; + +type + + { TForm1 } + + TForm1 = class(TForm) + btnMakeNewDB: TButton; + btnReKeyDB: TButton; + btnViewAppID: TButton; + btnSetAppID: TButton; + btnViewUserVersion: TButton; + btnSetUserVersion: TButton; + btnAddToDB: TButton; + btnUpdateGrid: TButton; + btnCountRows: TButton; + DataSource1: TDataSource; + DBGrid1: TDBGrid; + Label12: TLabel; + Shape4: TShape; + Shape5: TShape; + txtUser_Name: TEdit; + txtInfo: TEdit; + Label10: TLabel; + Label11: TLabel; + Label3: TLabel; + Label4: TLabel; + Label5: TLabel; + Label6: TLabel; + Label7: TLabel; + Label8: TLabel; + Label9: TLabel; + Shape1: TShape; + Shape2: TShape; + Shape3: TShape; + txtNew: TEdit; + txtApplication_ID: TEdit; + Label2: TLabel; + SQLite3Connection1: TSQLite3Connection; + SQLQuery1: TSQLQuery; + SQLTransaction1: TSQLTransaction; + txtUser_Version: TEdit; + txtPass: TEdit; + procedure btnAddToDBClick(Sender: TObject); + procedure btnMakeNewDBClick(Sender: TObject); + procedure btnReKeyDBClick(Sender: TObject); + procedure btnSetAppIDClick(Sender: TObject); + procedure btnSetUserVersionClick(Sender: TObject); + procedure btnViewAppIDClick(Sender: TObject); + procedure btnViewUserVersionClick(Sender: TObject); + procedure btnUpdateGridClick(Sender: TObject); + procedure btnCountRowsClick(Sender: TObject); + procedure FormCreate(Sender: TObject); + private + { private declarations } + public + { public declarations } + const + // More information on the use of these values is below. + // They need not be set as constants in your application. They can be any valid value + application_id = 1189021115; // must be a 32-bit Unsigned Integer (Longword 0 .. 4294967295) + user_version = 23400001; // must be a 32-bit Signed Integer (LongInt -2147483648 .. 2147483647) + + end; + +var + Form1: TForm1; + +implementation + +{$R *.lfm} + +{ TForm1 } + +procedure TForm1.btnMakeNewDBClick(Sender: TObject); +var + newFile : Boolean; +begin + + SQLite3Connection1.Close; // Ensure the connection is closed when we start + + // Set the password initially. + // Could probably be done with a PRAGMA statement, but this is so much simpler + // and once set, doesn't need to be reset every time we open the database. + // txtPass can be left blank if you want an unencrypted database. + SQLite3Connection1.Password := txtPass.Text; + + 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 + + // Make the database and the tables + try + SQLite3Connection1.Open; + SQLTransaction1.Active := true; + + + // Per the SQLite Documentation (edited for clarity): + // The pragma user_version is used to set or get the value of the user-version. + // The user-version is a big-endian 32-bit signed integer stored in the database header at offset 60. + // The user-version is not used internally by SQLite. It may be used by applications for any purpose. + // http://www.sqlite.org/pragma.html#pragma_schema_version + SQLite3Connection1.ExecuteDirect('PRAGMA user_version = ' + IntToStr(user_version) + ';'); + + + // Per the SQLite Documentation: + // The application_id PRAGMA is used to query or set the 32-bit unsigned big-endian + // "Application ID" integer located at offset 68 into the database header. + // Applications that use SQLite as their application file-format should set the + // Application ID integer to a unique integer so that utilities such as file(1) can + // determine the specific file type rather than just reporting "SQLite3 Database". + // A list of assigned application IDs can be seen by consulting the magic.txt file + // in the SQLite source repository. + // http://www.sqlite.org/pragma.html#pragma_application_id + SQLite3Connection1.ExecuteDirect('PRAGMA application_id = ' + IntToStr(application_id) + ';'); + + + // 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; + +procedure TForm1.btnAddToDBClick(Sender: TObject); +begin + SQLite3Connection1.Password := txtPass.Text; // The current password + + if (txtUser_Name.Text = '') OR (txtInfo.Text = '') then + begin + ShowMessage('Please enter both a Name and Info'); + end + else + begin + + // Attempt to add txtUser_Name and txtInfo to the database + try + SQLite3Connection1.Open; + SQLTransaction1.Active := True; + + // Insert the values into the database + // We're using ParamByName which prevents SQL Injection + // http://wiki.freepascal.org/Working_With_TSQLQuery#Parameters_in_TSQLQuery.SQL + SQLQuery1.SQL.Text := 'Insert into DATA (Current_Time,User_Name,Info) values (:Current_Time,:User_Name,:Info)'; + SQLQuery1.Params.ParamByName('Current_Time').AsDateTime := Now; + SQLQuery1.Params.ParamByName('User_Name').AsString := txtUser_Name.Text; + SQLQuery1.Params.ParamByName('Info').AsString := txtInfo.Text; + SQLQuery1.ExecSQL; + + SQLTransaction1.Commit; + + // Clear Edit boxes + txtUser_Name.Text := ''; + txtInfo.Text := ''; + + // Now let's update the grid to show the new values to the user: + btnUpdateGridClick(nil); + except + ShowMessage('Unable to add User_Name: ' + txtUser_Name.Text + ' and Info: ' + txtInfo.Text + ' to the database. Ensure database exists and password is correct.'); + end; + + end; + +end; + +procedure TForm1.btnReKeyDBClick(Sender: TObject); +begin + + SQLite3Connection1.Close; // Ensure the connection is closed when we start + + SQLite3Connection1.Password := txtPass.Text; // The current password + + // Update the database key + try + SQLite3Connection1.Open; + SQLTransaction1.Active := True; + + + // Here we change the key. + // We use double-quotes here so that a blank key (IE: "") can be provided if + // you want to remove encryption from the database. + // This is a very simplistic demonstration. Ideally, we would take a stronger cryptographic approach + // Some helpful info on this topic can be found at: + // https://www.owasp.org/index.php/Cheat_Sheets + // Per SQLite Documentation: + // Note that the hexkey, rekey and hexrekey pragmas only work with SQLite version 3.6.8 and later. + // http://www.sqlite.org/see/doc/trunk/www/readme.wiki + // Section: Using the "key" PRAGMA + SQLite3Connection1.ExecuteDirect('PRAGMA rekey = ' + QuotedStr(txtNew.Text) + ';'); + + + SQLTransaction1.Commit; + SQLite3Connection1.Close; + + // Transfer the password to txtPass and erase txtNew + txtPass.Text := txtNew.Text; + txtNew.Text := ''; + + // ... and make sure we remember the new password in our sqlconnection ready + // for reconnecting + SQLite3Connection1.Password := txtPass.Text; + + ShowMessage('Password rekey succesful.'); + + except + ShowMessage('Unable to set the new key using: PRAGMA rekey = ' + txtNew.Text + ';'); + end; + +end; + +procedure TForm1.btnSetAppIDClick(Sender: TObject); +begin + + SQLite3Connection1.Close; // Ensure the connection is closed when we start + + SQLite3Connection1.Password := txtPass.Text; // The current password + + // Try to set the application_id Pragma + try + SQLite3Connection1.Open; + SQLTransaction1.Active := True; + + + SQLQuery1.SQL.Text := 'PRAGMA application_id = ' + txtApplication_ID.Text + ';'; + SQLQuery1.ExecSQL; + + + SQLTransaction1.Commit; + SQLite3Connection1.Close; + + ShowMessage('SetAppID succesful'); + + except + ShowMessage('Unable to set new application_id: ' + txtApplication_ID.Text + ';'); + end; + +end; + +procedure TForm1.btnSetUserVersionClick(Sender: TObject); +begin + + SQLite3Connection1.Close; // Ensure the connection is closed when we start + + SQLite3Connection1.Password := txtPass.Text; // The current password + + // Try to set the user_version Pragma + try + SQLite3Connection1.Open; + SQLTransaction1.Active := True; + + + SQLQuery1.SQL.Text := 'PRAGMA user_version = ' + txtUser_Version.Text + ';'; + SQLQuery1.ExecSQL; + + + SQLTransaction1.Commit; + SQLite3Connection1.Close; + + ShowMessage('SetUserVersion succesful.'); + + except + ShowMessage('Unable to set user_version: ' + txtUser_Version.Text + ';'); + end; + +end; + +procedure TForm1.btnViewAppIDClick(Sender: TObject); +begin + + SQLite3Connection1.Close; // Ensure the connection is closed when we start + + SQLite3Connection1.Password := txtPass.Text; // The current password + + // Try to query database for application_id Pragma + try + SQLite3Connection1.Open; + + SQLQuery1.SQL.Text := 'PRAGMA application_id;'; + SQLQuery1.Open; + + // Display the resulting value + ShowMessage('application_id is: '+SQLQuery1.fields[0].asString); + + except + ShowMessage('Unable to display application_id'); + end; + +end; + +procedure TForm1.btnViewUserVersionClick(Sender: TObject); +begin + + SQLite3Connection1.Close; // Ensure the connection is closed when we start + + SQLite3Connection1.Password := txtPass.Text; // The current password + + // Try to query database for user_version Pragma + try + SQLite3Connection1.Open; + + SQLQuery1.SQL.Text := 'PRAGMA user_version;'; + SQLQuery1.Open; + + // Display the resulting value + ShowMessage('user_version is: '+SQLQuery1.fields[0].asString); + + except + ShowMessage('Unable to display user_version'); + end; + +end; + +procedure TForm1.btnUpdateGridClick(Sender: TObject); +begin + + SQLite3Connection1.Password := txtPass.Text; // The current password + + // Try to perform query + try + SQLite3Connection1.Connected := True; + + // Set SQL text to select everything from the DATA table + SQLQuery1.SQL.Clear; + SQLQuery1.SQL.Text := 'Select * from DATA'; + SQLQuery1.Open; + + // Allow the DBGrid to view the results of our query + DataSource1.DataSet := SQLQuery1; + DBGrid1.DataSource := DataSource1; + DBGrid1.AutoFillColumns := true; + + except + ShowMessage('Unable to query the database'); + end; + +end; + +procedure TForm1.btnCountRowsClick(Sender: TObject); +begin + + SQLite3Connection1.Close; // Ensure the connection is closed when we start + + SQLite3Connection1.Password := txtPass.Text; // The current password + + // Try to perform query + try + SQLite3Connection1.Connected := True; + + // Set SQL text to count all rows from the DATA table + SQLQuery1.SQL.Clear; + SQLQuery1.SQL.Text := 'Select Count(*) from DATA'; + SQLQuery1.Open; + + // Allow the DBGrid to view the results of our query + DataSource1.DataSet := SQLQuery1; + DBGrid1.DataSource := DataSource1; + DBGrid1.AutoFillColumns := true; + + except + ShowMessage('Unable to query the database'); + end; + +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin + + // Ensure we're using the local sqlite3.dll + SQLiteLibraryName := 'sqlite3.dll'; + + // Set the path to the database + SQLite3Connection1.DatabaseName := 'new.db'; + +end; + +end. +