Creating readwrite field at runtime in Dataset - provider - clientdatatset - delphi

I am trying to create an additional field that is NOT ReadOnly at runtime in Delphi. I have a TADOQuery with SQL on the lines of
SELECT *,CAST(0 AS BIT) AS CheckField FROM MyTable WHERE KeyField = :KeyValue
This links through a TDatasetProvider to a TClientDatset.
My issue is the resulting field in the TClientDataset ends up as ReadOnly.
The TClientDataset is created at design time to link into grids etc. The other components are created at runtime in a separate object.
I have had a similar setup working , but with everything created at design time. I resolve this problem by creating persistent fields on the TADOQuery component and setting the CheckField's ReadOnly property to False prior to opening the TClientDatset. I am unsure as to how to do this at runtime given the field component doesn't exist until I have opened the ClientDatset and by then it's too late to set it's readonly property!

The problem is the CAST().
The dataset doesn't analyze what the underlying field might be that's being CAST; it just knows that it's a function result that is placed into the column, and you can't edit a function result.
You could just SELECT *, 0 AS CheckField, and then set a validation on the resulting field to restrict the values to 0 and 1.

Related

Delphi / Rad Studio 10.4 - TSimpleDataSet: cannot expand Connection and DataSet property

when I use a TSimpleDataSet, the IDE cannot allow me to expand Connection and DataSet property.
See the image below. I don't have the arrow to expand properties but only on Connection and DataSet.
Please give me support!
Your project needs to contain a TSqlConnection component before you can set the Connection property of a TSimpleDataSet.
Do this:
Start a new VCL project.
Add a TSqlConnection and a TSqlQuery to the form.
Configure the TSqlConnection to access a Sqlite database on your system.
Set the SqlConnection of SqlQuery1 to SqlConnection1.
Now ...
Add a TSimpleDataSet to the form.
Set the Connection property of SimpleDataSet1 to SqlConnection1. This answers your first point.
Notice that you still can't change the DataSet property of SimpleDataSet1. That's working as designed, because it is only supposed to use this internally-created dataset.
Open the DataSet property of SimpleDataSet1 and set its CommandText property to do a SELECT query against one of the tables in the Sqlite databaase.
Set SimpleDataSet1.DataSet.Active to True. That'h how you use TSimpleDataSet's DataSet property.
Next, see https://docwiki.embarcadero.com/RADStudio/Sydney/en/Using_TSimpleDataSet and https://docwiki.embarcadero.com/Libraries/Sydney/en/SimpleDS.TSimpleDataSet.

How do I add data to a database in Delphi by using edits instead of a DBNavigator?

My interface is very basic. It just includes edits for the user to input data into a database, when they click the button i want it to add the data into my database.
You can easily do this.
Go to the Data Controls tab of the Component palette.
Select a TDBEdit and place it on the same form as your DBNavigator. The IDE will name this DBEdit1
Set the Datasource property of your DBEdit1 to the same datasource as your DBNavigator.
Set the DataField property of DBEdit1 to the name of a field in your dataset.
Compile and run.
That's it. Leave your DBNavigator on your form because you will find that when you make a change to the contents of DBEdit1, its Save and Cancel buttons automatically enable to let you save or cancel the change.
Also, you'll find that if you click your DBNavigator's '+' button, which begins the insertion of a new record into your table, you can then type the field values for the new record into your DBEdits.
Don't use normal non-DB-aware TEdit components and a dynamically-created Sql statement which concatenates the TEdits's contents with other Sql as suggested in the other answer which briefly appeared here and now seems to have been deleted - it is a waste of time, but much more importantly renders your app vulnerable to Sql-Injection - see https://en.wikipedia.org/wiki/SQL_injection. By sending the server an unverified Sql statement which includes what the user has typed into a TEdit, you're effectively providing the user with an opportunity to type additional Sql statements into the TEdit and that is exactly how Sql injection can occur. On the other hand, when you use TDBEdits, the Sql for updating the database record is automatically generated by Delphi's TDataSet framework in a way which does not provide a similar opportunity for Sql Injection.
If some reason you absolutely have to generate your own Sql Update statements, to minimise the risk of Sql Injection, make sure that you use a parameterised Update statement, that is, one where the changed field values are specified as values of parameters in your TDataSet-descendant's Parameters object, rather than in the Update Sql itself. An example of a parameterised Update statement might be:
Update MyTable set FieldA =:FieldA, FieldB=:FieldB where RowID =:RowID
where :FieldA, :FieldB and :RowID are the parameters.

How to add a non-bound column to a DevExpress DB QuantumGrid

I am using these components:
UniDac for connection to mysql database
DevExpress for QuantumGrid
IDE:
Embarcadero Rad Studio XE2
I have a cxGrid component with one level and a cxGrid1DBTableView specified as the level's View. I can get data from my database and edit it in the grid. I want to add a column that is not in the bound DataSet. When I specify the Column properties value as CheckBox I can see the column but I can't change the value from unchecked to checked by clicking it. The field doesn't have a DataBinding assigned to it. I tried other types of Properties but all are the same I cant change the row value in the grid.
I've been searching for a way to fix this for couple of days, so im hoping you guys can help me.
Are you trying to add a checkbox item that does not have a database field behind it? I have this on one of my forms.
In addition to setting the Properties to "Checkbox" you need to set the DataBinding -> ValueType to "Boolean". The DataBinding->FieldName can be left blank.
To access the values or change their defaults you can use the DataController like this:
View.DataController.Values[i, CheckBoxFieldIndex] := true;
In addition you need to set
DataController.DataModeController.SmartRefresh := true;
To set that option you will also need a KeyField defined for the Controller (DataController.KeyFieldNames)
Setting the SmartRefresh to true will prevent the grid from trying to get an updated value from the underlying dataset. You need to prevent the refresh or the value for the non-bound column will be set back to Null. This comes with some restriction on how you update your dataset. Any changes made to the data in code will not be reflected by the grid unless you explicitly refresh the grid.
You also have to fill the field View.DataController.KeyFieldNames with one of the datasets fields. At least I need it in Delphi 7.

TClientDataSet.Cancel Loses TDBMemo Values

I have a form with the following controls: TDBEdit, TDBMemo, TDataSource, TClientDataSet
If the user edits the fields and then clicks a button on the form that simply calls the MyCDS.Cancel method (to cancel the edits), the TDBEdit fields revert to their original values, but the TDBMemo fields are not reverted (they are set to blank values).
The TClientDataSet is populated from a MSSQLServer 2008 database. The TDBEdit fields are nvarchar(255) in the database, and the TDBMemo are nvarchar(max) or xml fields.
Looking at the values in the Debug Inspector (ctrl+F7, MyCDS.FieldByName('afield'), Inspect) shows the following for one of the nvarchar(max) fields:
DataSize = 0
DataType = ftWideMemo
Size = 1
This is the same for all of the nvarchar(max) and xml fields whether or not the underlying field has data or not.
It appears that there is an incompatibility between the nvarchar(max) (which is treated as a ftWideMemo) and the TDBMemo control.
Has anyone seen issues like this before? Do you have any suggestions how to resolve it?
In case anyone in interested, the problem is a bug in the TClientDataSet component. The problem only occurs when you clone a dataset, don't have a Provider and turn LogChanges off. If you edit a record on the cloned dataset and then cancel the edit, then any memo fields lose their values.
Since you don't necessarily use a Provider in file-based applications, it doesn't always make sense to enable LogChanges. However, to work around the limitation with memo fields, you can leave LogChanges on, and then call MergeChangeLog after operations that change the cloned dataset.
I created a program that demonstrates the issue. See the Embarcadero incident QC#110511.

How to retrieve information from a field that has the property Required set as false?

I have the following DBX structure in my software:
TSQLDataSet -> TDataSetProvider -> TClientDataSet
One of the fields from my TClientDataSet has the property Required set to false, because this field auto increments based on triggers and generators on the database (Firebird).
However, after configuring both TSQLDataSet and TClientDataSet with this field not being required, I'm getting really weird results when I try to read this field from my TClientDataSet. I suspect that I might need to do something extra to force my TClientDataSet to acquire the value of this field in this condition.
What am I missing here?
Thanks in advance.
EDIT
The help file for the Required property says something about this, but I couldn't quite understand what it want me to do.
Description
Specifies whether a nonblank value for a field is
required.
Use Required to find out if a field requires a value or if the field
can be blank.
If a field is created with the Fields editor, this property is set
based on the underlying table. Applications that set Required to true
for fields that must have values (for example, a password or part
number), but for which the underlying table does not require the
field, must write an OnValidate event handler to enforce the property.
When the Required property reflects a property of the underlying
database table, trying to post apply a null value causes an exception
to be raised. Applications that set the Required property to true when
the underlying table does not require the field, should raise an
EDatabaseError exception on null values in the OnValidate event
handler in order to achieve the same result.
EDIT 2
Forgot to mention: between the TDataSetProvider and the TClientDataSet, there is a DataSnap layer (the TClientDataSet connection is made with a DataSnap driver).
EDIT 3
I created a small test case with this DataSnap setup and it worked perfectly. The project is legacy, messy and I guess that either I have an obscure option configured somewhere that is biting me or I have stumbled in a DataSnap bug.
Haole, have you tried TClientDataset.RefreshRecord after inserting? Or even TClientDataset.Refresh?
Having generators, you can even get the generator in advance (before calling ApplyUpdates) in a query like select gen_id(generator,1) from RDB$Database (it's from memory, don't have Firebird here to test) and fill the PK field in advance.
EDIT: seems this is a heisenbug. I would try to remove the components and reconfigure them again from scratch (which means: after you remove, save and close Delphi).
Or even better, create an empty project with just that needed query configuration and try to view that data in a TDBGrid. If this problem still happens, maybe your FB installation have some component corrupted (or even Delphi installation)
Seems that the problem was an outdated field being read as an INTEGER and it was a SMALLINT in the database.
This problem was hard to debug and this question was misleading. Thanks for everyone that helped me debug this.

Resources