2

TClientDataSet に新しく挿入されたレコードで次のコードを使用すると:

cdsMyDateField.OldValue <> Null

EConvertError が発生します。

''0.0' is not a valid timestamp'.

DelphiのVCLのコードを見ると、値(Null)が無効なDateTimeであるため、値をTDateTimeに変換しようとすると、この例外が発生しますが、バリアントを比較していると、Nullになるバリアントが返されると思いましたこの場合、それは起こらず、代わりにこの例外が発生します。

State = dsInsert すべての OldValue が Null であるかのように、値を比較する前に DataSet.State = dsInsert かどうかを確認できることはわかっていますが、OldValue が単にすべてで Null を返すのではなく、値を変換しようとする理由を理解したいと思います。 State = dsInsert の場合のフィールド。

誰か光をくださいませんか?

4

6 に答える 6

5

FWIW、私はこの同じ問題に遭遇し、いくつかの頭痛の種になりました。私の意見: 動作に一貫性がないため、その理由だけでもバグとして分類します。プロパティの読み取り時に例外を発生させるのはばかげており、プロパティの意図に沿っていないため、これもバグです (少なくとも驚くべき原則です)。(また、ある問題が長い間存在しているという事実は、それがバグであるかどうかを意味するものではありません。)

(編集:回避策を含む詳細情報で回答を更新します。QCレポートにも同じように投稿しました:http://qc.embarcadero.com/wc/qcmain.aspx?d=73852

また、datasnap/clientdatasets を多用するアプリでも同じ問題が発生しました。ただし、実際の問題は clientdataset にあるのではなく、SysUtils の Timestamp 検証ルーチンにあり、明らかに誤って 0.0 値の Timestamp を無効として検証します。

したがって、回避策/修正は SysUtils および検証ルーチンに対するものであり、Tclientdataset に対するものではありません。具体的には、検証ルーチン "ValidateTimeStamp()" で、時刻部分は < 0 で正しく比較されますが、日付部分は誤って <= 0 で比較されます。

その結果、(正当な) 0.0 Datetime 値が datepart = 0 の Timestamp に変換される場合があり、この値が再度逆検証されると (ここや QC レポートに示されているようにデータセット フィールドから値が読み取られる場合など)、例外が (誤って) 発生します。したがって、タイムスタンプの日付部分の検証を変更して厳密な「より小」を使用するという簡単な修正を行うだけで、TClientDataset によって明らかになる問題が修正されます。

Delphi 7.1に基づく回避策は次のとおりです

(* SysUtils.pas line 10934 (Delphi 7.1)  *)
(**)(* fix - Timestamp values 0.0 erroneously designated as invalid *)
(* D7.1 *)
(* Walter Prins, originally patched May 2005, submitted 4 June 2009 *)
procedure ValidateTimeStamp(const TimeStamp: TTimeStamp);
begin
  if (TimeStamp.Time < 0) or (TimeStamp.Date < 0) then (* Changed TimeStamp.Date <= 0 to TimeStamp.Date < 0 *)
    ConvertErrorFmt(@SInvalidTimeStamp, [TimeStamp.Date, TimeStamp.Time]);
end;
于 2009-05-27T10:42:46.010 に答える
2

Delphi の TDateTime は double であり、日付は整数部分に格納され、時刻は小数部分に格納されます。したがって、空の日付値を 0.0 に変換することは、ある程度正しいです。アクセスしている基になるフィールドは TDateField (または TDateTime フィールド) であるため、おそらく内部で変換を行っています。

さらに、Variant を Null に対してチェックすることは、Delphi では適切な方法ではなくなりました。Null バリアントはまだ割り当てられていますが、値は Null ですが、割り当てられていないバリアントには値がありません。(SQL データベースの NULL 値を考えてみてください)。代わりに、Variants.pas ユニットにある VarIsNull(const V: Variant) 関数を使用してください。バリアントが null の場合は true を返し、それ以外の値がある場合は false を返します。

于 2009-05-05T19:46:58.353 に答える
1

Debug DCUsオプションをアクティブにして以下のコードをデバッグしましたが、奇妙なことに、SysUtils.ValidateTimeStampはdate = 0のTimeStampを無効と評価し、EConvertError例外をスローします(NullまたはUnassignedを返す代わりに)。

したがって、最終的な結果として、dsInsert状態のnullフィールドに対してOldValueリクエストを実行すると、その無効になります。バリアントは決して返されないため、(field.OldValue <> Null)またはVarIsNull(field.OldValue)でテストする場合は関係ありません。例外は前にスローされます。

cds_somethingには2つのフィールドがあります(設計時に作成されます)。

  • dt_Something
  • num_something

コード:

var
  b: TClientDataset;
begin
  b := cds_somethin;
  b.Close;
  b.CreateDataSet;
  b.Insert;
  if b.FieldByName('DT_Sometinhg').OldValue <> Null then
    ShowMessage('Something wrong!!!')
  else
    ShowMessage('Normal');

  b.Cancel;

注:この投稿の元の編集を台無しにしました。これは今、私が見つけたものの正しい解釈です。

追加:他のいくつかのフィールドタイプ(文字列、BCD、フロート、メモ)でテストされ、OldValueは割り当てられていないため、上記のテストはfalseと評価されます。

TDateFieldとTDateTimeFieldのみがその動作を示しているように見えます。TTimeFieldとTSQLTimeStampは正常を評価しますが、TSQLTimeStampField.OldValueはNullまたはUnassigned(wtf !!)のどちらにも等しくありません。

スニペットが少し変更されました:

var
  b: TClientDataset;
begin
  b := cds_somethin;
  b.Close;
  b.CreateDataSet;
  b.Insert;
  /*
  if (b.FieldByName('DT_Something').OldValue <> Null) 
     and (b.FieldByName('DT_Something').OldValue <> Unassigned)  then
    ShowMessage('Something wrong!!!')
  else
    ShowMessage('Normal');
  */
  if (b.FieldByName('ts_Something').OldValue <> Null) 
     and (b.FieldByName('ts_Something').OldValue <> Unassigned)  then
    ShowMessage('Something wrong!!!')
  else
    ShowMessage('Normal');


  b.Cancel;

ここで、ts_SomethingはTSQLTimeStampFieldです。フィールドは設計時に作成されます。

于 2009-05-05T20:03:59.250 に答える
1

これは、レコード バッファに関連する Midas/TClientDataSet のバグです。AppendData を何度も実行すると、InternalCalc フィールドが「not null」として表示されます (レコード バッファ内の間違った null フラグ)。Delphi 2010 では、Midas.dll ソース (.cpp ファイル) を調べることができます。

http://img514.imageshack.us/img514/2840/wrongnull.jpg

ソリューションを実装する方法を研究しています。

アル・ゴンザレス。

于 2010-10-21T18:36:15.333 に答える
0

次のように、Delphi 2009 update 2 および Delphi 2007 で再現しました。

uses DB, DBClient;
procedure TTestForm1.TestButtonClick(Sender: TObject);
const
  SMyDateField = 'MyDateField';
  SMyIntegerField = 'MyIntegerField';
var
  MyClientDataSet: TClientDataSet;
  MyClientDataSetMyDateField: TField;
  MyClientDataSetMyIntegerField: TField;
  OldValue: Variant;
begin
  MyClientDataSet := TClientDataSet.Create(Self);
  MyClientDataSet.FieldDefs.Add(SMyDateField, ftDate);
  MyClientDataSet.FieldDefs.Add(SMyIntegerField, ftInteger);
  MyClientDataSet.CreateDataSet();
  MyClientDataSetMyDateField := MyClientDataSet.FieldByName(SMyDateField);
  MyClientDataSetMyIntegerField := MyClientDataSet.FieldByName(SMyIntegerField);
  MyClientDataSet.Insert();
  OldValue := MyClientDataSetMyIntegerField.OldValue;
  OldValue := MyClientDataSetMyDateField.OldValue;
end;

常に次のエラーが発生します。

exception class EConvertError with message ''0.0' is not a valid timestamp'.

これがバグとみなされるかどうかはわかりません:

  • 挿入時に技術的には OldValue がないため、取得すると例外が発生する可能性があります
  • MyClientDataSetMyIntegerField.OldValue は 0 を返しますが、MyClientDataSetMyDateField.OldValue は例外を発生させます

その他の注意事項:

  • TCustomClientDataSet.GetFieldData は実際の基になるデータを取得します
  • TDataSet.DataConvert は、基になるデータをネイティブ データ形式に変換し、必要に応じて有効性チェックを実行します。

編集: Fabricio のコメントの結果として、挿入後に OldValue が技術的に無効であることを強調しました。技術的には、これはバグではないかもしれません。

彼の「新しい証拠」は、VCL/RTL ソースを確認することで確認できます。

フィールド タイプが ftDate、ftTime、ftDateTime の場合、TDataSet.DataConvert は、TimeStamp を埋めるローカルの NativeToDateTime 関数を呼び出します。次に、SysUtils.TimeStampToDateTime を使用してそれを変換します。SysUtils.TimeStampToDateTime は、SysUtils.ValidateTimeStamp を呼び出します。この関数は、時刻部分が 0 未満の場合に例外を発生させます。日付部分がゼロ以下です。

Date 部分は、フィールド タイプ ftDate および ftDateTime (TDateField および TDateTimeField) の場合にのみゼロになる可能性があるため、それらのみが例外を発生させることができます。他のすべてのデータ型の場合、NativeToDateTime には問題はありません。これらの型はすべて、ゼロバイトで埋められた結果を許可します。

RTL/VCL の履歴を確認しました。Delphi 6 の SysUtils.TimeStampToDateTime は SysUtils.ValidateTimeStamp を呼び出すため、この動作は 2001 年から同じです。

そのため、これをバグと見なすことは非常に困難です。

于 2009-05-06T10:07:42.107 に答える
0

MySQLでdelphi 2007を使用していますが、私の解決策は次のとおりです。

私が使用したMySQLクエリでは:

select CAST(dateField AS CHAR) from table_name

... CAST を使用してフィールドを文字列値に変換し、値が0000-00-00... であるかどうかをテストできます。その後、フィールドの実際の値を使用します。

于 2012-08-21T19:00:04.620 に答える