Re: [fpc-devel] Recent changes to TField.SetData
On Tue, 26 Apr 2011, LacaK wrote: Michael, do you plan do something else with this OnValidate problem ? I thought I had done everything, or did I (again) forget something ? Michael. Thanks -Laco. Hi, here are diffs with changes, I hope they will be good for all TDataSet descendants (those specific to FPC and also those which are also for use in Delphi) Code which is general (and is used repeatedly) is placed in TField methods and only small code remains which must be placed into each TDataSet descendant. (I patched only TBufDataSet, but later can be updated also other TDataSet descendants ... remove redundant code and add Field.Validate(...)) Thanks -Laco. The whole code, which is repeated (and can be put in one place) is: if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then begin DatabaseErrorFmt(SNotEditing,[Name],self); exit; end; if (Field.FieldNo0) and not (State in [dsSetKey, dsFilter]) then begin if Read OnlythenDatabaseErrorFmtSReadOnlyField,[Field.DisplayName], Self); Field.Validate(Buffer); end; You are right in your dislike. I will create a 'BeforeSetFieldData' in TDataset and put the code in there, and the same for 'AfterSetFieldData' with the OnChange code. OK, Then please do not forget commit necessary changes also in other descendants (not only in TCustomBufDataSet but also in TParadox, TMemDataset, TFixedFormatDataSet, TDbf) to completelly fix 'missing onvalidate call' problem. Thanks I am still thinking about this. I have other solution, which is relative simple , puts base logic into base TField methods (so all TDataSet descendants can benefit from it) and does not negatively affect existing TDataSet descendants and does not require introduce new methods (BeforeSetFieldData, AfterSetFieldData). Let's distributes repeating used code as follows: 1. base checks before into procedure TField.SetData(Buffer: Pointer; NativeFormat : Boolean); 820 begin 821 If Not Assigned(FDataset) then 822 DatabaseErrorFmt(SNoDataset,[FieldName]); if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then //here should be IMO also dsNewValue DatabaseErrorFmt(SNotEditing,[FDataSet.Name],FDataSet); if ReadOnly and (FieldNo0) and not (FDataSet.State in [dsSetKey, dsFilter]) then DatabaseErrorFmt(SReadOnlyField, [DisplayName], Self); 829 FDataSet.SetFieldData(Self,Buffer, NativeFormat); -- 2. into procedure TField.Validate(Buffer: Pointer); 892 begin if FData S et.Statein[dsSetKey,dsFilter]thenExit893If assigned(OnValidate) Then -- 3. after call into procedure TDataSet.SetFieldData(Field: TField; Buffer: Pointer); begin if not (State in [dsCalcFields, dsFilter, dsNewValue]) then DataEvent(deFieldChange, Ptrint(Field)); end; -- And now all what we must do, is put into method SetFieldData in TDataSet descendants one line: Field.Validate(Buffer); and one line at end: inherited; //calls field change ... this is done by all TDataSet descendants in fcl-db as a last thing It is only draft, so some detils may be changed ... ;-) What do you think ? Laco. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
/ Michael, // do you plan do something else with this OnValidate problem ? / I thought I had done everything, or did I (again) forget something ? There is IMHO unresolved original problem which is in fact, that some TDataSet descendants implement call to Field.Validate in descendant's SetFieldData (for example ZEOS in procedure TZAbstractRODataset.SetFieldData) So if we leave call to Validate(Buffer) in TField.SetData ( http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/packages/fcl-db/src/base/fields.inc?r1=17199r2=17220 ) Then in case of these TDataSet descendants will Validate called twice. (once in TField.SetData and second in descendant's SetFieldData) Please look at diff's which are attached in my email from 13.4.2011. (There are changes which we must/can do to fix it) Thanks -Laco. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
Hi, here are diffs with changes, I hope they will be good for all TDataSet descendants (those specific to FPC and also those which are also for use in Delphi) Code which is general (and is used repeatedly) is placed in TField methods and only small code remains which must be placed into each TDataSet descendant. (I patched only TBufDataSet, but later can be updated also other TDataSet descendants ... remove redundant code and add Field.Validate(...)) Thanks -Laco. The whole code, which is repeated (and can be put in one place) is: if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then begin DatabaseErrorFmt(SNotEditing,[Name],self); exit; end; if (Field.FieldNo0) and not (State in [dsSetKey, dsFilter]) then begin if Read OnlythenDatabaseErrorFmtSReadOnlyField,[Field.DisplayName], Self); Field.Validate(Buffer); end; You are right in your dislike. I will create a 'BeforeSetFieldData' in TDataset and put the code in there, and the same for 'AfterSetFieldData' with the OnChange code. OK, Then please do not forget commit necessary changes also in other descendants (not only in TCustomBufDataSet but also in TParadox, TMemDataset, TFixedFormatDataSet, TDbf) to completelly fix 'missing onvalidate call' problem. Thanks I am still thinking about this. I have other solution, which is relative simple , puts base logic into base TField methods (so all TDataSet descendants can benefit from it) and does not negatively affect existing TDataSet descendants and does not require introduce new methods (BeforeSetFieldData, AfterSetFieldData). Let's distributes repeating used code as follows: 1. base checks before into procedure TField.SetData(Buffer: Pointer; NativeFormat : Boolean); 820 begin 821 If Not Assigned(FDataset) then 822 DatabaseErrorFmt(SNoDataset,[FieldName]); if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then //here should be IMO also dsNewValue DatabaseErrorFmt(SNotEditing,[FDataSet.Name],FDataSet); if ReadOnly and (FieldNo0) and not (FDataSet.State in [dsSetKey, dsFilter]) then DatabaseErrorFmt(SReadOnlyField, [DisplayName], Self); 829 FDataSet.SetFieldData(Self,Buffer, NativeFormat); -- 2. into procedure TField.Validate(Buffer: Pointer); 892 begin if FData Set.Statein[dsSetKey,dsFilter]thenExit 893 If assigned(OnValidate) Then -- 3. after call into procedure TDataSet.SetFieldData(Field: TField; Buffer: Pointer); begin if not (State in [dsCalcFields, dsFilter, dsNewValue]) then DataEvent(deFieldChange, Ptrint(Field)); end; -- And now all what we must do, is put into method SetFieldData in TDataSet descendants one line: Field.Validate(Buffer); and one line at end: inherited; //calls field change ... this is done by all TDataSet descendants in fcl-db as a last thing It is only draft, so some detils may be changed ... ;-) What do you think ? Laco. --- bufdataset.pas.ori Sun Feb 20 14:31:16 2011 +++ bufdataset.pas Wed Apr 13 09:07:18 2011 @@ -1830,14 +1830,10 @@ var CurrBuff : pointer; NullMask : pbyte; begin - if not (state in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then -begin -DatabaseErrorFmt(SNotEditing,[Name],self); -exit; -end; CurrBuff := GetCurrentBuffer; - If Field.Fieldno 0 then // If = 0, then calculated field or something + If Field.FieldNo 0 then // If = 0, then calculated field or something begin +Field.Validate(Buffer); NullMask := CurrBuff; inc(CurrBuff,FFieldBufPositions[Field.FieldNo-1]); --- fields.inc.ori Wed Apr 13 08:50:56 2011 +++ fields.inc Wed Apr 13 09:05:04 2011 @@ -820,12 +820,10 @@ procedure TField.SetData(Buffer: Pointer begin If Not Assigned(FDataset) then DatabaseErrorFmt(SNoDataset,[FieldName]); - if (FieldNo0) and not (FDataSet.State in [dsSetKey, dsFilter]) then -begin -if ReadOnly then - DatabaseErrorFmt(SReadOnlyField, [DisplayName], Self); -Validate(Buffer); -end; + if not (FDataSet.State in dsWriteModes) then +DatabaseErrorFmt(SNotEditing,[FDataSet.Name],FDataSet); + if ReadOnly and (FieldNo0) and not (FDataSet.State in [dsSetKey, dsFilter]) then +DatabaseErrorFmt(SReadOnlyField, [DisplayName], Self); FDataSet.SetFieldData(Self,Buffer, NativeFormat); end; @@ -890,7 +888,7 @@ end; procedure TField.Validate(Buffer: Pointer); begin - If assigned(OnValidate) Then + If assigned(OnValidate) and not (FDataSet.State in [dsSetKey,dsFilter]) Then begin FValueBuffer:=Buffer; FValidating:=True; ___ fpc-devel maillist - fpc-devel@lists.freepascal.org
Re: [fpc-devel] Recent changes to TField.SetData
Small optimalization use dsWriteModes instead of fixed set: - if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then //here should be IMO also dsNewValue + if not (FDataSet.State in dsWriteModes) then DatabaseErrorFmt(SNotEditing,[FDataSet.Name],FDataSet); if ReadOnly and (FieldNo0) and not (FDataSet.State in [dsSetKey, dsFilter]) then DatabaseErrorFmt(SReadOnlyField, [DisplayName], Self); 829 FDataSet.SetFieldData(Self,Buffer, NativeFormat); -- Laco. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
On Mon, 4 Apr 2011, Luiz Americo Pereira Camara wrote: On 4/4/2011 10:28, michael.vancann...@wisa.be wrote: On Mon, 4 Apr 2011, LacaK wrote: Michael Van Canneyt wrote / napísal(a): On Mon, 4 Apr 2011, LacaK wrote: Then please move approprate code at least into TCustomBufDataset.SetFieldData Thanks This code was already there. Should the 'Validate' code be moved there too ? Yes. TDataset descendants are responsible to calling Validate, e.g., zeos call it inside TZAbstractRODataset.SetFieldData. One of the reasons of letting descendants call it it's too improve granularity so Validate can be called in the middle of the routine, as is in zeos, instead to force the call before the TDataset routine. Ok. I will move the code. Michael.___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
--or-- introduce any new method (ValidateFieldData ? ;-))) and let tdatsset descendants use it: {$IFDEF FPC} ValidateFieldData(Field: TField; Buffer: Pointer); {$ENDIF} --or-- some smarter solution ? The whole code, which is repeated (and can be put in one place) is: if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then begin DatabaseErrorFmt(SNotEditing,[Name],self); exit; end; if (Field.FieldNo0) and not (State in [dsSetKey, dsFilter]) then begin if ReadOnly then DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName], Self); Field.Validate(Buffer); end; Laco. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
On Tue, 5 Apr 2011, LacaK wrote: --or-- introduce any new method (ValidateFieldData ? ;-))) and let tdatsset descendants use it: {$IFDEF FPC} ValidateFieldData(Field: TField; Buffer: Pointer); {$ENDIF} --or-- some smarter solution ? The whole code, which is repeated (and can be put in one place) is: if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then begin DatabaseErrorFmt(SNotEditing,[Name],self); exit; end; if (Field.FieldNo0) and not (State in [dsSetKey, dsFilter]) then begin if ReadOnly then DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName], Self); Field.Validate(Buffer); end; You are right in your dislike. I will create a 'BeforeSetFieldData' in TDataset and put the code in there, and the same for 'AfterSetFieldData' with the OnChange code. Michael. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
On 5/4/2011 02:41, LacaK wrote: Then please move approprate code at least into TCustomBufDataset.SetFieldData Thanks This code was already there. Should the 'Validate' code be moved there too ? Yes. TDataset descendants are responsible to calling Validate, e.g., zeos call it inside TZAbstractRODataset.SetFieldData. I agree, but I do not like idea, that SAME piece of code must be repeated in many descendants of TDataSet (TCustomBufDataSet, TParadox, TMemDataset, TFixedFormatDataSet, TDbf, TCustomSqliteDataset etc.) If it was just adding the call before SetFieldData it would take less than 10min, but it's necessary to know where to put so does not break something. So it would be good, if we can put 'validate code' into any new method? and call it once from descendants (be precise only from descendants, which are intended for use in FPC, because components which are also for Delphi must do it in Delphi compatible way and can not use this new method) It's just to lines of code. One of the reasons of letting descendants call it it's too improve granularity so Validate can be called in the middle of the routine, as is in zeos, instead to force the call before the TDataset routine. Yes, but I think, that in many cases can be expected, that 'validate' will be called before any other action, It's necessary to do some checks before calling it. Luiz ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
The whole code, which is repeated (and can be put in one place) is: if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then begin DatabaseErrorFmt(SNotEditing,[Name],self); exit; end; if (Field.FieldNo0) and not (State in [dsSetKey, dsFilter]) then begin if Read OnlythenDatabaseErrorFmtSReadOnlyField,[Field.DisplayName], Self); Field.Validate(Buffer); end; You are right in your dislike. I will create a 'BeforeSetFieldData' in TDataset and put the code in there, and the same for 'AfterSetFieldData' with the OnChange code. OK, Then please do not forget commit necessary changes also in other descendants (not only in TCustomBufDataSet but also in TParadox, TMemDataset, TFixedFormatDataSet, TDbf) to completelly fix 'missing onvalidate call' problem. Thanks I am still thinking about this. I have other solution, which is relative simple , puts base logic into base TField methods (so all TDataSet descendants can benefit from it) and does not negatively affect existing TDataSet descendants and does not require introduce new methods (BeforeSetFieldData, AfterSetFieldData). Let's distributes repeating used code as follows: 1. base checks before into procedure TField.SetData(Buffer: Pointer; NativeFormat : Boolean); 820 begin 821 If Not Assigned(FDataset) then 822 DatabaseErrorFmt(SNoDataset,[FieldName]); if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then //here should be IMO also dsNewValue DatabaseErrorFmt(SNotEditing,[FDataSet.Name],FDataSet); if ReadOnly and (FieldNo0) and not (FDataSet.State in [dsSetKey, dsFilter]) then DatabaseErrorFmt(SReadOnlyField, [DisplayName], Self); 829 FDataSet.SetFieldData(Self,Buffer, NativeFormat); -- 2. into procedure TField.Validate(Buffer: Pointer); 892 begin if FDataSet.State in [dsSetKey, dsFilter] then Exit; 893 If assigned(OnValidate) Then -- 3. after call into procedure TDataSet.SetFieldData(Field: TField; Buffer: Pointer); begin if not (State in [dsCalcFields, dsFilter, dsNewValue]) then DataEvent(deFieldChange, Ptrint(Field)); end; -- And now all what we must do, is put into method SetFieldData in TDataSet descendants one line: Field.Validate(Buffer); and one line at end: inherited; //calls field change ... this is done by all TDataSet descendants in fcl-db as a last thing It is only draft, so some detils may be changed ... ;-) What do you think ? Laco. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
On 5/4/2011 04:38, michael.vancann...@wisa.be wrote: On Tue, 5 Apr 2011, LacaK wrote: --or-- introduce any new method (ValidateFieldData ? ;-))) and let tdatsset descendants use it: {$IFDEF FPC} ValidateFieldData(Field: TField; Buffer: Pointer); {$ENDIF} --or-- some smarter solution ? The whole code, which is repeated (and can be put in one place) is: if not (State in [dsEdit, dsInsert, dsFilter, dsCalcFields]) then begin DatabaseErrorFmt(SNotEditing,[Name],self); exit; end; if (Field.FieldNo0) and not (State in [dsSetKey, dsFilter]) then begin if ReadOnly then DatabaseErrorFmt(SReadOnlyField, [Field.DisplayName], Self); Field.Validate(Buffer); end; You are right in your dislike. I will create a 'BeforeSetFieldData' in TDataset and put the code in there, I still think that is overkill: adding a method to replace just two lines of code. Anyway, if you decide this way, consider another name. Sometimes is to be called in the middle of SetFieldData and the same for 'AfterSetFieldData' with the OnChange code. This one is a bit more odd since currently only bufdataset will use it (or do you plan to replace the existing code in descendants?) Luiz ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
Am 04.04.2011 07:29, schrieb Michael Van Canneyt: On Mon, 4 Apr 2011, LacaK wrote: But It seems to me, that DataChanged is now called 2 times First in TDataSet descendants in SetFieldData method (see bufdataset.pas, dbf.pas, paradox.pp, meds.pp) and second in above mentioned place. So it would be good remove this call from TDataSet descendants. For example for bufdataset.pas: 1860 if not (State in [dsCalcFields, dsFilter, dsNewValue]) then 1861 DataEvent(deFieldChange, Ptrint(Field)); Hm. No, we'll remove the call from TField. There may be other descendants (AnyDac, Zeos and whatnot) that also call DataEvent and OnValidate, and we cannot apply this change on them. Thanks. For example the code in tmsebufdataset: procedure tmsebufdataset.setfielddata(field: tfield; buffer: pointer); var po1: pointer; datasize: integer; begin field.validate(buffer); po1:= getfieldbuffer(field,buffer = nil,datasize); if buffer nil then begin move(buffer^,po1^,datasize); end else begin fillchar(po1^,datasize,0); end; fieldchanged(field); end; Martin ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
On Mon, 4 Apr 2011, LacaK wrote: Michael Van Canneyt wrote / napísal(a): On Mon, 4 Apr 2011, LacaK wrote: Hi, This fix http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/packages/fcl-db/src/base/fields.inc?r1=17199r2=17220 fixes mising call to TField.Validate (before data are written into record buffer) and adds also call to TField.DataChanged (after data are written) But It seems to me, that DataChanged is now called 2 times First in TDataSet descendants in SetFieldData method (see bufdataset.pas, dbf.pas, paradox.pp, meds.pp) and second in above mentioned place. So it would be good remove this call from TDataSet descendants. For example for bufdataset.pas: 1860 if not (State in [dsCalcFields, dsFilter, dsNewValue]) then 1861 DataEvent(deFieldChange, Ptrint(Field)); Hm. No, we'll remove the call from TField. There may be other descendants (AnyDac, Zeos and whatnot) that also call DataEvent and OnValidate, and we cannot apply this change on them. Yes it seems, that we must do it in this way. (Although I do not understand why Borland introduces this strange behavior) Then please move approprate code at least into TCustomBufDataset.SetFieldData Thanks This code was already there. Should the 'Validate' code be moved there too ? Michael.___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
On 4/4/2011 10:28, michael.vancann...@wisa.be wrote: On Mon, 4 Apr 2011, LacaK wrote: Michael Van Canneyt wrote / napísal(a): On Mon, 4 Apr 2011, LacaK wrote: Then please move approprate code at least into TCustomBufDataset.SetFieldData Thanks This code was already there. Should the 'Validate' code be moved there too ? Yes. TDataset descendants are responsible to calling Validate, e.g., zeos call it inside TZAbstractRODataset.SetFieldData. One of the reasons of letting descendants call it it's too improve granularity so Validate can be called in the middle of the routine, as is in zeos, instead to force the call before the TDataset routine. Luiz ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
Re: [fpc-devel] Recent changes to TField.SetData
Then please move approprate code at least into TCustomBufDataset.SetFieldData Thanks This code was already there. Should the 'Validate' code be moved there too ? Yes. TDataset descendants are responsible to calling Validate, e.g., zeos call it inside TZAbstractRODataset.SetFieldData. I agree, but I do not like idea, that SAME piece of code must be repeated in many descendants of TDataSet (TCustomBufDataSet, TParadox, TMemDataset, TFixedFormatDataSet, TDbf, TCustomSqliteDataset etc.) So it would be good, if we can put 'validate code' into any new method? and call it once from descendants (be precise only from descendants, which are intended for use in FPC, because components which are also for Delphi must do it in Delphi compatible way and can not use this new method) One of the reasons of letting descendants call it it's too improve granularity so Validate can be called in the middle of the routine, as is in zeos, instead to force the call before the TDataset routine. Yes, but I think, that in many cases can be expected, that 'validate' will be called before any other action, It seems, that TDataSet descendants never call inherited TDataSet.SetFieldData(Field: TField; Buffer: Pointer); (because it is empty method) So if we move 'validate code' there then in 'our' descendants we put only {$IFDEF FPC} inherited; {$ENDIF} at first line. This does not affect existing tdataset descendants, and FPC dataset descendants can be adjusted to benefit from this. (I know, it is not perfect solution ;-) but I very dislike repeat same 'validate' code in X places) --or-- introduce any new method (ValidateFieldData ? ;-))) and let tdatsset descendants use it: {$IFDEF FPC} ValidateFieldData(Field: TField; Buffer: Pointer); {$ENDIF} --or-- some smarter solution ? Laco. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel
[fpc-devel] Recent changes to TField.SetData
Hi, This fix http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/packages/fcl-db/src/base/fields.inc?r1=17199r2=17220 fixes mising call to TField.Validate (before data are written into record buffer) and adds also call to TField.DataChanged (after data are written) But It seems to me, that DataChanged is now called 2 times First in TDataSet descendants in SetFieldData method (see bufdataset.pas, dbf.pas, paradox.pp, meds.pp) and second in above mentioned place. So it would be good remove this call from TDataSet descendants. For example for bufdataset.pas: 1860 if not (State in [dsCalcFields, dsFilter, dsNewValue]) then 1861 DataEvent(deFieldChange, Ptrint(Field)); Laco. ___ fpc-devel maillist - fpc-devel@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-devel