I'm trying to use Delphi 11.2's TFDBatchMove to make a copy of a record in a Firebird database table. The table contains at least one column which is set to "not null" and a default value for the column is specified.
The reader for the TFDBatchMove is a TFDBatchMoveDataSetReader which gets a TFDQuery assigned to .DataSet. That query selects all the columns to copy, which excludes the "not null" column!
The Write for the TFDBatchMove is a TFDBatchMoveSQLWriter, which only gets the DBConnection assigned and the correct TableName.
At runtime I get an exception on .Execute of the TFDBatchMove:
First chance exception at $7720E292. Exception class EIBNativeException with message
'[FireDAC][Phys][FB]validation error for column "MEINE_TABELLE"."MEINE_SPALTE", value "*** null ***"'. Process MeineExe.exe (6728)
How to prevent it trying to write to that column?
I meanwhile found out that the writer has already some event assigned where I can modify the data written based on which column it tries to write to and there I can (and even need to) set a value for that not null colmum which differs from the default specified in the database anyway.
So one can either "close" this question as "resolved" or wonder why the TFDBatchMove seemingly tries to write null into that column, which was not in the select clause of the reader instead of letting the DB use the default value set up.
Related
How can we use locate function or a same operation function using a calculated field in delphi Tadotable?
something like this
SampleAdotable.locate('samplefield',text,[lopartialkey]);
where samplefield is a calculated field in SampleAdotable.In normal case an exception with this message is created:
Item can not be found in the collection corresponding to the requested name or ordinal
thank you
If your SampleField is of type fkCalculated, I don't think you can use this field as a field whose value you try to locate in a call to Locate.
The reason is that Locate calls TCustomADODataSet.LocateRecord which generates the error you quote and the reason it does is that SampleField is not a field in the ADO Recordset underlying the TCustomADODataSet. The exception occurs in the call to Cursor.MoveNext.
To do what you want, try constructing a calculated field in the SQL expression used to obtain the row data from the database. Depending on the server you are using, you may need to use a TAdoQuery instead of a TAdoTable to get the rows.
I'm running into a situation where a record is returning null for getPersistentValue(name) even when I can verify that the value was NOT null.
Test:
//get it
def team =Team.get(params.id)
if(team){
log.debug(team.dateCreated);//logs date correctly
team.properties=params;
log.debug(team.getPersistentValue('dateCreated');//logs NULL???
}
It does not do this for all records. The only obvious difference I can find is the "version" column kept by grails, but manually messing with this didn't alter the behavior. What situations would cause this method to return null when the record exists and the value was set on it?
When you have a select * from XXX query eventually you can get more fields than you expected, do you know if there is a way to check if new fields have been added since you created that query and defined its persistent fields ?.
Normally you can open your queries without having to worry if new fields have been added. If new fields are not within your persistent fields then you just won't see them. But on a Datasnap REST Server you get an AV when trying to return a Dataset from a query that now has more fields than when you created its persistent fields.
If would like to know if there is a quick check that I can do so I can return a more helpful error instead of an AV.
Something like :
MyQuery.Open;
if MyQuery.FieldDefs.Count <> MyQuery.Fields.Count then begin
raise Exception.Create('The number of returned Fields doesn''t match the expected');
end;
Thank you
If the dataset has persistent fields and you want those additional fields to be created when the query opens, you have to set dataset.FieldOptions.AutoCreateMode to acCombineAlways first.
Now after opening the query, you can check for the existance of additional fields with lcAutomatic in dataset.Fields.LifeCycles.
In case you are interested in which fields are new, just iterate dataset.Fields and check for field.LifeCycle = lcAutomatic.
BTW, using the setting above you probably might not need that check anymore.
I found a very strange behavior in our grails application today that i want to share with you.
We are using grails 2.3.11 on mysql 5.1.48.
We had a DomainObject.findById( id ) in one of your Controller actions.
We failed to check the id for a null value so DomainObject.findById( null )
would be called when no id is passed as an argument.
Normally DomainObject.findById( null )
will return null but there is a special condition that will yield other results!
If the controller action called before that inserted a new record in the database (lets call it Object B), regardless of the domain object stored, the DomainObject.findById( null ) will find the DomainObject with the same Id the Object B got on insert.
So when the controller action called before saved anything the findById(null) will return a row. And that row will have the same id the last inserted element got.
I am totally aware that using findById(null) is not the desired way to do it but I was quite shocked about the results it yielded. But returning any seemingly "random" result seems very strange to me.
I also want to note that DomainObject.get(null) will not suffer from this problem.
Anybody else witnessed this?
There is an active Jira pointing in this direction: https://jira.grails.org/browse/GRAILS-9628 but its not really describing this issue.
We don't really support passing null as an argument to a dynamic finder like that. Dynamic finders have explicit support for querying by null. Instead of DomainClass.findByName(null) you would call DomainClass.findByNameIsNull(). If you have a reference that may or may not be null, instead of passing that as an argument to a dynamic finder, the code can almost always be made cleaner by writing a criteria query or a "where" query that has a conditional in it.
I hope that helps.
Thx for your information scot.
I have further details. This behaviour is also altered by the underlying database.
While mysql suffers from this, maria-db (a mysql clone) does not!
So what happens is bound to the underlying database system.
That should not happen to an abstraction layer ....
I want a simple SQL (trigger) for duplicate checking.
My table name is test1 with 2 columns, code and sname. Before inserting a new record, check if the record already exists: if it does, generate an error and do not insert; if it does not, let the insert proceed.
How do I do that?
The simplest, most reliable way to ensure that there is no duplicate data in the table is not using triggers at all, but using UNIQUE or PRIMARY KEY constraints:
CREATE TABLE test1
(
code INTEGER NOT NULL PRIMARY KEY,
sname VARCHAR(32) NOT NULL UNIQUE
);
The four constraints (two NOT NULL, one PRIMARY KEY, one UNIQUE) automatically ensure that no duplicate records are inserted into the table.
If you choose to add a trigger, it will be duplicating the work that is done by these constraints.
As to how to do it, you will need to create a stored procedure which is invoked from the trigger statement. It will be given the new code and new name, and will do a SELECT to see whether any matching record occurs, and will raise an exception if it does and will not raise an exception if not.
CREATE PROCEDURE trig_insert_test1(ncode INTEGER, nname VARCHAR(32))
DEFINE ocode INTEGER;
FOREACH SELECT code INTO ocode
FROM test1
WHERE code = ncode OR sname = nname
RAISE EXCEPTION -271, -100, "Value to be inserted already exists in table test1";
END FOREACH;
END PROCEDURE
Then you use:
CREATE TRIGGER ins_test1 INSERT ON test1
REFERENCING NEW AS NEW
FOR EACH ROW (EXECUTE PROCEDURE ins_trig_test1(new.code, new.sname))
In Informix 4GL, you can either create strings containing these statements, and then PREPARE and EXECUTE (and FREE) them, or you can use SQL blocks:
SQL
CREATE TRIGGER ins_test1 INSERT ON test1
REFERENCING NEW AS NEW
FOR EACH ROW (EXECUTE PROCEDURE ins_trig_test1(new.code, new.sname))
END SQL
But, as I said at the outset, using triggers for this is not the best way to go; it is redundant given the table definition.
I've not run any of the SQL or SPL past the server; you'll need to check that the semi-colons are in the right places in the SPL, as SPL is fussy about that.
You can find the syntax for the SQL and SPL statements in the Informix 11.70 Information Centre.