Dbutils.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:24k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. {*******************************************************}
  2. {                                                       }
  3. {         Delphi VCL Extensions (RX)                    }
  4. {                                                       }
  5. {         Copyright (c) 1995, 1996 AO ROSNO             }
  6. {         Copyright (c) 1997, 1998 Master-Bank          }
  7. {                                                       }
  8. {*******************************************************}
  9. unit DBUtils;
  10. {$I RX.INC}
  11. {$W-,R-,B-,N+,P+}
  12. interface
  13. uses {$IFDEF WIN32} Windows, Registry, {$ELSE} WinTypes, WinProcs, {$ENDIF}
  14.   Classes, SysUtils, DB, {$IFNDEF RX_D3} DBTables, {$ENDIF} IniFiles;
  15. type
  16. { TLocateObject }
  17.   TLocateObject = class(TObject)
  18.   private
  19.     FDataSet: TDataSet;
  20.     FLookupField: TField;
  21.     FLookupValue: string;
  22.     FLookupExact, FCaseSensitive: Boolean;
  23.     FBookmark: TBookmark;
  24.     FIndexSwitch: Boolean;
  25.     procedure SetDataSet(Value: TDataSet);
  26.   protected
  27.     function MatchesLookup(Field: TField): Boolean;
  28.     procedure CheckFieldType(Field: TField); virtual;
  29.     procedure ActiveChanged; virtual;
  30.     function LocateFilter: Boolean; virtual;
  31.     function LocateKey: Boolean; virtual;
  32.     function LocateFull: Boolean; virtual;
  33.     function UseKey: Boolean; virtual;
  34.     function FilterApplicable: Boolean; virtual;
  35.     property LookupField: TField read FLookupField;
  36.     property LookupValue: string read FLookupValue;
  37.     property LookupExact: Boolean read FLookupExact;
  38.     property CaseSensitive: Boolean read FCaseSensitive;
  39.     property Bookmark: TBookmark read FBookmark write FBookmark;
  40.   public
  41.     function Locate(const KeyField, KeyValue: string; Exact,
  42.       CaseSensitive: Boolean): Boolean;
  43.     property DataSet: TDataSet read FDataSet write SetDataSet;
  44.     property IndexSwitch: Boolean read FIndexSwitch write FIndexSwitch;
  45.   end;
  46. type
  47.   TCreateLocateObject = function: TLocateObject;
  48. const
  49.   CreateLocateObject: TCreateLocateObject = nil;
  50. function CreateLocate(DataSet: TDataSet): TLocateObject;
  51. { Utility routines }
  52. function IsDataSetEmpty(DataSet: TDataSet): Boolean;
  53. procedure RefreshQuery(Query: TDataSet);
  54. function DataSetSortedSearch(DataSet: TDataSet; const Value,
  55.   FieldName: string; CaseInsensitive: Boolean): Boolean;
  56. function DataSetSectionName(DataSet: TDataSet): string;
  57. procedure InternalSaveFields(DataSet: TDataSet; IniFile: TObject;
  58.   const Section: string);
  59. procedure InternalRestoreFields(DataSet: TDataSet; IniFile: TObject;
  60.   const Section: string; RestoreVisible: Boolean);
  61. {$IFDEF WIN32}
  62. function DataSetLocateThrough(DataSet: TDataSet; const KeyFields: string;
  63.   const KeyValues: Variant; Options: TLocateOptions): Boolean;
  64. procedure SaveFieldsReg(DataSet: TDataSet; IniFile: TRegIniFile);
  65. procedure RestoreFieldsReg(DataSet: TDataSet; IniFile: TRegIniFile;
  66.   RestoreVisible: Boolean);
  67. {$ENDIF WIN32}
  68. procedure SaveFields(DataSet: TDataSet; IniFile: TIniFile);
  69. procedure RestoreFields(DataSet: TDataSet; IniFile: TIniFile;
  70.   RestoreVisible: Boolean);
  71. procedure AssignRecord(Source, Dest: TDataSet; ByName: Boolean);
  72. function ConfirmDelete: Boolean;
  73. procedure ConfirmDataSetCancel(DataSet: TDataSet);
  74. procedure CheckRequiredField(Field: TField);
  75. procedure CheckRequiredFields(const Fields: array of TField);
  76. { SQL expressions }
  77. function DateToSQL(Value: TDateTime): string;
  78. function FormatSQLDateRange(Date1, Date2: TDateTime;
  79.   const FieldName: string): string;
  80. function FormatSQLDateRangeEx(Date1, Date2: TDateTime;
  81.   const FieldName: string): string;
  82. function FormatSQLNumericRange(const FieldName: string;
  83.   LowValue, HighValue, LowEmpty, HighEmpty: Double; Inclusive: Boolean): string;
  84. function StrMaskSQL(const Value: string): string;
  85. function FormatSQLCondition(const FieldName, Operator, Value: string;
  86.   FieldType: TFieldType; Exact: Boolean): string;
  87. function FormatAnsiSQLCondition(const FieldName, Operator, Value: string;
  88.   FieldType: TFieldType; Exact: Boolean): string;
  89. const
  90.   TrueExpr = '0=0';
  91. const
  92.   { Server Date formats}
  93.   sdfStandard16 = '''"''mm''/''dd''/''yyyy''"'''; {"mm/dd/yyyy"}
  94.   sdfStandard32 = '''''''dd/mm/yyyy''''''';       {'dd/mm/yyyy'}
  95.   sdfOracle     = '"TO_DATE(''"dd/mm/yyyy"'', ''DD/MM/YYYY'')"';
  96.   sdfInterbase  = '"CAST(''"mm"/"dd"/"yyyy"'' AS DATE)"';
  97.   sdfMSSQL      = '"CONVERT(datetime, ''"mm"/"dd"/"yyyy"'', 103)"';
  98. const
  99.   ServerDateFmt: string[50] = sdfStandard16;
  100. {$IFNDEF WIN32}
  101. type
  102.   TBlobType = ftBlob..ftGraphic;
  103. {$ENDIF}
  104. const
  105. {$IFNDEF RX_D4}
  106.   {$IFDEF WIN32}
  107.   ftBlobTypes = [ftBlob..ftTypedBinary];
  108.   {$ELSE}
  109.   ftBlobTypes = [ftBlob..ftGraphic];
  110.   {$ENDIF}
  111. {$ELSE}
  112.   ftBlobTypes = [Low(TBlobType)..High(TBlobType)];
  113. {$ENDIF RX_D3}
  114. {$IFDEF RX_V110} {$NODEFINE ftBlobTypes} {$ENDIF}
  115. {$IFNDEF RX_D4}
  116.   ftNonTextTypes = [ftBytes, ftVarBytes, ftBlob, ftMemo, ftGraphic
  117.     {$IFDEF WIN32}, ftFmtMemo, ftParadoxOle, ftDBaseOle, ftTypedBinary
  118.     {$IFDEF RX_D3}, ftCursor {$ENDIF} {$ENDIF}];
  119.   {$IFDEF VER110} { C++ Builder 3 or higher }
  120.   {$NODEFINE ftNonTextTypes}
  121.   (*$HPPEMIT 'namespace Dbutils'*)
  122.   (*$HPPEMIT '{'*)
  123.   (*$HPPEMIT '#define ftNonTextTypes (System::Set<TFieldType, ftUnknown, ftCursor> () '*)
  124.   (*$HPPEMIT '        << ftBytes << ftVarBytes << ftBlob << ftMemo << ftGraphic '*)
  125.   (*$HPPEMIT '        << ftFmtMemo << ftParadoxOle << ftDBaseOle << ftTypedBinary << ftCursor )'*)
  126.   (*$HPPEMIT '}'*)
  127.   {$ENDIF}
  128. type
  129.   Largeint = Longint;
  130.   {$IFDEF VER110} {$NODEFINE Largeint} {$ENDIF}
  131. {$ENDIF RX_D4}
  132. {$IFDEF RX_D3}
  133. procedure _DBError(const Msg: string);
  134. {$ELSE}
  135. procedure _DBError(Ident: Word);
  136. {$ENDIF}
  137. implementation
  138. uses Forms, Controls, Dialogs, Consts, DBConsts, RXDConst, VCLUtils, FileUtil,
  139.   AppUtils, rxStrUtils, MaxMin, {$IFNDEF RX_D3} BdeUtils, {$ENDIF}
  140.   {$IFNDEF WIN32} Str16, {$ENDIF} DateUtil;
  141. { Utility routines }
  142. {$IFDEF RX_D3}
  143. procedure _DBError(const Msg: string);
  144. begin
  145.   DatabaseError(Msg);
  146. {$ELSE}
  147. procedure _DBError(Ident: Word);
  148. begin
  149.   DBError(Ident);
  150. {$ENDIF}
  151. end;
  152. function ConfirmDelete: Boolean;
  153. begin
  154.   Screen.Cursor := crDefault;
  155.   Result := MessageDlg(ResStr(SDeleteRecordQuestion), mtConfirmation,
  156.     [mbYes, mbNo], 0) = mrYes;
  157. end;
  158. procedure ConfirmDataSetCancel(DataSet: TDataSet);
  159. begin
  160.   if DataSet.State in [dsEdit, dsInsert] then begin
  161.     DataSet.UpdateRecord;
  162.     if DataSet.Modified then begin
  163.       case MessageDlg(LoadStr(SConfirmSave), mtConfirmation, mbYesNoCancel, 0) of
  164.         mrYes: DataSet.Post;
  165.         mrNo: DataSet.Cancel;
  166.         else SysUtils.Abort;
  167.       end;
  168.     end
  169.     else DataSet.Cancel;
  170.   end;
  171. end;
  172. {$IFDEF RX_D3}
  173. function SetToBookmark(ADataSet: TDataSet; ABookmark: TBookmark): Boolean;
  174. begin
  175.   Result := False;
  176.   with ADataSet do
  177.     if Active and (ABookmark <> nil) and not (Bof and Eof) and
  178.       BookmarkValid(ABookmark) then
  179.     try
  180.       ADataSet.GotoBookmark(ABookmark);
  181.       Result := True;
  182.     except
  183.     end;
  184. end;
  185. {$ENDIF}
  186. { Refresh Query procedure }
  187. procedure RefreshQuery(Query: TDataSet);
  188. var
  189.   BookMk: TBookmark;
  190. begin
  191.   with Query do begin
  192.     DisableControls;
  193.     try
  194.       if Active then BookMk := GetBookmark else BookMk := nil;
  195.       try
  196.         Close;
  197.         Open;
  198. {$IFDEF RX_D3}
  199.         SetToBookmark(Query, BookMk);
  200. {$ELSE}
  201.         if Query is TDBDataSet then SetToBookmark(Query, BookMk);
  202. {$ENDIF}
  203.       finally
  204.         if BookMk <> nil then FreeBookmark(BookMk);
  205.       end;
  206.     finally
  207.       EnableControls;
  208.     end;
  209.   end;
  210. end;
  211. { TLocateObject }
  212. procedure TLocateObject.SetDataSet(Value: TDataSet);
  213. begin
  214.   ActiveChanged;
  215.   FDataSet := Value;
  216. end;
  217. function TLocateObject.LocateFull: Boolean;
  218. begin
  219.   Result := False;
  220.   with DataSet do begin
  221.     First;
  222.     while not EOF do begin
  223.       if MatchesLookup(FLookupField) then begin
  224.         Result := True;
  225.         Break;
  226.       end;
  227.       Next;
  228.     end;
  229.   end;
  230. end;
  231. function TLocateObject.LocateKey: Boolean;
  232. begin
  233.   Result := False;
  234. end;
  235. function TLocateObject.FilterApplicable: Boolean;
  236. begin
  237. {$IFDEF RX_D3}
  238.   Result := FLookupField.FieldKind in [fkData, fkInternalCalc];
  239. {$ELSE}
  240.   Result := ({$IFDEF WIN32} FLookupField.FieldKind = fkData {$ELSE}
  241.     not FLookupField.Calculated {$ENDIF}) and IsFilterApplicable(DataSet);
  242. {$ENDIF}
  243. end;
  244. function TLocateObject.LocateFilter: Boolean;
  245. {$IFDEF WIN32}
  246. var
  247.   SaveCursor: TCursor;
  248.   Options: TLocateOptions;
  249.   Value: Variant;
  250. begin
  251.   SaveCursor := Screen.Cursor;
  252.   Screen.Cursor := crHourGlass;
  253.   try
  254.     Options := [];
  255.     if not FCaseSensitive then Include(Options, loCaseInsensitive);
  256.     if not FLookupExact then Include(Options, loPartialKey);
  257.     if (FLookupValue = '') then VarClear(Value)
  258.     else Value := FLookupValue;
  259.     Result := DataSet.Locate(FLookupField.FieldName, Value, Options);
  260.   finally
  261.     Screen.Cursor := SaveCursor;
  262.   end;
  263. {$ELSE}
  264. begin
  265.   Result := False;
  266. {$ENDIF}
  267. end;
  268. procedure TLocateObject.CheckFieldType(Field: TField);
  269. begin
  270. end;
  271. function TLocateObject.Locate(const KeyField, KeyValue: string;
  272.   Exact, CaseSensitive: Boolean): Boolean;
  273. var
  274.   LookupKey: TField;
  275. begin
  276.   if DataSet = nil then begin
  277.     Result := False;
  278.     Exit;
  279.   end;
  280.   DataSet.CheckBrowseMode;
  281.   LookupKey := DataSet.FieldByName(KeyField);
  282.   DataSet.CursorPosChanged;
  283.   FLookupField := LookupKey;
  284.   FLookupValue := KeyValue;
  285.   FLookupExact := Exact;
  286.   FCaseSensitive := CaseSensitive;
  287.   if FLookupField.DataType <> ftString then begin
  288.     FCaseSensitive := True;
  289.     try
  290.       CheckFieldType(FLookupField);
  291.     except
  292.       Result := False;
  293.       Exit;
  294.     end;
  295.   end;
  296.   FBookmark := DataSet.GetBookmark;
  297.   try
  298.     DataSet.DisableControls;
  299.     try
  300.       Result := MatchesLookup(FLookupField);
  301.       if not Result then begin
  302.         if UseKey then Result := LocateKey
  303.         else begin
  304.           if FilterApplicable then Result := LocateFilter
  305.           else Result := LocateFull;
  306.         end;
  307.         if not Result then SetToBookmark(DataSet, FBookmark);
  308.       end;
  309.     finally
  310.       DataSet.EnableControls;
  311.     end;
  312.   finally
  313.     FLookupValue := EmptyStr;
  314.     FLookupField := nil;
  315.     DataSet.FreeBookmark(FBookmark);
  316.     FBookmark := nil;
  317.   end;
  318. end;
  319. function TLocateObject.UseKey: Boolean;
  320. begin
  321.   Result := False;
  322. end;
  323. procedure TLocateObject.ActiveChanged;
  324. begin
  325. end;
  326. function TLocateObject.MatchesLookup(Field: TField): Boolean;
  327. var
  328.   Temp: string;
  329. begin
  330.   Temp := Field.AsString;
  331.   if not FLookupExact then
  332.     SetLength(Temp, Min(Length(FLookupValue), Length(Temp)));
  333.   if FCaseSensitive then Result := AnsiCompareStr(Temp, FLookupValue) = 0
  334.   else Result := AnsiCompareText(Temp, FLookupValue) = 0;
  335. end;
  336. function CreateLocate(DataSet: TDataSet): TLocateObject;
  337. begin
  338.   if Assigned(CreateLocateObject) then Result := CreateLocateObject
  339.   else Result := TLocateObject.Create;
  340.   if (Result <> nil) and (DataSet <> nil) then
  341.     Result.DataSet := DataSet;
  342. end;
  343. { DataSet locate routines }
  344. {$IFDEF WIN32}
  345. function DataSetLocateThrough(DataSet: TDataSet; const KeyFields: string;
  346.   const KeyValues: Variant; Options: TLocateOptions): Boolean;
  347. var
  348.   FieldCount: Integer;
  349.   Fields: TList;
  350.   Bookmark: TBookmarkStr;
  351.   function CompareField(Field: TField; Value: Variant): Boolean;
  352.   var
  353.     S: string;
  354.   begin
  355.     if Field.DataType = ftString then begin
  356.       S := Field.AsString;
  357.       if (loPartialKey in Options) then
  358.         Delete(S, Length(Value) + 1, MaxInt);
  359.       if (loCaseInsensitive in Options) then
  360.         Result := AnsiCompareText(S, Value) = 0
  361.       else
  362.         Result := AnsiCompareStr(S, Value) = 0;
  363.     end
  364.     else Result := (Field.Value = Value);
  365.   end;
  366.   function CompareRecord: Boolean;
  367.   var
  368.     I: Integer;
  369.   begin
  370.     if FieldCount = 1 then
  371.       Result := CompareField(TField(Fields.First), KeyValues)
  372.     else begin
  373.       Result := True;
  374.       for I := 0 to FieldCount - 1 do
  375.         Result := Result and CompareField(TField(Fields[I]), KeyValues[I]);
  376.     end;
  377.   end;
  378. begin
  379.   Result := False;
  380.   with DataSet do begin
  381.     CheckBrowseMode;
  382.     if BOF and EOF then Exit;
  383.   end;
  384.   Fields := TList.Create;
  385.   try
  386.     DataSet.GetFieldList(Fields, KeyFields);
  387.     FieldCount := Fields.Count;
  388.     Result := CompareRecord;
  389.     if Result then Exit;
  390.     DataSet.DisableControls;
  391.     try
  392.       Bookmark := DataSet.Bookmark;
  393.       try
  394.         with DataSet do begin
  395.           First;
  396.           while not EOF do begin
  397.             Result := CompareRecord;
  398.             if Result then Break;
  399.             Next;
  400.           end;
  401.         end;
  402.       finally
  403.         if not Result {$IFDEF RX_D3} and
  404.           DataSet.BookmarkValid(PChar(Bookmark)) {$ENDIF} then
  405.           DataSet.Bookmark := Bookmark;
  406.       end;
  407.     finally
  408.       DataSet.EnableControls;
  409.     end;
  410.   finally
  411.     Fields.Free;
  412.   end;
  413. end;
  414. {$ENDIF}
  415. { DataSetSortedSearch. Navigate on sorted DataSet routine. }
  416. function DataSetSortedSearch(DataSet: TDataSet; const Value,
  417.   FieldName: string; CaseInsensitive: Boolean): Boolean;
  418. var
  419.   L, H, I: Longint;
  420.   CurrentPos: Longint;
  421.   CurrentValue: string;
  422.   BookMk: TBookmark;
  423.   Field: TField;
  424.   function UpStr(const Value: string): string;
  425.   begin
  426.     if CaseInsensitive then Result := AnsiUpperCase(Value)
  427.     else Result := Value;
  428.   end;
  429.   function GetCurrentStr: string;
  430.   begin
  431.     Result := Field.AsString;
  432.     if Length(Result) > Length(Value) then
  433.       SetLength(Result, Length(Value));
  434.     Result := UpStr(Result);
  435.   end;
  436. begin
  437.   Result := False;
  438.   if DataSet = nil then Exit;
  439.   Field := DataSet.FindField(FieldName);
  440.   if Field = nil then Exit;
  441.   if Field.DataType = ftString then begin
  442.     DataSet.DisableControls;
  443.     BookMk := DataSet.GetBookmark;
  444.     try
  445.       L := 0;
  446.       DataSet.First;
  447.       CurrentPos := 0;
  448.       H := DataSet.RecordCount - 1;
  449.       if Value <> '' then begin
  450.         while L <= H do begin
  451.           I := (L + H) shr 1;
  452.           if I <> CurrentPos then DataSet.MoveBy(I - CurrentPos);
  453.           CurrentPos := I;
  454.           CurrentValue := GetCurrentStr;
  455.           if (UpStr(Value) > CurrentValue) then
  456.             L := I + 1
  457.           else begin
  458.             H := I - 1;
  459.             if (UpStr(Value) = CurrentValue) then Result := True;
  460.           end;
  461.         end; { while }
  462.         if Result then begin
  463.           if (L <> CurrentPos) then DataSet.MoveBy(L - CurrentPos);
  464.           while (L < DataSet.RecordCount) and
  465.             (UpStr(Value) <> GetCurrentStr) do
  466.           begin
  467.             Inc(L);
  468.             DataSet.MoveBy(1);
  469.           end;
  470.         end;
  471.       end
  472.       else Result := True;
  473.       if not Result then SetToBookmark(DataSet, BookMk);
  474.     finally
  475.       DataSet.FreeBookmark(BookMk);
  476.       DataSet.EnableControls;
  477.     end;
  478.   end
  479.   else
  480. {$IFDEF RX_D3}
  481.     DatabaseErrorFmt(SFieldTypeMismatch, [Field.DisplayName]);
  482. {$ELSE}
  483.     DBErrorFmt(SFieldTypeMismatch,
  484.       [Field.DisplayName{$IFNDEF WIN32}^{$ENDIF}]);
  485. {$ENDIF}
  486. end;
  487. { Save and restore DataSet Fields layout }
  488. function DataSetSectionName(DataSet: TDataSet): string;
  489. begin
  490.   with DataSet do
  491.     if (Owner <> nil) and (Owner is TCustomForm) then
  492.       Result := GetDefaultSection(Owner as TCustomForm)
  493.     else Result := Name;
  494. end;
  495. function CheckSection(DataSet: TDataSet; const Section: string): string;
  496. begin
  497.   Result := Section;
  498.   if Result = '' then Result := DataSetSectionName(DataSet);
  499. end;
  500. procedure InternalSaveFields(DataSet: TDataSet; IniFile: TObject;
  501.   const Section: string);
  502. var
  503.   I: Integer;
  504. begin
  505.   with DataSet do begin
  506.     for I := 0 to FieldCount - 1 do begin
  507.       IniWriteString(IniFile, CheckSection(DataSet, Section),
  508.         Name + Fields[I].FieldName,
  509.         Format('%d,%d,%d', [Fields[I].Index, Fields[I].DisplayWidth,
  510.         Integer(Fields[I].Visible)]));
  511.     end;
  512.   end;
  513. end;
  514. procedure InternalRestoreFields(DataSet: TDataSet; IniFile: TObject;
  515.   const Section: string; RestoreVisible: Boolean);
  516. type
  517.   TFieldInfo = record
  518.     Field: TField;
  519.     EndIndex: Integer;
  520.   end;
  521.   PFieldArray = ^TFieldArray;
  522.   TFieldArray = array[0..(65528 div SizeOf(TFieldInfo)) - 1] of TFieldInfo;
  523. const
  524.   Delims = [' ',','];
  525. var
  526.   I, J: Integer;
  527.   S: string;
  528.   FieldArray: PFieldArray;
  529. begin
  530.   with DataSet do begin
  531.     FieldArray := AllocMemo(FieldCount * SizeOf(TFieldInfo));
  532.     try
  533.       for I := 0 to FieldCount - 1 do begin
  534.         S := IniReadString(IniFile, CheckSection(DataSet, Section),
  535.           Name + Fields[I].FieldName, '');
  536.         FieldArray^[I].Field := Fields[I];
  537.         FieldArray^[I].EndIndex := Fields[I].Index;
  538.         if S <> '' then begin
  539.           FieldArray^[I].EndIndex := StrToIntDef(ExtractWord(1, S, Delims),
  540.             FieldArray^[I].EndIndex);
  541.           Fields[I].DisplayWidth := StrToIntDef(ExtractWord(2, S, Delims),
  542.             Fields[I].DisplayWidth);
  543.           if RestoreVisible then
  544.             Fields[I].Visible := Boolean(StrToIntDef(ExtractWord(3, S, Delims),
  545.               Integer(Fields[I].Visible)));
  546.         end;
  547.       end;
  548.       for I := 0 to FieldCount - 1 do begin
  549.         for J := 0 to FieldCount - 1 do begin
  550.           if FieldArray^[J].EndIndex = I then begin
  551.             FieldArray^[J].Field.Index := FieldArray^[J].EndIndex;
  552.             Break;
  553.           end;
  554.         end;
  555.       end;
  556.     finally
  557.       FreeMemo(Pointer(FieldArray));
  558.     end;
  559.   end;
  560. end;
  561. {$IFDEF WIN32}
  562. procedure SaveFieldsReg(DataSet: TDataSet; IniFile: TRegIniFile);
  563. begin
  564.   InternalSaveFields(DataSet, IniFile, DataSetSectionName(DataSet));
  565. end;
  566. procedure RestoreFieldsReg(DataSet: TDataSet; IniFile: TRegIniFile;
  567.   RestoreVisible: Boolean);
  568. begin
  569.   InternalRestoreFields(DataSet, IniFile, DataSetSectionName(DataSet),
  570.     RestoreVisible);
  571. end;
  572. {$ENDIF WIN32}
  573. procedure SaveFields(DataSet: TDataSet; IniFile: TIniFile);
  574. begin
  575.   InternalSaveFields(DataSet, IniFile, DataSetSectionName(DataSet));
  576. end;
  577. procedure RestoreFields(DataSet: TDataSet; IniFile: TIniFile;
  578.   RestoreVisible: Boolean);
  579. begin
  580.   InternalRestoreFields(DataSet, IniFile, DataSetSectionName(DataSet),
  581.     RestoreVisible);
  582. end;
  583. function IsDataSetEmpty(DataSet: TDataSet): Boolean;
  584. begin
  585.   with DataSet do Result := (not Active) or (Eof and Bof);
  586. end;
  587. { SQL expressions }
  588. function DateToSQL(Value: TDateTime): string;
  589. begin
  590.   Result := IntToStr(Trunc(Value));
  591. end;
  592. function FormatSQLDateRange(Date1, Date2: TDateTime;
  593.   const FieldName: string): string;
  594. begin
  595.   Result := TrueExpr;
  596.   if (Date1 = Date2) and (Date1 <> NullDate) then begin
  597.     Result := Format('%s = %s', [FieldName, FormatDateTime(ServerDateFmt,
  598.       Date1)]);
  599.   end
  600.   else if (Date1 <> NullDate) or (Date2 <> NullDate) then begin
  601.     if Date1 = NullDate then
  602.       Result := Format('%s < %s', [FieldName,
  603.         FormatDateTime(ServerDateFmt, IncDay(Date2, 1))])
  604.     else if Date2 = NullDate then
  605.       Result := Format('%s > %s', [FieldName,
  606.         FormatDateTime(ServerDateFmt, IncDay(Date1, -1))])
  607.     else
  608.       Result := Format('(%s < %s) AND (%s > %s)',
  609.         [FieldName, FormatDateTime(ServerDateFmt, IncDay(Date2, 1)),
  610.         FieldName, FormatDateTime(ServerDateFmt, IncDay(Date1, -1))]);
  611.   end;
  612. end;
  613. function FormatSQLDateRangeEx(Date1, Date2: TDateTime;
  614.   const FieldName: string): string;
  615. begin
  616.   Result := TrueExpr;
  617.   if (Date1 <> NullDate) or (Date2 <> NullDate) then begin
  618.     if Date1 = NullDate then
  619.       Result := Format('%s < %s', [FieldName,
  620.         FormatDateTime(ServerDateFmt, IncDay(Date2, 1))])
  621.     else if Date2 = NullDate then
  622.       Result := Format('%s >= %s', [FieldName,
  623.         FormatDateTime(ServerDateFmt, Date1)])
  624.     else
  625.       Result := Format('(%s < %s) AND (%s >= %s)',
  626.         [FieldName, FormatDateTime(ServerDateFmt, IncDay(Date2, 1)),
  627.         FieldName, FormatDateTime(ServerDateFmt, Date1)]);
  628.   end;
  629. end;
  630. function FormatSQLNumericRange(const FieldName: string;
  631.   LowValue, HighValue, LowEmpty, HighEmpty: Double; Inclusive: Boolean): string;
  632. const
  633.   Operators: array[Boolean, 1..2] of string[2] = (('>', '<'), ('>=', '<='));
  634. begin
  635.   Result := TrueExpr;
  636.   if (LowValue = HighValue) and (LowValue <> LowEmpty) then begin
  637.     Result := Format('%s = %g', [FieldName, LowValue]);
  638.   end
  639.   else if (LowValue <> LowEmpty) or (HighValue <> HighEmpty) then begin
  640.     if LowValue = LowEmpty then
  641.       Result := Format('%s %s %g', [FieldName, Operators[Inclusive, 2], HighValue])
  642.     else if HighValue = HighEmpty then
  643.       Result := Format('%s %s %g', [FieldName, Operators[Inclusive, 1], LowValue])
  644.     else begin
  645.       Result := Format('(%s %s %g) AND (%s %s %g)',
  646.         [FieldName, Operators[Inclusive, 2], HighValue,
  647.         FieldName, Operators[Inclusive, 1], LowValue]);
  648.     end;
  649.   end;
  650. end;
  651. function StrMaskSQL(const Value: string): string;
  652. begin
  653.   if (Pos('*', Value) = 0) and (Pos('?', Value) = 0) and (Value <> '') then
  654.     Result := '*' + Value + '*'
  655.   else Result := Value;
  656. end;
  657. function FormatSQLCondition(const FieldName, Operator, Value: string;
  658.   FieldType: TFieldType; Exact: Boolean): string;
  659. var
  660.   EmptyValue: Boolean;
  661.   FieldValue: string;
  662.   DateValue: TDateTime;
  663.   LogicOperator: string;
  664. begin
  665.   FieldValue := '';
  666.   DateValue := NullDate;
  667.   Exact := Exact or not (FieldType in
  668.     [ftString, ftDate, ftTime, ftDateTime]);
  669.   if FieldType in [ftDate, ftTime, ftDateTime] then begin
  670.     DateValue := StrToDateDef(Value, NullDate);
  671.     EmptyValue := (DateValue = NullDate);
  672.     FieldValue := FormatDateTime(ServerDateFmt, DateValue);
  673.   end
  674.   else begin
  675.     FieldValue := Value;
  676.     EmptyValue := FieldValue = '';
  677.     if not (Exact or EmptyValue) then
  678.       FieldValue := ReplaceStr(ReplaceStr(StrMaskSQL(FieldValue),
  679.         '*', '%'), '?', '_');
  680.     if FieldType = ftString then FieldValue := '''' + FieldValue + '''';
  681.   end;
  682.   LogicOperator := Operator;
  683.   if LogicOperator = '' then begin
  684.     if Exact then LogicOperator := '='
  685.     else begin
  686.       if FieldType = ftString then LogicOperator := 'LIKE'
  687.       else LogicOperator := '>=';
  688.     end;
  689.   end;
  690.   if EmptyValue then Result := TrueExpr
  691.   else if (FieldType = ftDateTime) and Exact then begin
  692.     DateValue := IncDay(DateValue, 1);
  693.     Result := Format('(%s >= %s) and (%s < %s)', [FieldName, FieldValue,
  694.       FieldName, FormatDateTime(ServerDateFmt, DateValue)]);
  695.   end
  696.   else Result := Format('%s %s %s', [FieldName, LogicOperator, FieldValue]);
  697. end;
  698. function FormatAnsiSQLCondition(const FieldName, Operator, Value: string;
  699.   FieldType: TFieldType; Exact: Boolean): string;
  700. var
  701.   S, Esc: string;
  702. begin
  703.   Esc := '';
  704.   if not Exact and (FieldType = ftString) then begin
  705.     S := ReplaceStr(ReplaceStr(ReplaceStr(Value, '/', '//'),
  706.       '_', '/_'), '%', '/%');
  707.     if S <> Value then Esc := ' ESCAPE''/''';
  708.   end
  709.   else S := Value;
  710.   Result := FormatSQLCondition(FieldName, Operator, S, FieldType, Exact) + Esc;
  711. end;
  712. procedure CheckRequiredField(Field: TField);
  713. begin
  714.   with Field do
  715.     if not ReadOnly and not Calculated and IsNull then begin
  716.       FocusControl;
  717. {$IFDEF WIN32}
  718.   {$IFNDEF RX_D3}
  719.       DBErrorFmt(SFieldRequired, [DisplayName]);
  720.   {$ELSE}
  721.       DatabaseErrorFmt(SFieldRequired, [DisplayName]);
  722.   {$ENDIF}
  723. {$ELSE}
  724.       DBErrorFmt(SFieldRequired, [DisplayName^]);
  725. {$ENDIF WIN32}
  726.     end;
  727. end;
  728. procedure CheckRequiredFields(const Fields: array of TField);
  729. var
  730.   I: Integer;
  731. begin
  732.   for I := Low(Fields) to High(Fields) do
  733.     CheckRequiredField(Fields[I]);
  734. end;
  735. procedure AssignRecord(Source, Dest: TDataSet; ByName: Boolean);
  736. var
  737.   I: Integer;
  738.   F, FSrc: TField;
  739. begin
  740.   if not (Dest.State in dsEditModes) then _DBError(SNotEditing);
  741.   if ByName then begin
  742.     for I := 0 to Source.FieldCount - 1 do begin
  743.       F := Dest.FindField(Source.Fields[I].FieldName);
  744.       if F <> nil then begin
  745. {$IFDEF WIN32}
  746.         F.Value := Source.Fields[I].Value;
  747. {$ELSE}
  748.         if (F.DataType = Source.Fields[I].DataType) and
  749.           (F.DataSize = Source.Fields[I].DataSize) then
  750.           F.Assign(Source.Fields[I])
  751.         else F.AsString := Source.Fields[I].AsString;
  752. {$ENDIF}
  753.       end;
  754.     end;
  755.   end
  756.   else begin
  757.     for I := 0 to Min(Source.FieldDefs.Count - 1, Dest.FieldDefs.Count - 1) do
  758.     begin
  759.       F := Dest.FindField(Dest.FieldDefs[I].Name);
  760.       FSrc := Source.FindField(Source.FieldDefs[I].Name);
  761.       if (F <> nil) and (FSrc <> nil) then begin
  762. {$IFDEF WIN32}
  763.         F.Value := FSrc.Value;
  764. {$ELSE}
  765.         if F.DataType = FSrc.DataType then F.Assign(FSrc)
  766.         else F.AsString := FSrc.AsString;
  767. {$ENDIF}
  768.       end;
  769.     end;
  770.   end;
  771. end;
  772. end.