1 {*********************************************************}
3 { Zeos Database Objects }
4 { SQLite Database Connectivity Classes }
6 { Originally written by Sergey Seroukhov }
8 {*********************************************************}
10 {@********************************************************}
11 { Copyright (c) 1999-2012 Zeos Development Group }
13 { License Agreement: }
15 { This library is distributed in the hope that it will be }
16 { useful, but WITHOUT ANY WARRANTY; without even the }
17 { implied warranty of MERCHANTABILITY or FITNESS FOR }
18 { A PARTICULAR PURPOSE. See the GNU Lesser General }
19 { Public License for more details. }
21 { The source code of the ZEOS Libraries and packages are }
22 { distributed under the Library GNU General Public }
23 { License (see the file COPYING / COPYING.ZEOS) }
24 { with the following modification: }
25 { As a special exception, the copyright holders of this }
26 { library give you permission to link this library with }
27 { independent modules to produce an executable, }
28 { regardless of the license terms of these independent }
29 { modules, and to copy and distribute the resulting }
30 { executable under terms of your choice, provided that }
31 { you also meet, for each linked independent module, }
32 { the terms and conditions of the license of that module. }
33 { An independent module is a module which is not derived }
34 { from or based on this library. If you modify this }
35 { library, you may extend this exception to your version }
36 { of the library, but you are not obligated to do so. }
37 { If you do not wish to do so, delete this exception }
38 { statement from your version. }
41 { The project web site is located on: }
42 { http://zeos.firmos.at (FORUM) }
43 { http://sourceforge.net/p/zeoslib/tickets/ (BUGTRACKER)}
44 { svn://svn.code.sf.net/p/zeoslib/code-0/trunk (SVN) }
46 { http://www.sourceforge.net/projects/zeoslib. }
49 { Zeos Development Group. }
50 {********************************************************@}
59 Types, Classes, {$IFDEF MSEgui}mclasses,{$ENDIF} SysUtils,
60 ZDbcIntfs, ZDbcConnection, ZPlainSqLiteDriver, ZDbcLogging, ZTokenizer,
61 ZGenericSqlAnalyser, ZURL, ZPlainDriver, ZCompatibility;
65 {** Implements SQLite Database Driver. }
67 TZSQLiteDriver = class(TZAbstractDriver)
69 constructor Create; override;
70 function Connect(const Url: TZURL): IZConnection; override;
71 function GetMajorVersion: Integer; override;
72 function GetMinorVersion: Integer; override;
74 function GetTokenizer: IZTokenizer; override;
75 function GetStatementAnalyser: IZStatementAnalyser; override;
79 {** Represents a SQLite specific connection interface. }
80 IZSQLiteConnection = interface (IZConnection)
81 ['{A4B797A9-7CF7-4DE9-A5BB-693DD32D07D2}']
82 function UseOldBlobEncoding: Boolean;
83 function GetPlainDriver: IZSQLitePlainDriver;
84 function GetConnectionHandle: Psqlite;
87 {** Implements SQLite Database Connection. }
89 { TZSQLiteConnection }
91 TZSQLiteConnection = class(TZAbstractConnection, IZSQLiteConnection)
95 function UseOldBlobEncoding: Boolean;
97 procedure InternalCreate; override;
98 procedure StartTransactionSupport;
101 destructor Destroy; override;
103 function CreateRegularStatement(Info: TStrings): IZStatement; override;
104 function CreatePreparedStatement(const SQL: string; Info: TStrings):
105 IZPreparedStatement; override;
107 procedure Commit; override;
108 procedure Rollback; override;
110 procedure Open; override;
111 procedure Close; override;
113 procedure SetCatalog(const Catalog: string); override;
114 function GetCatalog: string; override;
116 procedure SetTransactionIsolation(Level: TZTransactIsolationLevel); override;
118 function GetClientVersion: Integer; override;
119 function GetHostVersion: Integer; override;
121 function GetPlainDriver: IZSQLitePlainDriver;
122 function GetConnectionHandle: Psqlite;
124 function ReKey(const Key: string): Integer;
125 function Key(const Key: string): Integer;
126 function GetBinaryEscapeString(const Value: RawByteString): String; overload; override;
127 function GetBinaryEscapeString(const Value: TByteDynArray): String; overload; override;
128 {$IFDEF ZEOS_TEST_ONLY}
129 constructor Create(const ZUrl: TZURL);
134 {** The common driver manager object. }
135 SQLiteDriver: IZDriver;
140 ZSysUtils, ZDbcUtils, ZDbcSqLiteStatement, ZSqLiteToken,
141 ZDbcSqLiteUtils, ZDbcSqLiteMetadata, ZSqLiteAnalyser
142 {$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
147 Constructs this object with default properties.
149 constructor TZSQLiteDriver.Create;
152 AddSupportedProtocol(AddPlainDriverToCache(TZSQLite3PlainDriver.Create, 'sqlite'));
153 AddSupportedProtocol(AddPlainDriverToCache(TZSQLite3PlainDriver.Create));
157 Attempts to make a database connection to the given URL.
158 The driver should return "null" if it realizes it is the wrong kind
159 of driver to connect to the given URL. This will be common, as when
160 the JDBC driver manager is asked to connect to a given URL it passes
161 the URL to each loaded driver in turn.
163 <P>The driver should raise a SQLException if it is the right
164 driver to connect to the given URL, but has trouble connecting to
167 <P>The java.util.Properties argument can be used to passed arbitrary
168 string tag/value pairs as connection arguments.
169 Normally at least "user" and "password" properties should be
170 included in the Properties.
172 @param url the URL of the database to which to connect
173 @param info a list of arbitrary string tag/value pairs as
174 connection arguments. Normally at least a "user" and
175 "password" property should be included.
176 @return a <code>Connection</code> object that represents a
177 connection to the URL
180 function TZSQLiteDriver.Connect(const Url: TZURL): IZConnection;
182 Result := TZSQLiteConnection.Create(Url);
187 Gets the driver's major version number. Initially this should be 1.
188 @return this driver's major version number
190 function TZSQLiteDriver.GetMajorVersion: Integer;
196 Gets the driver's minor version number. Initially this should be 0.
197 @return this driver's minor version number
199 function TZSQLiteDriver.GetMinorVersion: Integer;
205 Gets a SQL syntax tokenizer.
206 @returns a SQL syntax tokenizer object.
208 function TZSQLiteDriver.GetTokenizer: IZTokenizer;
210 Result := TZSQLiteTokenizer.Create; { thread save! Allways return a new Tokenizer! }
214 Creates a statement analyser object.
215 @returns a statement analyser object.
217 function TZSQLiteDriver.GetStatementAnalyser: IZStatementAnalyser;
219 Result := TZSQLiteStatementAnalyser.Create; { thread save! Allways return a new Analyser! }
222 { TZSQLiteConnection }
225 Constructs this object and assignes the main properties.
227 procedure TZSQLiteConnection.InternalCreate;
229 FMetadata := TZSQLiteDatabaseMetadata.Create(Self, Url);
231 TransactIsolationLevel := tiNone;
232 CheckCharEncoding('UTF-8');
237 Destroys this object and cleanups the memory.
239 destructor TZSQLiteConnection.Destroy;
244 function TZSQLiteConnection.UseOldBlobEncoding: Boolean;
246 Result := Url.Properties.Values['OldBlobEncoding'] = 'True';
250 Set encryption key for a database
251 @param Key the key used to encrypt your database.
252 @return error code from SQLite Key function.
254 function TZSQLiteConnection.Key(const Key: string):Integer;
259 ErrorCode := GetPlainDriver.Key(FHandle, PAnsiChar(UTF8String(Key)), {$IFDEF WITH_STRLEN_DEPRECATED}AnsiStrings.{$ENDIF}StrLen(PAnsiChar(UTF8String(Key))));
261 ErrorCode := GetPlainDriver.Key(FHandle, PAnsiChar(Key), StrLen(PAnsiChar(Key)));
267 Reencrypt a database with a new key. The old/current key needs to be
268 set before calling this function.
269 @param Key the new key used to encrypt your database.
270 @return error code from SQLite ReKey function.
272 function TZSQLiteConnection.ReKey(const Key: string):Integer;
277 ErrorCode := GetPlainDriver.ReKey(FHandle, PAnsiChar(UTF8String(Key)), {$IFDEF WITH_STRLEN_DEPRECATED}AnsiStrings.{$ENDIF}StrLen(PAnsiChar(UTF8String(Key))));
279 ErrorCode := GetPlainDriver.ReKey(FHandle, PAnsiChar(Key), StrLen(PAnsiChar(Key)));
285 Opens a connection to database server with specified parameters.
287 procedure TZSQLiteConnection.Open;
290 ErrorMessage: PAnsiChar;
299 LogMessage := Format('CONNECT TO "%s" AS USER "%s"', [Database, User]);
302 FHandle := GetPlainDriver.Open(PAnsiChar(AnsiString(UTF8Encode(Database))), 0, ErrorMessage);
304 FHandle := GetPlainDriver.Open(PAnsiChar(Database), 0, ErrorMessage);
307 if FHandle = nil then
309 CheckSQLiteError(GetPlainDriver, FHandle, SQLITE_ERROR, ErrorMessage,
310 lcConnect, LogMessage);
312 DriverManager.LogMessage(lcConnect, PlainDriver.GetProtocol, LogMessage);
314 { Turn on encryption if requested }
315 if StrToBoolEx(Info.Values['encrypted']) then
318 ErrorCode := GetPlainDriver.Key(FHandle, PAnsiChar(UTF8String(Password)), {$IFDEF WITH_STRLEN_DEPRECATED}AnsiStrings.{$ENDIF}StrLen(PAnsiChar(UTF8String(Password))));
320 ErrorCode := GetPlainDriver.Key(FHandle, PAnsiChar(Password), StrLen(PAnsiChar(Password)));
322 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, nil, lcConnect, 'SQLite.Key');
325 { Set busy timeout if requested }
326 Timeout_ms := StrToIntDef(Info.Values['busytimeout'], -1);
327 if Timeout_ms >= 0 then
329 GetPlainDriver.BusyTimeout(FHandle, Timeout_ms);
333 if ( FClientCodePage <> '' ) then
335 SQL := 'PRAGMA encoding = '''+AnsiString(FClientCodePage)+'''';
336 ErrorCode := GetPlainDriver.Execute(FHandle, PAnsiChar(SQL),
337 nil, nil, ErrorMessage);
338 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, String(SQL));
341 SQL := 'PRAGMA show_datatypes = ON';
342 ErrorCode := GetPlainDriver.Execute(FHandle, PAnsiChar(SQL),
343 nil, nil, ErrorMessage);
344 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, String(SQL));
346 if Info.Values['foreign_keys'] <> '' then
348 if StrToBoolEx(Info.Values['foreign_keys']) then
349 SQL := 'PRAGMA foreign_keys = 1'
351 SQL := 'PRAGMA foreign_keys = 0';
352 ErrorCode := GetPlainDriver.Execute(FHandle, PAnsiChar(SQL), nil, nil, ErrorMessage);
353 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, String(SQL));
354 DriverManager.LogMessage(lcConnect, GetPlainDriver.GetProtocol, String(SQL));
356 StartTransactionSupport;
358 GetPlainDriver.Close(FHandle);
367 Creates a <code>Statement</code> object for sending
368 SQL statements to the database.
369 SQL statements without parameters are normally
370 executed using Statement objects. If the same SQL statement
371 is executed many times, it is more efficient to use a
372 <code>PreparedStatement</code> object.
374 Result sets created using the returned <code>Statement</code>
375 object will by default have forward-only type and read-only concurrency.
377 @param Info a statement parameters.
378 @return a new Statement object
380 function TZSQLiteConnection.CreateRegularStatement(Info: TStrings):
385 Result := TZSQLiteStatement.Create(GetPlainDriver, Self, Info, FHandle);
389 Creates a <code>PreparedStatement</code> object for sending
390 parameterized SQL statements to the database.
392 A SQL statement with or without IN parameters can be
393 pre-compiled and stored in a PreparedStatement object. This
394 object can then be used to efficiently execute this statement
397 <P><B>Note:</B> This method is optimized for handling
398 parametric SQL statements that benefit from precompilation. If
399 the driver supports precompilation,
400 the method <code>prepareStatement</code> will send
401 the statement to the database for precompilation. Some drivers
402 may not support precompilation. In this case, the statement may
403 not be sent to the database until the <code>PreparedStatement</code> is
404 executed. This has no direct effect on users; however, it does
405 affect which method throws certain SQLExceptions.
407 Result sets created using the returned PreparedStatement will have
408 forward-only type and read-only concurrency, by default.
410 @param sql a SQL statement that may contain one or more '?' IN
411 parameter placeholders
412 @param Info a statement parameters.
413 @return a new PreparedStatement object containing the
414 pre-compiled statement
416 function TZSQLiteConnection.CreatePreparedStatement(const SQL: string;
417 Info: TStrings): IZPreparedStatement;
421 {$IFDEF ZEOS_TEST_ONLY}
425 Result := TZSQLiteCAPIPreparedStatement.Create(GetPlainDriver, Self, SQL, Info, FHandle);
426 {$IFDEF ZEOS_TEST_ONLY}
427 1: Result := TZSQLitePreparedStatement.Create(GetPlainDriver, Self, SQL, Info, FHandle);
433 Starts a transaction support.
435 procedure TZSQLiteConnection.StartTransactionSupport;
438 ErrorMessage: PAnsiChar;
441 if TransactIsolationLevel <> tiNone then
444 SQL := 'BEGIN TRANSACTION';
445 ErrorCode := GetPlainDriver.Execute(FHandle, PAnsiChar(AnsiString(SQL)), nil, nil,
447 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, SQL);
448 DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, SQL);
453 Makes all changes made since the previous
454 commit/rollback permanent and releases any database locks
455 currently held by the Connection. This method should be
456 used only when auto-commit mode has been disabled.
459 procedure TZSQLiteConnection.Commit;
462 ErrorMessage: PAnsiChar;
465 if (TransactIsolationLevel <> tiNone) and not Closed then
468 SQL := 'COMMIT TRANSACTION';
469 ErrorCode := GetPlainDriver.Execute(FHandle, PAnsiChar(SQL), nil, nil,
471 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, String(SQL));
472 DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, String(SQL));
474 StartTransactionSupport;
479 Drops all changes made since the previous
480 commit/rollback and releases any database locks currently held
481 by this Connection. This method should be used only when auto-
482 commit has been disabled.
485 procedure TZSQLiteConnection.Rollback;
488 ErrorMessage: PAnsiChar;
491 if (TransactIsolationLevel <> tiNone) and not Closed then
494 SQL := 'ROLLBACK TRANSACTION';
495 ErrorCode := GetPlainDriver.Execute(FHandle, PAnsiChar(AnsiString(SQL)), nil, nil,
497 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, SQL);
498 DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, SQL);
500 StartTransactionSupport;
505 Releases a Connection's database and JDBC resources
506 immediately instead of waiting for
507 them to be automatically released.
509 <P><B>Note:</B> A Connection is automatically closed when it is
510 garbage collected. Certain fatal errors also result in a closed
513 procedure TZSQLiteConnection.Close;
518 if ( Closed ) or (not Assigned(PlainDriver)) then
521 LogMessage := 'DISCONNECT FROM "'+Database+'"';
522 if Assigned(DriverManager) then
523 DriverManager.LogMessage(lcDisconnect, PlainDriver.GetProtocol, LogMessage);
524 ErrorCode := GetPlainDriver.Close(FHandle);
525 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, nil,
526 lcOther, LogMessage);
532 Gets a selected catalog name.
533 @return a selected catalog name.
535 function TZSQLiteConnection.GetCatalog: string;
540 function TZSQLiteConnection.GetClientVersion: Integer;
542 Result := ConvertSQLiteVersionToSQLVersion(GetPlainDriver.LibVersion);
546 Sets a new selected catalog name.
547 @param Catalog a selected catalog name.
549 procedure TZSQLiteConnection.SetCatalog(const Catalog: string);
555 Sets a new transact isolation level.
556 @param Level a new transact isolation level.
558 procedure TZSQLiteConnection.SetTransactionIsolation(
559 Level: TZTransactIsolationLevel);
562 ErrorMessage: PAnsiChar;
565 if (TransactIsolationLevel <> tiNone) and not Closed then
568 SQL := 'ROLLBACK TRANSACTION';
569 ErrorCode := GetPlainDriver.Execute(FHandle, PAnsiChar(AnsiString(SQL)), nil, nil,
571 CheckSQLiteError(GetPlainDriver, FHandle, ErrorCode, ErrorMessage, lcExecute, SQL);
572 DriverManager.LogMessage(lcExecute, PlainDriver.GetProtocol, SQL);
575 inherited SetTransactionIsolation(Level);
578 StartTransactionSupport;
582 Gets a reference to SQLite connection handle.
583 @return a reference to SQLite connection handle.
585 function TZSQLiteConnection.GetConnectionHandle: Psqlite;
591 Gets a SQLite plain driver interface.
592 @return a SQLite plain driver interface.
594 function TZSQLiteConnection.GetPlainDriver: IZSQLitePlainDriver;
596 Result := PlainDriver as IZSQLitePlainDriver;
601 Returns the BinaryString in a Tokenizer-detectable kind
602 If the Tokenizer don't need to predetect it Result := BinaryString
603 @param Value represents the Binary-String
604 @param EscapeMarkSequence represents a Tokenizer detectable EscapeSequence (Len >= 3)
605 @result the detectable Binary String
607 function TZSQLiteConnection.GetBinaryEscapeString(const Value: RawByteString): String;
609 if GetAutoEncodeStrings then
610 Result := GetDriver.GetTokenizer.AnsiGetEscapeString(ZDbcSqLiteUtils.EncodeString(PAnsiChar(Value), Length(Value)))
612 Result := String(ZDbcSqLiteUtils.EncodeString(PAnsiChar(Value), Length(Value)));
617 Returns the BinaryString in a Tokenizer-detectable kind
618 If the Tokenizer don't need to predetect it Result := BinaryString
619 @param Value represents the Binary-String
620 @param EscapeMarkSequence represents a Tokenizer detectable EscapeSequence (Len >= 3)
621 @result the detectable Binary String
623 function TZSQLiteConnection.GetBinaryEscapeString(const Value: TByteDynArray): String;
625 if GetAutoEncodeStrings then
626 Result := GetDriver.GetTokenizer.AnsiGetEscapeString(ZDbcSqLiteUtils.EncodeString(PAnsiChar(Value), Length(Value)))
628 Result := String(ZDbcSqLiteUtils.EncodeString(PAnsiChar(Value), Length(Value)));
631 {$IFDEF ZEOS_TEST_ONLY}
632 constructor TZSQLiteConnection.Create(const ZUrl: TZURL);
634 inherited Create(ZUrl);
638 function TZSQLiteConnection.GetHostVersion: Integer;
640 Result := ConvertSQLiteVersionToSQLVersion(GetPlainDriver.LibVersion);
644 SQLiteDriver := TZSQLiteDriver.Create;
645 DriverManager.RegisterDriver(SQLiteDriver);
647 if DriverManager <> nil then
648 DriverManager.DeregisterDriver(SQLiteDriver);