TClientDataSet.ApplyUpdates() doesn't apply updates - delphi

My Delphi project has a TAdoQuery accesssing data on an MS Sql Server 2014 server, and TClientDataSet that receives the AdoQuery data via a TDataSetProvider. This is created from a Project Template I set up.
Normally, I've found this set-up to work faultlessly, but with this particular project I'm having a problem: ApplyUpdates() fails silently and the Sql Server data is not updated. In my stripped down debugging project, the only code I have, apart from a button-click handler which calls it, is:
procedure TForm1.ApplyUpdates;
var
Errors : Integer;
begin
Errors := ClientDataSet1.ApplyUpdates(0);
Caption := IntToStr(Errors) + '/' + IntToStr(ClientDataSet1.ChangeCount);
end;
After this executes, the form's caption should be 0/0 of course but what it actually says is 0/1. So on the face of it, no errors occurred but the CDSs ChangeCount hasn't been reset to zero as it should be. My q is, how can ApplyUpdates return no errors but the server dataset doesn't get updated.
Fwiw, I added the ChangeCount display as part of my effort to debug the problem. But I'm afraid I haven't been able to follow what's supposed to be going on in the details of the "conversation" between the DataSetProvider and its DataSet to apply the updates on the server.

I ran into this problem recently on a quick project I rustled up without the precaution of setting an OnReconcileError handler, as queried by #mjn.
Once I'd set up the OnReconcileError handler, it was obvious that the problem was that the provider's TSqlResolver wasn't able to identify the row to update. Iirc, the message on the ReconcileError pop-up form was words to the effect of "Unable to locate record. No key specified."
So, first thing I tried was to include this in my CDS's AfterOpen:
CDS1.Fields[0].ProviderFlags := [pfInKey];
(CDS1.Fields[0] is the PK field of the dataset)
Contrary to my expectations, that didn't fix it. After scratching my head for a while, I had a careful look on the server and discovered that the recently-recreated table I was using did not have a primary key index.
Once I'd created the primary key index on the server, the ApplyUpdates problem went away.
However, what puzzles me about this is that prompted by your q, I dropped the primary key index on my server table and the problem hasn't started occurring again (!). I'm guessing this is due to some kind of cacheing effect on my machine but I don't really want to reboot it right now to investigate.

Related

Live Data in FireDac

Is it possible in FireDac get live data from database (SQL Server in my case) without updating DataSources like it was in Paradox. The closest things I could found was Live Data Window of FDTable, but information is very scanty and I don’t even sure that Live Data Window means that data is updated automatically. I need it for some obvious reasons. For example database sends some alert and user sees it without TTimer or constantly updating some specific DataSource.
I never used LiveDataWindows for any of my programs, but I Think, as you mentioned above, you should give FDTable a try.
Here is the Link on how to set it up correctly on MSSSL Server as well.
Embarcadero docwiki
You can build a little trial app using the code from docwiki:
uses
Windows;
...
// Set locale ID to German phone book collation
FDTable1.FormatOptions.SortLocale := MAKELCID(MAKELANGID (LANG_GERMAN, SUBLANG_GERMAN), SORT_DEFAULT);
// Use the the punctuation and other symbols case insensitive sorting
FDTable1.FormatOptions.SortOptions := [soNoSymbols];
FDTable1.IndexFieldNames := 'NAME';
FDTable1.TableName := 'CUSTOMERS';
FDTable1.Open;

Firedac migration from BDE

My name is Stefano Fanti,I am a developer in Plexa.We are actually using your component ( FireDAC ) and I am contacting you in order to solve some
problems we have not been able to fix ourself.
In our company we decided to port a legacy 3-Tier DATASNAP application written on Delphi7 for the new environment Delphi XE7 , and we decided to use the new components library FireDAC too.
Our application is built in this way: a Client side using TClientDataSet directly connected to the Server side with TDataSetProvider/MIDAS and TTable TQuery TDatabase components (BDE)
We replaced BDE components with the related FireDAC ( FDConnection , FDTable and FDQuery ) without issues and we started testing thinking anyway it was not needed to perform it.
After some test , a couple of issues have arosen : the first problem is related to the modifications performed on DB ( MS SQL Server ) , the second is related the the reopening of the table after applying the modifications.
In our case , the RequestLive=True property ( default ) , does’nt allow to apply modifications ( Post/ApplyUpdates) to the Database.
Assigning RequestLive = False , we are able to apply modifications on all table’s records except the first, the last giving us the error .
The above anomaly have been verified on each Database table . However all database tables have the primary key defined properly.
Trying to use FireDAC Help I read the option RequestLive=False excludes fiMeta from Fetch,Options, preventing the component to use the primary key of the table, being otherwise seen correctly in any other case.
After several trials I found two configurations applied to the Server side, allowing us to achieve the result.
First configuration
FDTable.RequestLive= False
FDTable.UpdateMode = upWhereKeyOnly
with FProvider.DataSet.FieldByName (pkField) do
ProviderFlags: = ProviderFlags + [pfInKey];
Second configuration
FDTable.RequestLive= False
FDTable.UpdateMode = upWhereAll
TDataSetProvider.UpdateMode=upWhereAll
The above configurations give us the possibility to apply modifications to tables without errors , but nevertheless don’t allow us to solve the second problem : reopening of the tables for each operation. Indeed , the BeforeOpen and AfterOpen managers are executed each time.
What can I do to avoid the Table to be needless reopened after each modification ?
Are the configurations used to apply modifications correct , or maybe another solution exist allowing us to avoid code modifications causing us to lengthen development times ?
Thank you in advance for your help.
Best regards
Stefano Fanti

FireDAC - Show SQL after Macro Expantion

I am trying to use Macros in FireDAC to Preprocess my SQL Queries. I have a TADQuery object on a Data Module with the SQL set to something like:
Select * from MyTable
join OtherTable on MyTable.Key = OtherTable.Key
&Where
Then in my code I do this:
WhereClause = 'stuff based on my form';
Query.MacroByName('Where').AsRaw := WhereClause;
Query.Open;
This has worked great for complicated queries because it lets me make sure my fields and join conditions are correct using the SQL Property editor.
My problem is when the SQL statements ends up invalid because of my where clause. Is there any way to see the SQL after pre-processing that is going to be executed? Right now I am catching the FireDac errors and showing the SQL that is on EADDBEngineException object. However that is still showing my original SQL with the macros. If I can't get to it after the error happens is there anyway to force the Macro replacement to take place so I can look at the SQL in the debugger to help me see what is wrong.
If it matters I am connecting to a MS Access database with the goal of moving to SQL Server in the near future.
Apart from using Text property, to monitor what SQL is actually going to the database engine, consider using the "FDMonitor" FireDAC utility. According to the DokWiki pages (below):
drop a TFDMoniRemoteClientLink component on your form,
Set its Tracing property to True,
Add the MonitorBy=Xxx connection definition parameter to your existing FDConnection component. You can do this in the IDE object inspector, by selecting your FDConnection component, expanding the Params property, and setting MonitorBy to mbRemote.
Note that the TFDMoniXxxxClientLink should come before TFDConnection in the data module or form creation order, so adjust this by right clicking on the form or data module, then Creation Order, and moving the TFDMoni.. component above the FDConnection.
Also, it's helpful in the options of the TFDMoniXxxxClientLink, to disable most of the events being recorded, otherwise all the data returned is also shown in the FireDAC monitor. Expand the EventKinds property, and turn all the event kinds off, except for perhaps ekConnConnect, ekConnPrepare, and ekCmdExecute.
Then open the FireDAC Monitor from the IDE, (Tools > FireDAC Monitor). Start your app only once the monitor is running. Double click on a trace event (in the Trace Output tab), and you will see the actual SQL sent to the database in the bottom pane.
It also seems likely that adding the EventType of ekConnPrepare as mentioned above, would show you when the query's Prepare is called, but I haven't played enough with it say for sure.
Please see the following pages on the DocWiki for more information:
Overview: FDMonitor
How to: Tracing and Monitoring (FireDAC)
Other FireDAC utilities: Utilities (FireDAC)
(Just to remove this question from list of unanswered questions)
From comments:
Well, I've roughly checked what's happening there and I'm still not
sure if calling Prepare (which is useless for you as I get) is the
minimal requirement to trigger that preprocessing. Though, the
preprocessed SQL, the one which is sent to the DBMS you can access
through the Text property (quite uncommon name for such property). – TLama Feb
21 '14 at 8:18

Complex T-SQL script executed via TADOQuery is firing "Multiple-step OLE DB operation generated errors."

I have a very large block of SQL that I am trying to execute inside of Delphi, against a Microsoft SQL Database. I am getting this:
Multiple-step OLE DB operation generated errors.
Check each OLE DB status value, if available. No work was done.
The script has multiple sql IF statements followed by BEGIN and END blocks with invocations of stored procedures, declaration of variables, and EXEC inside that. Finally it returns some of the variable values by SELECT #Variable1 AsName1,#Variable2 AsName2....
The above multi-step error is coming in as an OLEException from ADO, not from the Delphi code, and happens after all the SQL exec-stored-procedure have occurred, and therefore I suspect it's firing this OLE exception when it reaches the final stage which SELECT #Variable1 AsName1,... to get back a few variable values for my program to see them.
I know about this retired/deprecated MS KB article, and this is unfortunately not my actual issue:
http://support.microsoft.com/kb/269495
In short that KB article says to fix a registry key and remove "Persist Security Info" from the connection string. That's not my problem. I'm asking this question because I found the answer already and I think that someone else who gets stuck here might not want to waste several hours finding potential issues when there are several that I have found after searching for solutions for several hours. Anyone who wants to add another answer with different options, is fine, and I'll select yours if it's reproducible, and if necessary I'll turn this one into a Community Wiki because there could be a dozen obscure causes for this "ADO recordset is in a bad mood and is unhappy with your T-SQL" exception.
I have found several potential causes that are listed in various sources of documentation. The original KB article in the question suggests removing the 'Persist Security Info' from my ADO connection string, however in a standalone test in an application with just a TADOConnection and a single TADOQuery, the presence or absence of Persist Security Info had no effect, nor did explicitly setting it True or False.
What DID fix it was removing this CursorType declaration:
CursorType=ctKeyset
What I have learned is that bidirectional ADO datasets are fine for SELECT * FROM TABLE in ADO but are not so fine for complex SQL scripts.
Potential source of this error is updating char field with large value.
Example: Form has edit box with max length property set to 20 characters and Oracle database table has field defined as char(10).
Updating with 10 characters (or less) will work fine while updating with more then 10 characters will cause 'Multiple step...' error on ADOQuerry.UpdateBatch().
You also have to know that CHAR will allways have 20 characters. Consider Trimming value in edit box. CHAR behaves different than VARCHAR2 type.
If you have a query with parameter ,check the number of parameters in the query is matched with script...!

insert to a different table onNewRecord

got an ADOQuery that has OnNewRecord event.
on the procedure i try to add data automaticaly to another table. the data is a few rows that are needed and handled in clientDataSet in case of cancellation.
at the loc
OtherAdoQuery.insert;
I get error that ADOQuery failed to insert null into a non null field. I am in insert mode, however I NEVER ASKED DELPHI TO POST! i dont find why it posts.
Edit: could you help me find a hint on this problem?
some more clarification:
at
ADOQuery.onNewRecord();
begin
CliendDataSet.insert; //here goes to post for ADOQueryPost. where ClientDataSet was in Browse State
end;
Edit:
this bug does not make sense! look at the stack trace:
beforePost
newRecord
myFunc
where myFunc does cause NewRecord with the Insert.
I'm not too familiar with TAdoQuery, but I know how to track down an error like this. First, if you don't already have it set, go into Project Options and turn on Use Debug DCUs under the Compile tab, then run a full build and run it. When you get that exception report in the debugger, hit Break and you should end up inside the code for the TAdoQuery or one of its sub-objects. Try examining the call stack. If you look up a few calls you'll probably find something that you did is calling something else that's calling Post. Follow the stack trace back until you reach your code and you'll get an idea of what's going on, and if you analyze it a little you should find some way to prevent the problem.
Having said that, let me make a quick guess as to the cause of your problem: When you call Insert on a dataset, if the dataset is already in appending mode because you previously called Insert or Append and didn't follow up with a Post, it will call Post itself before setting up a new row for you to work on. Maybe this is what's happening to you?
the answer was from a connection between the tables.
the ADOQuery.dataSource was set the DataSet of the ClientDataSet.
this mad so much damage, and no hint by the delphi.

Resources