1 {*********************************************************}
3 { Zeos Database Objects }
4 { Interbase Database Connectivity Classes }
6 { Originally written by Sergey Merkuriev }
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, Contnrs,
60 ZPlainFirebirdDriver, ZPlainDriver, ZCompatibility, ZDbcUtils, ZDbcIntfs,
61 ZDbcConnection, ZPlainFirebirdInterbaseConstants, ZSysUtils, ZDbcLogging,
62 ZDbcInterbase6Utils, ZDbcGenericResolver, ZTokenizer, ZGenericSqlAnalyser,
67 {** Implements Interbase6 Database Driver. }
69 TZInterbase6Driver = class(TZAbstractDriver)
71 constructor Create; override;
72 function Connect(const Url: TZURL): IZConnection; override;
73 function GetMajorVersion: Integer; override;
74 function GetMinorVersion: Integer; override;
76 function GetTokenizer: IZTokenizer; override;
77 function GetStatementAnalyser: IZStatementAnalyser; override;
81 {** Represents a Interbase specific connection interface. }
82 IZInterbase6Connection = interface (IZConnection)
83 ['{E870E4FE-21EB-4725-B5D8-38B8A2B12D0B}']
84 function GetDBHandle: PISC_DB_HANDLE;
85 function GetTrHandle: PISC_TR_HANDLE;
86 function GetDialect: Word;
87 function GetPlainDriver: IZInterbasePlainDriver;
88 procedure CreateNewDatabase(const SQL: String);
91 {** Implements Interbase6 Database Connection. }
93 { TZInterbase6Connection }
95 TZInterbase6Connection = class(TZAbstractConnection, IZInterbase6Connection)
98 FHandle: TISC_DB_HANDLE;
99 FTrHandle: TISC_TR_HANDLE;
100 FStatusVector: TARRAY_ISC_STATUS;
101 FHardCommit: boolean;
102 FDisposeClientCodePage: Boolean;
103 FHostVersion: Integer;
104 procedure CloseTransaction;
106 procedure InternalCreate; override;
107 procedure OnPropertiesChange(Sender: TObject); override;
109 procedure StartTransaction;
110 procedure SetTransactionIsolation(Level: TZTransactIsolationLevel); override;
111 function GetHostVersion: Integer; override;
112 function GetDBHandle: PISC_DB_HANDLE;
113 function GetTrHandle: PISC_TR_HANDLE;
114 function GetDialect: Word;
115 function GetPlainDriver: IZInterbasePlainDriver;
116 procedure CreateNewDatabase(const SQL: String);
118 function CreateRegularStatement(Info: TStrings): IZStatement; override;
119 function CreatePreparedStatement(const SQL: string; Info: TStrings):
120 IZPreparedStatement; override;
121 function CreateCallableStatement(const SQL: string; Info: TStrings):
122 IZCallableStatement; override;
124 function CreateSequence(const Sequence: string; BlockSize: Integer):
125 IZSequence; override;
127 procedure SetReadOnly(Value: Boolean); override;
129 procedure Commit; override;
130 procedure Rollback; override;
132 function PingServer: Integer; override;
134 procedure Open; override;
135 procedure Close; override;
137 function GetBinaryEscapeString(const Value: RawByteString): String; override;
138 function GetBinaryEscapeString(const Value: TByteDynArray): String; override;
139 function GetEscapeString(const Value: RawByteString): RawByteString; override;
140 function GetEscapeString(const Value: ZWideString): ZWideString; override;
143 {** Implements a specialized cached resolver for Interbase/Firebird. }
144 TZInterbase6CachedResolver = class(TZGenericCachedResolver)
146 function FormCalculateStatement(Columns: TObjectList): string; override;
149 {** Implements a Interbase 6 sequence. }
150 TZInterbase6Sequence = class(TZAbstractSequence)
152 function GetCurrentValue: Int64; override;
153 function GetNextValue: Int64; override;
154 function GetCurrentValueSQL: string; override;
155 function GetNextValueSQL: string; override;
160 {** The common driver manager object. }
161 Interbase6Driver: IZDriver;
165 uses ZDbcInterbase6Statement, ZDbcInterbase6Metadata, ZEncoding,
166 ZInterbaseToken, ZInterbaseAnalyser
167 {$IFDEF WITH_UNITANSISTRINGS}, AnsiStrings{$ENDIF};
169 { TZInterbase6Driver }
172 Attempts to make a database connection to the given URL.
173 The driver should return "null" if it realizes it is the wrong kind
174 of driver to connect to the given URL. This will be common, as when
175 the JDBC driver manager is asked to connect to a given URL it passes
176 the URL to each loaded driver in turn.
178 <P>The driver should raise a SQLException if it is the right
179 driver to connect to the given URL, but has trouble connecting to
182 <P>The java.util.Properties argument can be used to passed arbitrary
183 string tag/value pairs as connection arguments.
184 Normally at least "user" and "password" properties should be
185 included in the Properties.
187 @param url the URL of the database to which to connect
188 @param info a list of arbitrary string tag/value pairs as
189 connection arguments. Normally at least a "user" and
190 "password" property should be included.
191 @return a <code>Connection</code> object that represents a
192 connection to the URL
195 function TZInterbase6Driver.Connect(const Url: TZURL): IZConnection;
197 Result := TZInterbase6Connection.Create(Url);
202 Constructs this object with default properties.
204 constructor TZInterbase6Driver.Create;
207 AddSupportedProtocol(AddPlainDriverToCache(TZInterbase6PlainDriver.Create));
208 AddSupportedProtocol(AddPlainDriverToCache(TZFirebird10PlainDriver.Create));
209 AddSupportedProtocol(AddPlainDriverToCache(TZFirebird15PlainDriver.Create));
210 AddSupportedProtocol(AddPlainDriverToCache(TZFirebird20PlainDriver.Create));
211 AddSupportedProtocol(AddPlainDriverToCache(TZFirebird21PlainDriver.Create));
212 AddSupportedProtocol(AddPlainDriverToCache(TZFirebird25PlainDriver.Create));
214 AddSupportedProtocol(AddPlainDriverToCache(TZFirebirdD15PlainDriver.Create));
215 AddSupportedProtocol(AddPlainDriverToCache(TZFirebirdD20PlainDriver.Create));
216 AddSupportedProtocol(AddPlainDriverToCache(TZFirebirdD21PlainDriver.Create));
217 AddSupportedProtocol(AddPlainDriverToCache(TZFirebirdD25PlainDriver.Create));
221 Gets the driver's major version number. Initially this should be 1.
222 @return this driver's major version number
224 function TZInterbase6Driver.GetMajorVersion: Integer;
230 Gets the driver's minor version number. Initially this should be 0.
231 @return this driver's minor version number
233 function TZInterbase6Driver.GetMinorVersion: Integer;
239 Gets a SQL syntax tokenizer.
240 @returns a SQL syntax tokenizer object.
242 function TZInterbase6Driver.GetTokenizer: IZTokenizer;
244 Result := TZInterbaseTokenizer.Create;
248 Creates a statement analyser object.
249 @returns a statement analyser object.
251 function TZInterbase6Driver.GetStatementAnalyser: IZStatementAnalyser;
253 Result := TZInterbaseStatementAnalyser.Create; { thread save! Allways return a new Analyser! }
256 { TZInterbase6Connection }
258 procedure TZInterbase6Connection.CloseTransaction;
260 if FTrHandle <> 0 then
264 GetPlainDriver.isc_commit_transaction(@FStatusVector, @FTrHandle);
265 DriverManager.LogMessage(lcTransaction, PlainDriver.GetProtocol,
266 Format('COMMIT TRANSACTION "%s"', [Database]));
270 GetPlainDriver.isc_rollback_transaction(@FStatusVector, @FTrHandle);
271 DriverManager.LogMessage(lcTransaction, PlainDriver.GetProtocol,
272 Format('ROLLBACK TRANSACTION "%s"', [Database]));
275 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcDisconnect);
280 Releases a Connection's database and JDBC resources
281 immediately instead of waiting for
282 them to be automatically released.
284 <P><B>Note:</B> A Connection is automatically closed when it is
285 garbage collected. Certain fatal errors also result in a closed
288 procedure TZInterbase6Connection.Close;
290 if Closed or (not Assigned(PlainDriver)) then
297 GetPlainDriver.isc_detach_database(@FStatusVector, @FHandle);
299 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcDisconnect);
302 DriverManager.LogMessage(lcConnect, PlainDriver.GetProtocol,
303 Format('DISCONNECT FROM "%s"', [Database]));
309 Commit current transaction
311 procedure TZInterbase6Connection.Commit;
316 if FTrHandle <> 0 then
320 GetPlainDriver.isc_commit_transaction(@FStatusVector, @FTrHandle);
321 FTrHandle := 0; //normaly not required! Old server code?
324 GetPlainDriver.isc_commit_retaining(@FStatusVector, @FTrHandle);
326 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcTransaction);
327 DriverManager.LogMessage(lcTransaction,
328 PlainDriver.GetProtocol, 'TRANSACTION COMMIT');
333 Constructs this object and assignes the main properties.
335 procedure TZInterbase6Connection.InternalCreate;
338 ConnectTimeout : integer;
340 FDisposeClientCodePage := False;
341 Self.FMetadata := TZInterbase6DatabaseMetadata.Create(Self, Url);
343 FHardCommit := StrToBoolEx(URL.Properties.Values['hard_commit']);
344 { Sets a default Interbase port }
346 if Self.Port = 0 then
349 { set default sql dialect it can be overriden }
352 FDialect := StrToIntDef(URL.Properties.Values['dialect'], FDialect);
354 { Processes connection properties. }
355 self.Info.Values['isc_dpb_username'] := Url.UserName;
356 self.Info.Values['isc_dpb_password'] := Url.Password;
358 if FClientCodePage = '' then //was set on inherited Create(...)
359 if URL.Properties.Values['isc_dpb_lc_ctype'] <> '' then //Check if Dev set's it manually
361 FClientCodePage := URL.Properties.Values['isc_dpb_lc_ctype'];
362 CheckCharEncoding(FClientCodePage, True);
364 URL.Properties.Values['isc_dpb_lc_ctype'] := FClientCodePage;
366 RoleName := Trim(URL.Properties.Values['rolename']);
367 if RoleName <> '' then
368 URL.Properties.Values['isc_dpb_sql_role_name'] := UpperCase(RoleName);
370 ConnectTimeout := StrToIntDef(URL.Properties.Values['timeout'], -1);
371 if ConnectTimeout >= 0 then
372 URL.Properties.Values['isc_dpb_connect_timeout'] := IntToStr(ConnectTimeout);
377 procedure TZInterbase6Connection.OnPropertiesChange(Sender: TObject);
379 if StrToBoolEx(Info.Values['hard_commit']) <> FHardCommit then
381 if FTrHandle <> 0 then CloseTransaction;
382 FHardCommit := StrToBoolEx(Info.Values['hard_commit']);
387 Creates a <code>Statement</code> object for sending
388 SQL statements to the database.
389 SQL statements without parameters are normally
390 executed using Statement objects. If the same SQL statement
391 is executed many times, it is more efficient to use a
392 <code>PreparedStatement</code> object.
394 Result sets created using the returned <code>Statement</code>
395 object will by default have forward-only type and read-only concurrency.
397 @param Info a statement parameters.
398 @return a new Statement object
400 function TZInterbase6Connection.CreateRegularStatement(Info: TStrings):
405 Result := TZInterbase6Statement.Create(Self, Info);
409 Gets the host's full version number. Initially this should be 0.
410 The format of the version returned must be XYYYZZZ where
414 @return this server's full version number
416 function TZInterbase6Connection.GetHostVersion: Integer;
418 Result := FHostVersion;
422 Get database connection handle.
423 @return database handle
425 function TZInterbase6Connection.GetDBHandle: PISC_DB_HANDLE;
431 Return Interbase dialect number. Dialect a dialect Interbase SQL
433 @return dialect number
435 function TZInterbase6Connection.GetDialect: Word;
441 Return native interbase plain driver
444 function TZInterbase6Connection.GetPlainDriver: IZInterbasePlainDriver;
446 Result := PlainDriver as IZInterbasePlainDriver;
450 Get Interbase transaction handle
451 @return transaction handle
453 function TZInterbase6Connection.GetTrHandle: PISC_TR_HANDLE;
455 if (FTrHandle = 0) and not Closed then
457 Result := @FTrHandle;
461 Opens a connection to database server with specified parameters.
463 procedure TZInterbase6Connection.Open;
464 const sCS_NONE = 'NONE';
468 DBName: array[0..512] of AnsiChar;
469 TmpClientCodePageOld, TmpClientCodePageNew: PZCodePage;
476 if TransactIsolationLevel = tiReadUncommitted then
477 raise EZSQLException.Create('Isolation level do not capable');
478 if ConSettings^.ClientCodePage = nil then
479 CheckCharEncoding(FClientCodePage, True);
481 DPB := GenerateDPB(Info, FDPBLength, FDialect);
483 if HostName <> '' then
486 {$IFDEF WITH_STRPCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrPCopy(DBName, ZPlainString(HostName + '/' + IntToStr(Port) + ':' + Database))
488 {$IFDEF WITH_STRPCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrPCopy(DBName, ZPlainString(HostName + ':' + Database))
491 {$IFDEF WITH_STRPCOPY_DEPRECATED}AnsiStrings.{$ENDIF}StrPCopy(DBName, ZPlainString(Database));
494 { Create new db if needed }
495 if Info.Values['createNewDatabase'] <> '' then
497 CreateNewDatabase(Info.Values['createNewDatabase']);
498 { Logging connection action }
499 DriverManager.LogMessage(lcConnect, PlainDriver.GetProtocol,
500 Format('CREATE DATABASE "%s" AS USER "%s"', [Info.Values['createNewDatabase'], User]));
501 URL.Properties.Values['createNewDatabase'] := '';
505 { Connect to Interbase6 database. }
506 GetPlainDriver.isc_attach_database(@FStatusVector,
507 {$IFDEF WITH_STRLEN_DEPRECATED}AnsiStrings.{$ENDIF}StrLen(DBName), DBName,
508 @FHandle, FDPBLength, DPB);
510 { Check connection error }
511 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcConnect);
513 (GetMetadata.GetDatabaseInfo as IZInterbaseDatabaseInfo).CollectServerInformations; //keep this one first!
514 tmp := GetMetadata.GetDatabaseInfo.GetDatabaseProductVersion;
516 FHostVersion := StrToInt(Copy(tmp, 1, i-1))*1000000;
517 if Pos(' ', tmp) > 0 then //possible beta or alfa release
518 tmp := Copy(tmp, i+1, Pos(' ', tmp)-i-1)
520 tmp := Copy(tmp, i+1, Length(tmp)-i);
521 FHostVersion := FHostVersion + StrToInt(tmp)*100000;
522 { Logging connection action }
523 DriverManager.LogMessage(lcConnect, PlainDriver.GetProtocol,
524 Format('CONNECT TO "%s" AS USER "%s"', [Database, User]));
526 { Start transaction }
527 if not FHardCommit then
532 {Check for ClientCodePage: if empty switch to database-defaults
533 and/or check for charset 'NONE' which has a different byte-width
534 and no conversations where done except the collumns using collations}
535 with GetMetadata.GetCollationAndCharSet('', '', '', '') do
538 if FCLientCodePage = '' then
540 FCLientCodePage := GetString(6);
541 CheckCharEncoding(FClientCodePage);
544 if GetString(6) = sCS_NONE then
545 if not ( FClientCodePage = sCS_NONE ) then
547 URL.Properties.Values['isc_dpb_lc_ctype'] := sCS_NONE;
548 FClientCodePage := sCS_NONE;
549 {save the user wanted CodePage-Informations}
550 TmpClientCodePageOld := ConSettings.ClientCodePage;
551 { charset 'NONE' can't converty anything and write 'Data as is'!
552 If another charset was set on attaching the Server then all
553 column collations are retrieved with newly choosen collation.
554 BUT NO string convertations where done! So we need a
555 reopen (since we can set the Client-CharacterSet only on
556 connecting) to determine charset 'NONE' corectly. Then the column
557 collations have there proper CharsetID's to encode all strings
561 { Create a new PZCodePage for the new environment-variables }
562 TmpClientCodePageNew := New(PZCodePage);
563 TmpClientCodePageNew.Name := sCS_NONE;
564 TmpClientCodePageNew.ID := CS_NONE;
565 TmpClientCodePageNew.CharWidth := 1;
566 TmpClientCodePageNew.Encoding := TmpClientCodePageOld.Encoding;
567 TmpClientCodePageNew.CP := TmpClientCodePageOld.CP;
568 TmpClientCodePageNew.ZAlias := '';
569 TmpClientCodePageNew.IsStringFieldCPConsistent := False;
570 ConSettings.ClientCodePage := TmpClientCodePageNew;
571 SetConvertFunctions(ConSettings); //now let's the converters again
572 FDisposeClientCodePage := True;
573 {Also reset the MetaData ConSettings}
574 (FMetadata as TZInterbase6DatabaseMetadata).ConSettings := ConSettings;
575 { now we're able to read and write strings for columns without a
576 spezial declared collation for charset 'NONE' with the user
577 choosen CodePage and Encoding }
581 if FClientCodePage = sCS_NONE then
582 ConSettings.AutoEncode := True; //Must be set!
584 {$IFDEF WITH_STRDISPOSE_DEPRECATED}AnsiStrings.{$ENDIF}StrDispose(DPB);
589 Creates a <code>PreparedStatement</code> object for sending
590 parameterized SQL statements to the database.
592 A SQL statement with or without IN parameters can be
593 pre-compiled and stored in a PreparedStatement object. This
594 object can then be used to efficiently execute this statement
597 <P><B>Note:</B> This method is optimized for handling
598 parametric SQL statements that benefit from precompilation. If
599 the driver supports precompilation,
600 the method <code>prepareStatement</code> will send
601 the statement to the database for precompilation. Some drivers
602 may not support precompilation. In this case, the statement may
603 not be sent to the database until the <code>PreparedStatement</code> is
604 executed. This has no direct effect on users; however, it does
605 affect which method throws certain SQLExceptions.
607 Result sets created using the returned PreparedStatement will have
608 forward-only type and read-only concurrency, by default.
610 @param sql a SQL statement that may contain one or more '?' IN
611 parameter placeholders
612 @return a new PreparedStatement object containing the
613 pre-compiled statement
615 function TZInterbase6Connection.CreatePreparedStatement(
616 const SQL: string; Info: TStrings): IZPreparedStatement;
620 Result := TZInterbase6PreparedStatement.Create(Self, SQL, Info);
624 Creates a <code>CallableStatement</code> object for calling
625 database stored procedures.
626 The <code>CallableStatement</code> object provides
627 methods for setting up its IN and OUT parameters, and
628 methods for executing the call to a stored procedure.
630 <P><B>Note:</B> This method is optimized for handling stored
631 procedure call statements. Some drivers may send the call
632 statement to the database when the method <code>prepareCall</code>
634 may wait until the <code>CallableStatement</code> object
635 is executed. This has no
636 direct effect on users; however, it does affect which method
637 throws certain SQLExceptions.
639 Result sets created using the returned CallableStatement will have
640 forward-only type and read-only concurrency, by default.
642 @param sql a SQL statement that may contain one or more '?'
643 parameter placeholders. Typically this statement is a JDBC
644 function call escape string.
645 @param Info a statement parameters.
646 @return a new CallableStatement object containing the
647 pre-compiled SQL statement
649 function TZInterbase6Connection.CreateCallableStatement(const SQL: string;
650 Info: TStrings): IZCallableStatement;
654 Result := TZInterbase6CallableStatement.Create(Self, SQL, Info);
658 Drops all changes made since the previous
659 commit/rollback and releases any database locks currently held
660 by this Connection. This method should be used only when auto-
661 commit has been disabled.
664 procedure TZInterbase6Connection.Rollback;
666 if FTrHandle <> 0 then
670 GetPlainDriver.isc_rollback_transaction(@FStatusVector, @FTrHandle);
674 GetPlainDriver.isc_rollback_retaining(@FStatusVector, @FTrHandle);
675 CheckInterbase6Error(GetPlainDriver, FStatusVector);
676 DriverManager.LogMessage(lcTransaction, PlainDriver.GetProtocol, 'TRANSACTION ROLLBACK');
681 Checks if a connection is still alive by doing a call to isc_database_info
682 It does not matter what info we request, we are not looking at it, as long
683 as it is something which should _always_ work if the connection is there.
684 We check if the error returned is one of the net_* errors described in the
685 firebird client documentation (335544721 .. 335544727).
686 Returns 0 if the connection is OK
687 Returns non zero if the connection is not OK
689 function TZInterbase6Connection.PingServer: integer;
691 DatabaseInfoCommand: Char;
692 Buffer: array[0..IBBigLocalBufferLength - 1] of AnsiChar;
693 ErrorCode: ISC_STATUS;
695 DatabaseInfoCommand := Char(isc_info_reads);
697 ErrorCode := GetPlainDriver.isc_database_info(@FStatusVector, @FHandle, 1, @DatabaseInfoCommand,
698 IBLocalBufferLength, Buffer);
700 if (ErrorCode >= 335544721) and (ErrorCode <= 335544727) then
707 Start Interbase transaction
709 procedure TZInterbase6Connection.StartTransaction;
710 const tpb_Access: array[boolean] of String = ('isc_tpb_write','isc_tpb_read');
712 {EH: We do NOT handle the isc_tpb_autocommit of FB because we noticed a huge
713 performance drop especially for Batch executions. Note Zeos handles one Batch
714 Execution as one Update and loops until all batch array are send. FB with this
715 param commits after each "execute block" which definitally kills the idea and
716 the expected performance!}
717 //const tpb_AutoCommit: array[boolean] of String = ('','isc_tpb_autocommit');
724 if FTrHandle <> 0 then
725 begin {CLOSE Last Transaction first!}
726 GetPlainDriver.isc_commit_transaction(@FStatusVector, @FTrHandle);
727 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcTransaction);
731 Params := TStringList.Create;
733 { Set transaction parameters by TransactIsolationLevel }
734 Params.Add('isc_tpb_version3');
735 case TransactIsolationLevel of
738 Params.Add(tpb_Access[ReadOnly]);
739 Params.Add('isc_tpb_read_committed');
740 Params.Add('isc_tpb_rec_version');
741 Params.Add('isc_tpb_nowait');
745 Params.Add(tpb_Access[ReadOnly]);
746 Params.Add('isc_tpb_concurrency');
747 Params.Add('isc_tpb_nowait');
751 Params.Add(tpb_Access[ReadOnly]);
752 Params.Add('isc_tpb_consistency');
755 { Add user defined parameters for transaction }
756 if Pos('isc_tpb_', Info.Text) > 0 then
759 Params.AddStrings(Info);
763 {extend the firebird defaults by ReadOnly}
764 Params.Add(tpb_Access[ReadOnly]);
765 Params.Add('isc_tpb_concurrency');
766 Params.Add('isc_tpb_wait');
771 { GenerateTPB return PTEB with null pointer tpb_address from default
773 PTEB := GenerateTPB(Params, FHandle);
774 GetPlainDriver.isc_start_multiple(@FStatusVector, @FTrHandle, 1, PTEB);
775 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcTransaction);
776 DriverManager.LogMessage(lcTransaction, GetPlainDriver.GetProtocol,
777 'TRANSACTION STARTED.');
780 {$IFDEF WITH_STRDISPOSE_DEPRECATED}AnsiStrings.{$ENDIF}StrDispose(PTEB.tpb_address);
786 procedure TZInterbase6Connection.SetTransactionIsolation(Level: TZTransactIsolationLevel);
788 if (Level <> TransactIsolationLevel) and (FHandle <> 0) then
790 Inherited SetTransactionIsolation(Level);
795 @param SQL a sql strinf for creation database
797 procedure TZInterbase6Connection.CreateNewDatabase(const SQL: String);
799 TrHandle: TISC_TR_HANDLE;
802 GetPlainDriver.isc_dsql_execute_immediate(@FStatusVector, @FHandle, @TrHandle,
803 0, PAnsiChar({$IFDEF UNICODE}AnsiString{$ENDIF}(sql)), FDialect, nil);
804 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcExecute, SQL);
805 //disconnect from the newly created database because the connection character set is NONE,
806 //which usually nobody wants
807 GetPlainDriver.isc_detach_database(@FStatusVector, @FHandle);
808 CheckInterbase6Error(GetPlainDriver, FStatusVector, lcExecute, SQL);
811 function TZInterbase6Connection.GetBinaryEscapeString(const Value: RawByteString): String;
813 //http://tracker.firebirdsql.org/browse/CORE-2789
814 if EndsWith(GetPlainDriver.GetProtocol, '2.5') then
815 if (Length(Value)*2+3) < 32*1024 then
816 Result := GetSQLHexString(PAnsiChar(Value), Length(Value))
818 raise Exception.Create('Binary data out of range! Use parameters!')
820 raise Exception.Create('Your Firebird-Version does''t support Binary-Data in SQL-Statements! Use parameters!');
823 function TZInterbase6Connection.GetBinaryEscapeString(const Value: TByteDynArray): String;
825 //http://tracker.firebirdsql.org/browse/CORE-2789
826 if EndsWith(GetPlainDriver.GetProtocol, '2.5') then
827 if (Length(Value)*2+3) < 32*1024 then
828 Result := GetSQLHexString(PAnsiChar(Value), Length(Value))
830 raise Exception.Create('Binary data out of range! Use parameters!')
832 raise Exception.Create('Your Firebird-Version does''t support Binary-Data in SQL-Statements! Use parameters!');
835 function TZInterbase6Connection.GetEscapeString(const Value: RawByteString): RawByteString;
837 //http://www.firebirdsql.org/manual/qsg10-firebird-sql.html
838 if GetAutoEncodeStrings then
839 if StartsWith(Value, RawByteString('''')) and EndsWith(Value, RawByteString('''')) then
843 Result := GetDriver.GetTokenizer.GetEscapeString(Value)
847 Result := #39+{$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}StringReplace(Value, #39, #39#39, [rfReplaceAll])+#39
849 Result := GetDriver.GetTokenizer.GetEscapeString(#39+StringReplace(Value, #39, #39#39, [rfReplaceAll])+#39)
852 if StartsWith(Value, RawByteString('''')) and EndsWith(Value, RawByteString('''')) then
855 Result := #39+{$IFDEF WITH_UNITANSISTRINGS}AnsiStrings.{$ENDIF}StringReplace(Value, #39, #39#39, [rfReplaceAll])+#39;
858 function TZInterbase6Connection.GetEscapeString(const Value: ZWideString): ZWideString;
860 //http://www.firebirdsql.org/manual/qsg10-firebird-sql.html
861 if GetAutoEncodeStrings then
862 if StartsWith(Value, ZWideString('''')) and EndsWith(Value, ZWideString('''')) then
864 Result := GetDriver.GetTokenizer.GetEscapeString(Value)
870 Result := GetDriver.GetTokenizer.GetEscapeString(#39+StringReplace(Value, #39, #39#39, [rfReplaceAll])+#39)
872 Result := ZDbcUnicodeString(GetDriver.GetTokenizer.GetEscapeString(#39+StringReplace(ZPlainString(Value), #39, #39#39, [rfReplaceAll])+#39))
875 if StartsWith(Value, ZWideString('''')) and EndsWith(Value, ZWideString('''')) then
879 Result := #39+StringReplace(Value, #39, #39#39, [rfReplaceAll])+#39;
881 Result := ZDbcUnicodeString(#39+StringReplace(ZPlainString(Value), #39, #39#39, [rfReplaceAll])+#39);
885 Creates a sequence generator object.
886 @param Sequence a name of the sequence generator.
887 @param BlockSize a number of unique keys requested in one trip to SQL server.
888 @returns a created sequence object.
890 function TZInterbase6Connection.CreateSequence(const Sequence: string;
891 BlockSize: Integer): IZSequence;
893 Result := TZInterbase6Sequence.Create(Self, Sequence, BlockSize);
896 procedure TZInterbase6Connection.SetReadOnly(Value: Boolean);
898 if (ReadOnly <> Value) and (FTrHandle <> 0) then
903 { TZInterbase6CachedResolver }
906 Forms a where clause for SELECT statements to calculate default values.
907 @param Columns a collection of key columns.
908 @param OldRowAccessor an accessor object to old column values.
910 function TZInterbase6CachedResolver.FormCalculateStatement(
911 Columns: TObjectList): string;
912 // --> ms, 30/10/2005
916 Result := inherited FormCalculateStatement(Columns);
919 iPos := pos('FROM', uppercase(Result));
922 Result := copy(Result, 1, iPos+3) + ' RDB$DATABASE';
926 Result := Result + ' FROM RDB$DATABASE';
932 { TZInterbase6Sequence }
935 Gets the current unique key generated by this sequence.
936 @param the next generated unique key.
938 function TZInterbase6Sequence.GetCurrentValue: Int64;
940 Statement: IZStatement;
941 ResultSet: IZResultSet;
943 Statement := Connection.CreateStatement;
944 ResultSet := Statement.ExecuteQuery(Format(
945 'SELECT GEN_ID("%s", 0) FROM rdb$generators ' +
946 'WHERE rdb$generators.rdb$generator_name = ''%s''', [Name, Name]));
947 if ResultSet.Next then
948 Result := ResultSet.GetLong(1)
950 Result := inherited GetCurrentValue;
956 Gets the next unique key generated by this sequence.
957 @param the next generated unique key.
959 function TZInterbase6Sequence.GetCurrentValueSQL: string;
961 Result := Format(' GEN_ID("%s", 0) ', [Name]);
964 function TZInterbase6Sequence.GetNextValue: Int64;
966 Statement: IZStatement;
967 ResultSet: IZResultSet;
969 Statement := Connection.CreateStatement;
970 ResultSet := Statement.ExecuteQuery(Format(
971 'SELECT GEN_ID("%s", %d) FROM rdb$generators ' +
972 'WHERE rdb$generators.rdb$generator_name = ''%s''', [Name, BlockSize, Name]));
973 if ResultSet.Next then
974 Result := ResultSet.GetLong(1)
976 Result := inherited GetNextValue;
981 function TZInterbase6Sequence.GetNextValueSQL: string;
983 Result := Format(' GEN_ID("%s", %d) ', [Name, BlockSize]);
987 Interbase6Driver := TZInterbase6Driver.Create;
988 DriverManager.RegisterDriver(Interbase6Driver);
991 if Assigned(DriverManager) then
992 DriverManager.DeregisterDriver(Interbase6Driver);
993 Interbase6Driver := nil;