I am trying to build a scheduler application for my semestre exam, and I am interogating the database if a teacher has classes in a certain week, certain day and a certain hour. Is there a way to treat Sql Error Codes in Delphi Xe2 using DBExpress? I have a query performing my interogation(I let the users to select the parameters) on SQL Server2008, and here is the issue; If an user lets one parameter field empty, my application shows the message in the image; well this is not a user friendly way of showing error messages, so I tried to handle the exception without any succes: this is my code that i wrote to query SQL Server:
with DataModule1.QueryV1 do
Try
Close;
SQL.Clear; // clear command sql if there is
SQL.Add ('SELECT * FROM OrarC WHERE IDZi =' +
QuotedStr(LlbZiua.KeyValue)+ 'and IDInterval=' + QuotedStr(LlbIntOrar.KeyValue) +
'and IDSala='+ QuotedStr(EdtIDSala.Text)+ EdtSaptamana.Text);
Open;
except
on E:TDBXError do begin
showmessage('Error!');
end;
End;
on E:TDBXError I have also tried E:EDataBaseError and the result is the same; It would be good to handle the error by the SqlErrorCode returned but I don`t know how to get that.
Run your program under the debugger and observe the actual class of the exception that's raised, or you can get the exception class at runtime with something like:
..
except on E: Exception do
ShowMessage(E.ClassName);
end;
If it is indeed a TDBXError then you could use for example
uses
dbxcommon;
except
on E: TDBxError do begin
if (E.ErrorCode = TDBXErrorCodes.InvalidArgument) then
..
But as far as I can see 245 is not one of TDBXErrorCodes, so possibly you've got an other exception. If it's an EDatabaseError there's nothing much to do, since the class does not have an error code field.
Not quite relevant but also see Data.DBXCommon.TDBXConnection.OnErrorEvent to be able to manage dbexpress exceptions at a central location.
Related
I am using Delphi 2009 Unicode and Firebird 3.x UTF8/dialect 3 database with IBX components. And now I see that all the exceptions raised from the Firebird SQL procedure and trigger code (e.g. using exception my_exception; statement) are handled by IBX as special Firebird exceptions:
Attempt to execute an unprepared dynamic SQL statement
Error Code: 335544711 SQL Code: -901
IBX does not report the name/code/content of the original Firebird exception. It is quite strange, because Delphi 2009 IBX can handle Firebird 2.1 UTF8/Unicode exceptions without problems. It seems to me that IBX is trying to do some extra steps that are not allowed.
Of course, I know that all the advices to move to other frameworks from the IBX, but we are not living in the ideal world, so, the question is as it is.
Question extended:
After initialization code in the project file (this is IB routine http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/IB_SetIBDataBaseErrorMessages.html):
SetIBDataBaseErrorMessages([ShowSQLCode,ShowIBMessage,ShowSQLMessage]);
I am getting normal error messages from the exceptions that are raised from the triggers, but I am still getting generic 'Attempt to execute...' error message in the case when exception is raised from the SQL procedure.
Question updated:
The generic exception about attempt appears when the procedure is called from the IBX TIBStoredProc, but if stored procedure is called (via select from...) from the TIBDataSet, then the right error message appears. So - there should be problem how TIBStoredProc handles the error messages.
Debug shows, that Delphi IBX code IBSQL.pas contains code:
SQLExecProcedure:
begin
fetch_res := Call(FGDSLibrary.isc_dsql_execute2(StatusVector, TRHandle,
#FHandle, Database.SQLDialect, FSQLParams.AsXSQLDA,
FSQLRecord.AsXSQLDA), False);
if (fetch_res <> 0) then
begin
if (fetch_res <> isc_lock_conflict) then
begin
{ Sometimes a prepared stored procedure appears to get
off sync on the server ....This code is meant to try
to work around the problem simply by "retrying". This
need to be reproduced and fixed.
}
FGDSLibrary.isc_dsql_prepare(StatusVector, TRHandle, #FHandle, 0,
PByte(FProcessedSQL.Text), Database.SQLDialect, nil);
Call(FGDSLibrary.isc_dsql_execute2(StatusVector, TRHandle,
#FHandle, Database.SQLDialect, FSQLParams.AsXSQLDA,
FSQLRecord.AsXSQLDA), True);
end
else
IBDataBaseError; // go ahead and raise the lock conflict
end;
end
and the error message about unprepared statement is raised exactly upon the second execution isc_dsql_execute2(...), so - maybe such second try is not necessary and we can raise Exception IBDataBaseError whenever the fetch_res is not 0? Maybe someone knows why Jeff introduced such second call and what bugs this second call tried to solve?
It appears, that Delphi XE 10.2 code makes the second call only in the specific case:
if (fetch_res = isc_bad_stmt_handle) then
And that makes the erroneous second call rare enough to solve the problem in my question. So, the solution is to replace the initial general condition (fetch_res <> isc_lock_conflict) with the more specific condition (fetch_res = isc_bad_stmt_handle).
I am using Delphi (2009, never mind) with IBX and I am trying to execute simple code:
TestSQL.ExecQuery;
Before this code I have checked (and it can be seen in debugger watches as well) that TestSQL.Transaction.InTransaction is True. Nevertheless the exception is raised:
EIBInterBaseError with message 'invalid transaction handle (expecting explicit transaction start)'
So, there is no another solution than to execute the code:
TestSQL.Transaction.StartTransaction;
TestSQL.ExecQuery;
Now the other exception is raised:
EIBClientError with message 'Transaction is active'
Complete dead end? Delphi has code:
procedure TIBTransaction.CheckInTransaction;
begin
if FStreamedActive and (not InTransaction) then
Loaded;
if (FHandle = nil) then
IBError(ibxeNotInTransaction, [nil]);
end;
and it means that the transaction requirement is determined not only by InTransaction but by private variable FStreamedActive as well. So - the transaction control is way more complex? How can I impact FStreamedActive? What is the solution? My test code is part to lengthier code but I wonder how I can break down the inner status of transaction state?
I found the solution - TestSQL.Database was unintentionally different from TestSQL.Transaction.DefaultDatabase. And that manifested in such a strange error message. It is quite strange that IBX allow at all those databases to be different.
I'm facing difficulty to find a correct way to get errors on ApplyUpdates method, using FireDAC in memory (CachedUpdates).
Here is my scenario, a master-detail relationship, compounded by:
1 TFDConnection
2 TFDQuery
2 TDataSource
1 TFDSchemaAdapter
Both queries are setted as CachedUpdates and are linked to FDSchemaAdapter.
The FDQuery2 (detail) is linked with the master by MasterSource property. MasterFields and IndexFieldNames are setted as "idMaster". The property FetchOptions.DetailCascade is also checked.
I also have a button to perform the apply:
try
FDConnection1.StartTransaction;
FDSchemaAdapter1.ApplyUpdates(0);
FDQuery1.CommitUpdates;
FDQuery2.CommitUpdates;
FDConnection1.Commit;
except on E: Exception do
begin
FDConnection1.Rollback;
raise Exception.CreateFmt('Something went wrong. Error: %s', [E.Message]);
end;
end;
Everything is working fine so far.
The problem occurs when my database throw an exception, such a constraint violation. The exception is not raising. Consequently, my transaction is not 'rollbacked'.
ps: I'm using Delphi XE7 and Firebird 2.5
As the documentation states:
ApplyUpdates returns the number of errors it encountered. Based on
this return value and the setting of AMaxErrors successfully, applied
updates are removed from the centralized change log. If the update
process is aborted before all updates are applied, any unapplied
updates remain in the change log.
ApplyUpdates does not raise an
exception. Instead, the application should review erroneous records
using Reconcile and the OnReconcileRow event handler or the
FilterChanges and RowError properties for each dataset. For more
details, read "Reviewing errors" at Caching Updates.
So... you should not be expecting an exception but you should be checking the value returned by ApplyUpdates to decide if you can commit or handle errors.
I have an VCL app containing an object TDownloadUrl (VCL.ExtActns) used to download applications, my question is how to handle any kind of exception that restrict to download [for example:- like download failed or invalid URL link or internet failure or url not reachable or internet not available] using TDownLoadURL?.
Thanks in advance
TDownloadURL only defines 2 error messages, which are both declared in the Vcl.Consts unit:
SUrlMonDllMissing, which is raised when the Win32 URLDownloadToFile() function cannot be accessed at runtime.
SErrorDownloadingURL, which is raised when URLDownloadToFile() fails for any reason. Unfortunately, there is no way to differentiate why URLDownloadToFile() fails (although the OnProgress event may provide information about what it was doing just before the failure occurred).
The error messages are resource strings and thus can be localized, so they could potentially be in any language, not just English. And they are raised using the general SysUtils.Exception class itself, not any derived types. However, you can use them for substring matching, at least:
uses
..., Vcl.ExtActns, Vcl.Consts, System.StrUtils;
try
DownloadURL1.Filename := ...;
DownloadURL1.URL := ...;
DownloadURL1.Execute;
except
on E: Exception do
begin
if StartsText(SUrlMonDllMissing, E.Message) then
...
else if StartsText(SErrorDownloadingURL, E.Message) then
...
else
...
end;
end;
If you need more detailed error information, you might try calling URLDownloadToFile() directly, as it returns an HRESULT value. However,
be careful by the following gotcha in the documentation:
URLDownloadToFile returns S_OK even if the file cannot be created and the download is canceled. If the szFileName parameter contains a file path, ensure that the destination directory exists before calling URLDownloadToFile. For best control over the download and its progress, an IBindStatusCallback interface is recommended.
If that does not solve your issue, then you should use a different HTTP client API/library to perform the download, such as the HTTP client in Indy, ICS, Synapse, WinInet/WinHTTP, libCURL, etc.
I've not used this component, but it likely generates different exception types based on the errors it encounters. If that's the case then the article here covers handling multiple exception types:
Delphi Exception handling problem with multiple Exception handling blocks
In a delphi Application, When I move mouse to a component that has Hint I see this error :
" Access violation at address 00484F3B in module 'Plibrary.exe'. Read of address 0000026C"
Why this happen?
Call Stack:
:758e9617 KERNELBASE.RaiseException + 0x54
:458bf456 System.#UStrCmp
:00407558 #UStrCmp + $2C
Forms.TApplication.SetHint(???)
Forms.TApplication.Idle(???)
Forms.TApplication.HandleMessage
Forms.TApplication.Run Plibrary.Plibrary
:75ca1194 kernel32.BaseThreadInitThunk + 0x12
:7752b3f5 ntdll.RtlInitializeExceptionChain + 0x63
:7752b3c8 ntdll.RtlInitializeExceptionChain + 0x36"
And failing code is in System.pas file at line 17732 : "MOV ESI,[ESP]"
Edit (from comment):
In the remain.pas file at this procedure :
procedure TMainForm.ShowHint(Sender: TObject);
begin
if Length(Application.Hint) > 0 then begin
StatusBar.SimplePanel := True;
StatusBar.SimpleText := Application.Hint; //this line gives error
end else
StatusBar.SimplePanel := False;
end;
Read of address 0000026C
This very low address is indicative of an offset to a member field of a nil object reference. Run under the debugger and make sure the debugger is set to break on exceptions. When it does, you should be able to work out which object reference is nil.
Quite possibly the AV happens in VCL code although it will almost certainly be due to an error in your code. If the debugger doesn't break at a very helpful location, enable Debug DCUs in the project options in order to see the VCL source code at the point where the exception is raised.
The stack trace you provide suggests that the error is in TApplication.SetHint, whilst performing a string comparison. The first line of TApplication.SetHint reads:
if FHint <> Value then
I bet the offset to the FHint of TApplication is $026C and that, somehow, your Application variable is set to nil. That said, I don't understand why the error would not have been raised earlier at Length(Application.Hint). It's quite hard to debug this remotely!
Having looked at the layout of TApplication, I think we can rule out Application being nil. Perhaps FHint itself has somehow been corrupted, or perhaps even Value. I think it's going to take access to the actual code and a debugging environment to track this down.
Based on the information provided in the stacktrace:
TApplication.Idle:
Control := DoMouseIdle;
if FShowHint and (FMouseControl = nil) then
CancelHint;
Application.Hint := GetLongHint(GetHint(Control)); // SetHint is called next:
TApplication.SetHint:
if FHint <> Value then // This is the UStrCmp which fails with a Int overflow
String + IntOverflow -> non terminating string. So the most probable cause is a string without a terminator.
So where does the string come from...
GetHint(Control) searches the control (which is at the places you clicked with the cursor) and its parent for a hint that is not empty.
GetLongHint searches the string for a | and if found, uses the part following the | else it uses the complete string.
UStrCmp, is a long pieces of assembler code, that calls other pieces of assembler code. Some of them can raise the EIntegerOverflow error.
Advise
Use the debugger (with debug dcu's on) to see which string has no terminator (0 character at the end). And if you find it, try to solve it or expand the question if you want some more help from us.