Delphi BDE Double type Field changed to a String type - delphi

I'm using a BDE TTable which had certain fields which were originally ftDouble.
Because the input to be stored is sometimes non-numeric, I now changed the field type to ftString.
Input into the field is done with a TEdit. When the code gets to:
with tblDM do
begin
Edit;
FieldByName('s01_amt').AsString := Edit1.Text;
Post;
end;
if the entry is not a number, I get the BDE error:
'a' is not a valid floating point
value for field 's01_amt'.

That error message is only created by fields of type TFloatField, which is only created when the TFieldDef has a DataType value of ftFloat. Double-check that you've changed the property think you did.
The field definitions can be populated from the fields themselves. Make sure you've changed the underlying database schema and not just your TTable component.

I would just convert it to a float:
var
dFloat : double;
begin
try dFloat := strToFloat(edit1.txt); except dFloat := 0; end;
edit;
FieldByName('s01_amt').AsFloat := dFloat;
post;
end;

When you changed the field type, did you also change the database's field in the schema (structure in xBASE/Clipper)? If not, you're trying to assign a non-numeric value to a numeric type field, and that's what's causing the exception.
If you're still using DBF style files, you need to change the type of the field in the database from NUMERIC to CHARACTER. You can do it using SQL from the Database Desktop, IIRC, with the BDE's DBASE support.
Just changing the TField's type from ftFloat to ftString won't alter the database storage for that field automatically; you have to do it in both places yourself.

Related

Unable to find record, no key specified

I am coming across this infamous error in my Delphi application.
In my ADODataSet, I have set my CommandType to cmdText, and then set CommandText to
SELECT * FROM production.details
When I do a record change on my DBGrid, it throws the error of "Unable to find record, no key specified".
Under my ClientDataSet, I have set the various fields to have the Provider Flags of [pfInWhere, pfInUpdate, pfInKey], all other fields [pfInWhere, pfInUpdate]
A few people mention that it is the Provider Flags of the ADODataSet that need to be changed, since that is where the query originates. I tried this by the following
ADODataSet.Fields[0].ProviderFlags := [pfInKey, pfInUpdate, pfInWhere];
However, when I do this it throws a different error of List index out of bounds
Eventually I have all this in my DataSourceDataChange procedure:
if mySQLDataModule.dieDetailClientDataSet.ChangeCount > 0 then
begin
dieDetailClientDataSet.FieldByName('die_no').ProviderFlags := [pfInWhere, pfInUpdate, pfInKey];
dieDetailClientDataSet.FieldByName('comment').ProviderFlags := [pfInWhere, pfInUpdate];
dieDetailADODataSet.Fields[0].ProviderFlags := [pfInKey, pfInUpdate, pfInWhere];
dieDetailClientDataSet.ApplyUpdates(0);
end;
More details as requested : this is a MySQL database, the primary key is the same as what is specified in Fields[0]. In the CDS I have already set the ProviderFlags in the Designer Mode. It was because it wasn't working as expected that I read that it has to instead be set in the AdoDataSet as that is where my query has been set, hence me setting it manually here. Because all my fields are set up in the CDS (as there are also calculated fields) for my DBGrid that displays the data, I didn't redo all my fields in the AdoDataSet so there wasn't a designer option there, hence me doing it in code.

Delphi 7 to XE8 assigning binary ADO parameter values

I'm converting a report that resides in a DLL to XE8 so that I can update our report control, which is problematic in Windows 10. In doing this, I've had to replace our ADO data access controls with the built-in ADO controls.
The issues is that our data uses binary keys, and I've run into an issue assigning values to query parameters. In our code, the keys are passed around as strings, and assigned to the parameters and converted by the control at runtime.
Previously, utilizing the old control, it utilized the Delphi DB unit which contains a method .AsBlob which was used in the assignment. See below...
Qry.Close;
Qry.ParamByName('#Id').AsBlob := IdStringValue;
Qry.Open;
In the control's implementation it handled setting the property, which called into SetAsBlob. See below...
Type TBlobData = string;
...
procedure TQryParameter.SetAsBlob(const Value: TBlobData);
begin
Self.DataType := ftVarBytes;
Self.Value := StringToVarArray(Value);
end;
Part of the issue is that Data.DB has changed TBlobData = string; to TBlobData = TArray<Byte>;.
I've tried assigning these values to the query parameters following the same method used in the previous implementation, but it doesn't work.
Qry.Close;
Qry.Parameters.ParamByName('#Id').DataType := ftVarBytes;
Qry.Parameters.ParamByName('#Id').Value := ADODB.StringToVarArray(IdStringValue);
Qry.Open;
I get a fairly generic MSSQL error due to the parameter mismatch, "Application uses a value of the wrong type for the current operation."
The ADO query parameter is defined as VarBytes and the stored procedure accepts BINARY(6) for its only parameter so everything appears to be correct.
I've tried casting IdStringValue from String to AnsiString prior to calling StringToVarArray but it makes no difference.
Anyone know of a way to deal with this? Thank you.
Convert the string value to array of bytes when assigning the parameter.
Qry.Parameters.ParamByName('#Id').AsBlob := TEncoding.Default.GetBytes(StringValue);

Getting error using a parameter with anydac (now firedac) script for firebird

I'm unable to get the following script to execute without error.. It's running in delphi against a firebird database.
var
vScript: TADScript;
begin
vScript := TADScript.Create(nil);
vScript.Connection := xConnection;
vScript.Transaction := xTransaction;
with vScript.Params.Add do
begin
Name := 'DEFVAL'; // also tried ':DEFVAL'
AsInteger := 1;
end;
with vScript.SQLScripts.Add do
SQL.Text := 'ALTER TABLE "PERSON" add "AGE" INTEGER DEFAULT :DEFVAL';
vScript.ValidateAll;
vScript.ExecuteAll;
end
It gives an error of 'Token unknown - line 1, column 48', the parameter (:DEFVAL) location
I've tried assigning the sql text first then calling Params.FindParam but it's not in the list. This works for queries.
Not using parameters and just including the default value in the SQL string works but this code is used as part of a bigger frame work and that option is a last resort.
The reason you get a token unknown error is because ALTER TABLE statements do not allow the use of parameters in this way.
You will need to concat the stringified default value to the ALTER TABLE statement. Since your code is unconditionally applying a default value of 1 in this case then this can be simply included in the statement literal:
with vScript.SQLScripts.Add do
SQL.Text := 'ALTER TABLE "PERSON" add "AGE" INTEGER DEFAULT 1';
If you did need to accommodate potentially variable default values then obviously this would need to change to something similar to:
with vScript.SQLScripts.Add do
SQL.Text := Format('ALTER TABLE "PERSON" add "AGE" INTEGER DEFAULT %i', [iDefaultValue]);
Where iDefaultValue is some integer variable holding the default value required.

I have a syntax error in my insert into statement

I'm using a MS Access database, with the following columns in the Admins table:
Column Type
====== ====
Name Text
Surname Text
Dateadded Date/time
Adminnumber Number(long integer)
Password Text
ID type Autonumber (Not sure if ID is relevant)
This is my code but it keeps giving me a syntax error.
ADOquery1.Active := false;
adoquery1.sql.Text := 'insert into Admins(Name, surname, Adminnumber, Dateadded,password)Values('''+edit11.Text+''', '''+edit12.text+''', '''+edit13.Text+''', '''+edit14.Text+''', '''+edit15.text+''')';
ADOquery1.ExecSQL;
Adoquery1.SQL.Text := 'select * from Admins';
ADOquery1.Active := true;
i have been trying for a day to figure it out but its the same error no matter what code i use. The error is
Project project1.exe raised exception class eoleException
with message 'Syntax error in INSERT INTO statement'.
i have also tried:
ADOquery1.SQL.Add('Insert into admins');
ADOquery1.SQL.Add('(Name , Surname, Dateadded, Adminnumber, Password)');
ADOquery1.SQL.Add('Values :Name, :Surname, :Dateadded, :adminnumber :Password)');
ADOquery1.Parameters.ParamByName('Name').Value := edit11.Text;
ADOquery1.Parameters.ParamByName('Surname').Value := edit12.Text;
ADOquery1.Parameters.ParamByName('Dateadded').Value := edit13.Text;
ADOquery1.Parameters.ParamByName('Password').Value := edit14.Text;
ADOquery1.Parameters.ParamByName('Adminnumber').Value := edit15.Text;
ADOquery1.ExecSQL;
ADOquery1.SQL.Text := 'Select * from admins';
ADOquery1.Open ;
But this code gives me a problem with the from clause
The problem is that Name (and possibly Password) is a reserved word in MS Access. It's a poor choice for a column name, but if you must use it you should escape it by enclosing it in square brackets ([]). You're also missing an opening parenthesis (() after your VALUES statement, and a comma after the :adminnumber parameter.
ADOquery1.SQL.Add('Insert into admins');
ADOquery1.SQL.Add('([Name] , [Surname], [Dateadded], [Adminnumber], [Password])');
ADOquery1.SQL.Add('Values (:Name, :Surname, :Dateadded, :adminnumber, :Password)');
ADOquery1.Parameters.ParamByName('Name').Value := edit11.Text;
ADOquery1.Parameters.ParamByName('Surname').Value := edit12.Text;
ADOquery1.Parameters.ParamByName('Dateadded').Value := edit13.Text;
ADOquery1.Parameters.ParamByName('Password').Value := edit14.Text;
ADOquery1.Parameters.ParamByName('Adminnumber').Value := edit15.Text;
ADOquery1.ExecSQL;
ADOquery1.SQL.Text := 'Select * from admins';
ADOquery1.Open;
(The error can't be moving around, as you say in the comments to your question. The only line that can possibly cause the problem is the ADOQuery1.ExecSQL; line, as it's the only one that executes the INSERT statement. It's impossible for any other line to raise the exception.)
You should make some changes here that are pretty important to the maintainability of your code.
First, break the habit immediately of using the default names for controls, especially those you need to access from your code later. You change the name by changing the Name property for the control in the Object Inspector.
It's much easier in the code to use NameEdit.Text than it is to use Edit1.Text, especially by the time you get to Edit14. It would be much clearer if Edit14 was named PasswordEdit instead, and you'll be happy you did six months from now when you have to change the code.
Second, you should avoid using the default variant conversion from string that happens when you use ParamByName().Value. It works fine when you're assigning to a text column, but isn't really good when the type isn't text (such as when using dates or numbers). In those cases, you should convert to the proper data type before doing the assignment, so that you're sure it's done correctly.
ADOQuery1.ParamByName('DateAdded').Value := StrToDate(DateEdit.Text);
ADOQuery1.ParamByName('AdminNumber').Value := StrToInt(AdminNum.Text);
Finally, you should never, ever use string concatenation such as 'SOME SQL ''' + Edit1.Text + ''','''. This can lead to a severe security issue called SQL injection that can allow a malicious user to delete your data, drop tables, or reset user ids and passwords and giving them free access to your data. A Google search will find tons of information about the vulnerabilities that it can create. You shouldn't even do it in code you think is safe, because things can change in the future or you can get a disgruntled employee who decides to cause problems on the way out.
As an example, if a user decides to put John';DROP TABLE Admins; into edit14 in your application, and you call ExecSQL with that SQL, you will no longer have an Admins table. What happens if they instead use John';UPDATE Admins SET PASSWORD = NULL; instead? You now have no password for any of your admin users.

Delphi - DBGrid does not display UTF8 calculated field

I have a TDBGrid linked to a TDataSource with a TFibDataSet behind. On the OnCalcFields of the dataset I'm trying to add the string 'Russisch (русский)'.
procedure TForm1.pFIBDataSet1CalcFields(DataSet: TDataSet);
begin
DataSource1.DataSet.FieldByName('Language').AsString := ('Russisch (русский)');
The problem is that in the grid the result is displayed as :Russisch(????????)
DataSource1.DataSet.FieldByName('Language').AsWideString :=('Russisch (русский)');
has the same result
FibDataBase component has ConnectParams.Charset set to UTF-8. Also I set in the DBParams value lc_ctype=UTF8.
What I'm doing wrong here?
LE: Delphi XE, Firebird and UTF8 - this does not solve my problem.
LE1: Problem occurs only with calculated fields. Live data 'Russisch (русский)' is displayed correctly.
I guess you made the same mistake I always make. When you create a field like this:
it's ANSI. You have to select WideString for it to be unicode:

Resources