Live Data in FireDac - delphi

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;

Related

Permission needed for using GetTableNames

I have a Delphi/C++ builder app that uses Firedac to connect to a Sybase ASE database using the ODBC connection. When connection to the database, if I use the database's system admin (SA) user id/password, everything works fine and GetTableNames comes back with the list of tables in the database. But if I use a regular user to connect to database, GetTableNames comes back with an empty list. My question is, what permissions should I give the regular user for this to work.And as a side question, does anyone know what kind of command Firedac sends to database to get the table names?My code in Delphi looks like:
DBConnection.GetTableNames('', '', '', tableNameList, [TFDPhysObjectScope.osMy], [TFDPhysTableKind.tkTable]);
and in C++ Builder it looks like:
DBConnection->GetTableNames(L"", L"", L"", tableNameList, TFDPhysObjectScopes() << TFDPhysObjectScope::osMy, TFDPhysTableKinds() << TFDPhysTableKind::tkTable);
Thank youSam
For generic ODBC drivers it's the SQLTables function that FireDAC calls. Unfortunately, for the SAP Adaptive Server Enterprise driver I haven't found any information about the implementation of this function. It is the implementation detail, so it's not the issue.
The only note I found is this (for different products), for example:
sp_tables
This function corresponds to the ODBC function SQLTables.
So it's possible that the ODBC driver calls the sp_tables stored procedure in its SQLTables function implementation for that product, but no one explicitly said that (only that it corresponds).
What's more, for SAP Adaptive Server Enterprise, there is no such note by its sp_tables procedure. But you can give it a try. Or better yet, if you have some kind of command monitoring tool, use it to track what your driver calls from its SQLTables function implementation.
In any case, it is an implementation detail you should not care about, nor rely on.
The problem was NOT permissions, it was the 5th parameter of GetTableNames. The 5th parameter is the Scope which determines what kind of tables would be reported back. osMy means tables/objects owned by the logged-in user. But normally all tables in a database, are owned by SA/dbo. Adding osOther to the parameter will fix the problem. So the correct way of calling the function in Delphi would be:
DBConnection.GetTableNames('', '', '', tableNameList, [TFDPhysObjectScope.osMy, TFDPhysObjectScope.osOther], [TFDPhysTableKind.tkTable]);
and in C++ Builder it should look like:
DBConnection->GetTableNames(L"", L"", L"", tableNameList, TFDPhysObjectScopes() << TFDPhysObjectScope::osMy << TFDPhysObjectScope::osOther, TFDPhysTableKinds() << TFDPhysTableKind::tkTable);
Which will return the name of all tables that are not system tables.I must mention that I blame Embarcadero for poor documentation of TFDPhysObjectScope. The documentation does NOT explain what any of these values means. Way too often (as in this case) we see this:
Embarcadero Technologies does not currently have any additional
information. Please help us document this topic by using the
Discussion page!
And we are forced to guess and/or try-fail.

TClientDataSet.ApplyUpdates() doesn't apply updates

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.

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

RawHeaders.Values in Delphi 2010

I'm using the component in delphi indy idhttp 2010 and I have the following problem, I'm trying to get all the values ​​of rawheaders idHTTP1.Request.RawHeaders.Values ​​['User-Agent'], the only one I know of is user agent and I wonder where I can find the list of values ​​to use in RawHeaders.Values​​.
Does anyone could help me?
Your question can be read in few ways.
You ask about "to get all the values ​​of rawheaders" - that is "read, not modify".
And then you tell about "values ​​to use in RawHeaders.Values​​" - which is "write, not read".
Actually it is hard to guess what did you meant here.
Well, if you want to read all the values that are contained there, you are to start from documentation for idHTTP1.Request: http://www.indyproject.org/docsite/html/TIdEntityHeaderInfo.html
There you click on "properties" link, select "RawHeaders" property and - after reading property RawHeaders: TIdHeaderList; - come to http://www.indyproject.org/docsite/html/TIdHeaderList.html
There you read that RawHeaders - just as expected - are a subclass of TStringList thus you can read it with all the usual TStrings-related methods.
Like
idHTTP1.Request.RawHeaders.SaveToFile('1.txt');
s := idHTTP1.Request.RawHeaders.CommaText;
with idHTTP1.Request.RawHeaders do for i := 0 to Count - 1 do begin s := Strings[i]; ... end;
for s in idHTTP1.Request.RawHeaders do begin ... end;
etc.
Alternatively if you want to write some sane and safe values, you are to start from documentation for idHTTP1.Request: http://www.indyproject.org/docsite/html/TIdEntityHeaderInfo.html
There you can see the link to "Hypertext Transfer Protocol version 1.1" where you can find most of them
Or you can add some custom non-standard headers with "X-" prefix, after testing that your server would not break when found them.
Also note that there are some frequently use though non-standard headers or their parameters, like in content-disposition. Some of them are probably retroactively described by communities like HTML5 working group. Or maybe not,
Also note that there are a number of sub-protocols built on top of HTTP. Like WebDAV. Like file transfer in Gnutella2 protocol, etc. They may add their own custom headers, that were not described in their founding HTTP protocol. Read the documentation on those protocols, you may be interested in (if any).

TDBGrid doesn't update when multiple users are editing it

I am developing an application which has a simple database. All of the functions are going well but when a user is editing the database from the program, the other user cannot see the content immediately. The other user needs to close the program and reopen it for the data to appear and its DBGrid be updated with those changes form the other computers. I am using Delphi 7 for this and ZeosLib to access my Firebird database. I tried using the refresh button on the DBNavigator but it doesn't work.
The components I used to connect to the database are:
ZConnection
ZQuery
DataSource
DBGrid
DBNavigator
This is the code for my ZConnection and ZQuery.
object ZConnection1: TZConnection
ControlsCodePage = cGET_ACP
UTF8StringsAsWideField = False
Connected = True
Port = 3051
Database = '192.168.254.254:test'
User = 'test'
Password = 'test'
Protocol = 'firebird-2.5'
Left = 96
Top = 8
end
object ZQuery1: TZQuery
Connection = ZConnection1
Active = True
SQL.Strings = (
'select * from "test"')
Params = <>
Left = 128
Top = 8
object ZQuery1ID: TStringField
FieldName = 'ID'
Required = True
Size = 8
end
Sounds like you're running afoul of ACID. This is a basic guarantee of SQL-style databases, that all database updates will be Atomic, Consistent, Isolated, and Durable, and is accomplished through transactions.
Specifically, you're having trouble with the Consistency and the Isolation, which ensure that an external viewer never sees an update before it's finished, even if that update contains more than one change. (The classic example is a bank transfer, which requires subtracting money from one account and adding it to another. If you only see one of these two actions but not the other one, you have bad data.)
You can think of a transaction as an independent view of the state of the database. Every database connection has its own transaction, and any changes it makes are invisible to anyone else (Isolated) until they Commit (finalize) the transaction. Depending on the transactions' isolation settings, they may remain invisible to other users even after that, if they have an ongoing transaction, until they commit their transaction and begin a new one. It sounds like your code isn't taking this into account.
If you need updates to become visible immediately, you'll want to ensure that the transaction's isolation mode is READ COMMITTED, and set up database events to send notifications to connected clients when various things get updated, so the clients can perform refreshes of their data. You'll also want to ensure that the user updates result in a Commit action right away, so that the isolated data will become available.
Since I don't use ZeosLib, I can't explain all the details of how you'll need to set this all up, but this is enough to get you on the right track.
I suggest that you add a timer to the form which displays the grid. Set the timer so that it fires its OnTimer event once a minute (or longer). In this event, close the query then reopen it. This way, everyone always gets current information (albeit a minute late).
with qWhatever do // this is the query which is connected to the grid
try
disablecontrols;
close;
open
finally
enablecontrols
end;
For a multi-user application, where clients need to receive notifications, one option is to use Firebird events to send a 'broadcast' message for every data change (SQL INSERT, UPDATE or DELETE).
Clients can 'register' (listen) for a specific message type, and whenever the Firebird server sends a message with this type, they will receive it, and run client application code, which in your case would refresh the user interface (grid).
While this can be a sufficient solution in many simple use cases, there are also some restrictions. I recently blogged about this topic here:
Firebird Database Events and Message-oriented Middleware
(I am author of middleware libraries for Delphi and Free Pascal)
I solved this problem by adding this before query.
IBDatabase1.Close;
IBDatabase1.Open;

Resources