16 |
|
* |
17 |
|
* The Initial Developer of the Original Code is Tony Whyman. |
18 |
|
* |
19 |
< |
* The Original Code is (C) 2016 Tony Whyman, MWA Software |
19 |
> |
* The Original Code is (C) 2016-2021 Tony Whyman, MWA Software |
20 |
|
* (http://www.mwasoftware.co.uk). |
21 |
|
* |
22 |
|
* All Rights Reserved. |
69 |
|
Syntax: |
70 |
|
|
71 |
|
Transaction Start: |
72 |
< |
*S:<date/time>,<session id>,<transaction no.>,<string length>:<transaction Name>,<string length>:<TPB>,<default Completion> |
72 |
> |
*S:<date/time>,<attachmentid>,<session id>,<transaction no.>,<string length>:<transaction Name>,<string length>:<TPB>,<default Completion> |
73 |
|
|
74 |
|
Transaction Commit: |
75 |
< |
*C:<date/time>,<session id>,<transaction no.> |
75 |
> |
*C:<date/time>,<attachmentid>,<session id>,<transaction no.> |
76 |
|
|
77 |
|
Transaction Commit retaining : |
78 |
< |
*c:<date/time>,<session id>,<transaction no.><old transaction no.> |
78 |
> |
*c:<date/time>,<attachmentid>,<session id>,<transaction no.><old transaction no.> |
79 |
|
|
80 |
|
Transaction Rollback: |
81 |
< |
*R:<date/time>,<session id>,<transaction no.> |
81 |
> |
*R:<date/time>,<attachmentid>,<session id>,<transaction no.> |
82 |
|
|
83 |
|
Transaction Rollback retaining: |
84 |
< |
*r:<date/time>,<session id>,<transaction no.><old transaction no.> |
84 |
> |
*r:<date/time>,<attachmentid>,<session id>,<transaction no.><old transaction no.> |
85 |
|
|
86 |
|
Update/Insert/Delete |
87 |
< |
*Q:<date/time>,<session id>,<transaction no.>,<length of query text in bytes>:<query text> |
87 |
> |
*Q:<date/time>,<attachmentid>,<session id>,<transaction no.>,<length of query text in bytes>:<query text> |
88 |
|
|
89 |
|
} |
90 |
|
|
93 |
|
TFBJournaling = class(TActivityHandler, IJournallingHook) |
94 |
|
private |
95 |
|
{Logfile} |
96 |
< |
const sQueryJournal = '*Q:''%s'',%d,%d,%d:%s' + LineEnding; |
97 |
< |
const sTransStartJnl = '*S:''%s'',%d,%d,%d:%s,%d:%s,%d' + LineEnding; |
98 |
< |
const sTransCommitJnl = '*C:''%s'',%d,%d' + LineEnding; |
99 |
< |
const sTransCommitRetJnl = '*c:''%s'',%d,%d,%d' + LineEnding; |
100 |
< |
const sTransRollBackJnl = '*R:''%s'',%d,%d' + LineEnding; |
101 |
< |
const sTransRollBackRetJnl = '*r:''%s'',%d,%d,%d' + LineEnding; |
96 |
> |
const sQueryJournal = '*Q:''%s'',%d,%d,%d,%d:%s' + LineEnding; |
97 |
> |
const sTransStartJnl = '*S:''%s'',%d,%d,%d,%d:%s,%d:%s,%d' + LineEnding; |
98 |
> |
const sTransCommitJnl = '*C:''%s'',%d,%d,%d' + LineEnding; |
99 |
> |
const sTransCommitRetJnl = '*c:''%s'',%d,%d,%d,%d' + LineEnding; |
100 |
> |
const sTransRollBackJnl = '*R:''%s'',%d,%d,%d' + LineEnding; |
101 |
> |
const sTransRollBackRetJnl = '*r:''%s'',%d,%d,%d,%d' + LineEnding; |
102 |
|
private |
103 |
|
FOptions: TJournalOptions; |
104 |
|
FJournalFilePath: string; |
125 |
|
function GetJournalOptions: TJournalOptions; |
126 |
|
function StartJournaling(aJournalLogFile: AnsiString): integer; overload; |
127 |
|
function StartJournaling(aJournalLogFile: AnsiString; Options: TJournalOptions): integer; overload; |
128 |
+ |
function StartJournaling(S: TStream; Options: TJournalOptions): integer; overload; |
129 |
|
procedure StopJournaling(RetainJournal: boolean); |
130 |
|
end; |
131 |
|
|
140 |
|
FUserCharSetMap: array of TCharSetMap; |
141 |
|
FSecDatabase: AnsiString; |
142 |
|
FInlineBlobLimit: integer; |
143 |
+ |
FAttachmentID: integer; |
144 |
|
protected |
145 |
|
FDatabaseName: AnsiString; |
146 |
|
FRaiseExceptionOnConnectError: boolean; |
219 |
|
function GetEventHandler(Event: AnsiString): IEvents; overload; |
220 |
|
|
221 |
|
function GetSQLDialect: integer; |
222 |
+ |
function GetAttachmentID: integer; |
223 |
|
function CreateBlob(transaction: ITransaction; RelationName, ColumnName: AnsiString; BPB: IBPB=nil): IBlob; overload; |
224 |
|
function CreateBlob(transaction: ITransaction; BlobMetaData: IBlobMetaData; BPB: IBPB=nil): IBlob; overload; virtual; abstract; |
225 |
|
function OpenBlob(transaction: ITransaction; BlobMetaData: IBlobMetaData; BlobID: TISC_QUAD; BPB: IBPB=nil): IBlob; overload; virtual; abstract; |
236 |
|
function GetDBInformation(Requests: array of byte): IDBInformation; overload; |
237 |
|
function GetDBInformation(Request: byte): IDBInformation; overload; |
238 |
|
function GetDBInformation(Requests: IDIRB): IDBInformation; overload; |
236 |
– |
function GetAttachmentID: integer; |
239 |
|
function GetConnectString: AnsiString; |
240 |
|
function GetRemoteProtocol: AnsiString; |
241 |
|
function GetAuthenticationMethod: AnsiString; |
242 |
|
function GetSecurityDatabase: AnsiString; |
243 |
|
function GetODSMajorVersion: integer; |
244 |
|
function GetODSMinorVersion: integer; |
245 |
+ |
function GetCharSetID: integer; |
246 |
|
function HasDecFloatSupport: boolean; virtual; |
247 |
|
function GetInlineBlobLimit: integer; |
248 |
|
procedure SetInlineBlobLimit(limit: integer); |
249 |
|
function HasBatchMode: boolean; virtual; |
250 |
|
function HasTable(aTableName: AnsiString): boolean; |
251 |
+ |
function HasFunction(aFunctionName: AnsiString): boolean; |
252 |
+ |
function HasProcedure(aProcName: AnsiString): boolean; |
253 |
|
|
254 |
|
public |
255 |
|
{Character Sets} |
615 |
|
|
616 |
|
procedure TFBJournaling.EndSession(RetainJournal: boolean); |
617 |
|
begin |
618 |
< |
if JournalingActive then |
618 |
> |
if JournalingActive and (FJournalFilePath <> '') then |
619 |
|
begin |
620 |
|
FreeAndNil(FJournalFileStream); |
621 |
< |
if not RetainJournal then |
621 |
> |
if not (joNoServerTable in FOptions) and not RetainJournal then |
622 |
|
try |
623 |
|
GetAttachment.ExecuteSQL([isc_tpb_write,isc_tpb_wait,isc_tpb_consistency], |
624 |
|
sqlCleanUpSession,[FSessionID]); |
643 |
|
TPBText: AnsiString; |
644 |
|
begin |
645 |
|
FDoNotJournal := true; |
646 |
+ |
if not (joNoServerTable in FOptions) then |
647 |
|
try |
648 |
|
GetAttachment.ExecuteSQL(Tr,sqlRecordJournalEntry,[FSessionID,Tr.GetTransactionID,NULL]); |
649 |
|
finally |
651 |
|
end; |
652 |
|
TPBText := Tr.getTPB.AsText; |
653 |
|
LogEntry := Format(sTransStartJnl,[FBFormatDateTime(GetDateTimeFmt,Now), |
654 |
+ |
GetAttachment.GetAttachmentID, |
655 |
|
FSessionID, |
656 |
|
Tr.GetTransactionID, |
657 |
|
Length(Tr.TransactionName), |
671 |
|
case Action of |
672 |
|
TARollback: |
673 |
|
begin |
674 |
< |
LogEntry := Format(sTransRollbackJnl,[FBFormatDateTime(GetDateTimeFmt,Now),FSessionID,TransactionID]); |
674 |
> |
LogEntry := Format(sTransRollbackJnl,[FBFormatDateTime(GetDateTimeFmt,Now), |
675 |
> |
GetAttachment.GetAttachmentID, |
676 |
> |
FSessionID,TransactionID]); |
677 |
|
Result := true; |
678 |
|
end; |
679 |
|
TACommit: |
680 |
|
begin |
681 |
< |
LogEntry := Format(sTransCommitJnl,[FBFormatDateTime(GetDateTimeFmt,Now),FSessionID,TransactionID]); |
681 |
> |
LogEntry := Format(sTransCommitJnl,[FBFormatDateTime(GetDateTimeFmt,Now), |
682 |
> |
GetAttachment.GetAttachmentID, |
683 |
> |
FSessionID,TransactionID]); |
684 |
|
Result := true; |
685 |
|
end; |
686 |
|
end; |
695 |
|
case Action of |
696 |
|
TACommitRetaining: |
697 |
|
LogEntry := Format(sTransCommitRetJnl,[FBFormatDateTime(GetDateTimeFmt,Now), |
698 |
+ |
GetAttachment.GetAttachmentID, |
699 |
|
FSessionID,Tr.GetTransactionID,OldTransactionID]); |
700 |
|
TARollbackRetaining: |
701 |
|
LogEntry := Format(sTransRollbackRetJnl,[FBFormatDateTime(GetDateTimeFmt,Now), |
702 |
+ |
GetAttachment.GetAttachmentID, |
703 |
|
FSessionID,Tr.GetTransactionID,OldTransactionID]); |
704 |
|
end; |
705 |
|
if assigned(FJournalFileStream) then |
706 |
|
FJournalFileStream.Write(LogEntry[1],Length(LogEntry)); |
707 |
|
|
708 |
|
FDoNotJournal := true; |
709 |
+ |
if not (joNoServerTable in FOptions) then |
710 |
|
try |
711 |
|
GetAttachment.ExecuteSQL(Tr,sqlRecordJournalEntry,[FSessionID,Tr.GetTransactionID,OldTransactionID]); |
712 |
|
finally |
720 |
|
begin |
721 |
|
SQL := TQueryProcessor.Execute(Stmt); |
722 |
|
LogEntry := Format(sQueryJournal,[FBFormatDateTime(GetDateTimeFmt,Now), |
723 |
+ |
GetAttachment.GetAttachmentID, |
724 |
|
FSessionID, |
725 |
|
Stmt.GetTransaction.GetTransactionID, |
726 |
|
Length(SQL),SQL]); |
746 |
|
function TFBJournaling.StartJournaling(aJournalLogFile: AnsiString; |
747 |
|
Options: TJournalOptions): integer; |
748 |
|
begin |
749 |
+ |
try |
750 |
+ |
StartJournaling(TFileStream.Create(aJournalLogFile,fmCreate),Options); |
751 |
+ |
finally |
752 |
+ |
FJournalFilePath := aJournalLogFile; |
753 |
+ |
end; |
754 |
+ |
end; |
755 |
+ |
|
756 |
+ |
function TFBJournaling.StartJournaling(S: TStream; Options: TJournalOptions |
757 |
+ |
): integer; |
758 |
+ |
begin |
759 |
|
FOptions := Options; |
760 |
+ |
if not (joNoServerTable in FOptions) then |
761 |
|
with GetAttachment do |
762 |
|
begin |
763 |
< |
if not HasTable(sJournalTableName) then |
763 |
> |
if not HasTable(sJournalTableName) then |
764 |
|
begin |
765 |
|
ExecImmediate([isc_tpb_write,isc_tpb_wait,isc_tpb_consistency],sqlCreateJournalTable); |
766 |
|
ExecImmediate([isc_tpb_write,isc_tpb_wait,isc_tpb_consistency],sqlCreateSequence); |
767 |
|
end; |
768 |
|
FSessionID := OpenCursorAtStart(sqlGetNextSessionID)[0].AsInteger; |
769 |
|
end; |
770 |
< |
FJournalFilePath := aJournalLogFile; |
745 |
< |
FJournalFileStream := TFileStream.Create(FJournalFilePath,fmCreate); |
770 |
> |
FJournalFileStream := S; |
771 |
|
Result := FSessionID; |
772 |
|
end; |
773 |
|
|
790 |
|
begin |
791 |
|
if not IsConnected then Exit; |
792 |
|
DBInfo := GetDBInformation([isc_info_db_id,isc_info_ods_version,isc_info_ods_minor_version, |
793 |
< |
isc_info_db_SQL_Dialect]); |
793 |
> |
isc_info_db_SQL_Dialect, isc_info_attachment_id]); |
794 |
|
for i := 0 to DBInfo.GetCount - 1 do |
795 |
|
with DBInfo[i] do |
796 |
|
case getItemType of |
800 |
|
FODSMajorVersion := getAsInteger; |
801 |
|
isc_info_db_SQL_Dialect: |
802 |
|
FSQLDialect := getAsInteger; |
803 |
+ |
isc_info_attachment_id: |
804 |
+ |
FAttachmentID := getAsInteger; |
805 |
|
end; |
806 |
|
|
807 |
|
FCharSetID := 0; |
858 |
|
FFirebirdAPI := api.GetAPI; {Keep reference to interface} |
859 |
|
FSQLDialect := 3; |
860 |
|
FDatabaseName := DatabaseName; |
834 |
– |
FDPB := DPB; |
861 |
|
SetLength(FUserCharSetMap,0); |
836 |
– |
FRaiseExceptionOnConnectError := RaiseExceptionOnConnectError; |
862 |
|
FODSMajorVersion := 0; |
863 |
|
FODSMinorVersion := 0; |
864 |
|
FInlineBlobLimit := DefaultMaxInlineBlobLimit; |
865 |
+ |
FDPB := DPB; |
866 |
+ |
FRaiseExceptionOnConnectError := RaiseExceptionOnConnectError; |
867 |
|
end; |
868 |
|
|
869 |
|
function TFBAttachment.GenerateCreateDatabaseSQL(DatabaseName: AnsiString; aDPB: IDPB): AnsiString; |
1208 |
|
Result := FSQLDialect; |
1209 |
|
end; |
1210 |
|
|
1211 |
+ |
function TFBAttachment.GetAttachmentID: integer; |
1212 |
+ |
begin |
1213 |
+ |
Result := FAttachmentID; |
1214 |
+ |
end; |
1215 |
+ |
|
1216 |
|
function TFBAttachment.CreateBlob(transaction: ITransaction; RelationName, |
1217 |
|
ColumnName: AnsiString; BPB: IBPB): IBlob; |
1218 |
|
begin |
1282 |
|
Result := GetDBInfo(getBuffer,getDataLength); |
1283 |
|
end; |
1284 |
|
|
1253 |
– |
function TFBAttachment.GetAttachmentID: integer; |
1254 |
– |
var Info: IDBInformation; |
1255 |
– |
begin |
1256 |
– |
Info := GetDBInformation(isc_info_attachment_id); |
1257 |
– |
if (Info.Count > 0) and (Info[0].getItemType = isc_info_attachment_id) then |
1258 |
– |
Result := Info[0].getAsInteger |
1259 |
– |
else |
1260 |
– |
Result := -1; |
1261 |
– |
end; |
1262 |
– |
|
1285 |
|
function TFBAttachment.GetConnectString: AnsiString; |
1286 |
|
begin |
1287 |
|
Result := FDatabaseName; |
1312 |
|
Result := FODSMinorVersion; |
1313 |
|
end; |
1314 |
|
|
1315 |
+ |
function TFBAttachment.GetCharSetID: integer; |
1316 |
+ |
begin |
1317 |
+ |
Result := FCharSetID; |
1318 |
+ |
end; |
1319 |
+ |
|
1320 |
|
function TFBAttachment.HasDecFloatSupport: boolean; |
1321 |
|
begin |
1322 |
|
Result := false; |
1347 |
|
[aTableName])[0].AsInteger > 0; |
1348 |
|
end; |
1349 |
|
|
1350 |
+ |
function TFBAttachment.HasFunction(aFunctionName: AnsiString): boolean; |
1351 |
+ |
begin |
1352 |
+ |
Result := OpenCursorAtStart( |
1353 |
+ |
'Select count(*) From RDB$FUNCTIONS Where RDB$FUNCTION_NAME = ?', |
1354 |
+ |
[aFunctionName])[0].AsInteger > 0; |
1355 |
+ |
end; |
1356 |
+ |
|
1357 |
+ |
function TFBAttachment.HasProcedure(aProcName: AnsiString): boolean; |
1358 |
+ |
begin |
1359 |
+ |
Result := OpenCursorAtStart( |
1360 |
+ |
'Select count(*) From RDB$PROCEDURES Where RDB$PROCEDURE_NAME = ?', |
1361 |
+ |
[aProcName])[0].AsInteger > 0; |
1362 |
+ |
end; |
1363 |
+ |
|
1364 |
|
function TFBAttachment.HasDefaultCharSet: boolean; |
1365 |
|
begin |
1366 |
|
Result := FHasDefaultCharSet |