소스 검색

Merged revisions 10845,10848,10858,10860,10862-10864,10875,10882,10884,10891,10905,10907,10909,10915-10916,10922-10923,10926,10928,10930,10933-10935,10939,10942,10948,10952-10956,10960,10964-10965,10968-10969 via svnmerge from
svn+ssh://[email protected]/FPC/svn/fpc/trunk

........
r10845 | joost | 2008-04-30 20:34:03 +0200 (Wed, 30 Apr 2008) | 5 lines

* Removed unused BlobsCached function
* Implemented GetSchemaInfoSQL
* Add support for pragma-statements
* Map empty field-types to ftString types
* Implemented BLOB-support
........
r10884 | joost | 2008-05-04 23:11:25 +0200 (Sun, 04 May 2008) | 4 lines

* Removed unused method StringQuery
* Fix for Execscallback
* Set ConnOptions
* Set FPrepared flag on prepare and call sqlite3_reset before execute
........
r10905 | joost | 2008-05-07 22:53:09 +0200 (Wed, 07 May 2008) | 1 line

* Fixed bug #8442 + test
........
r10926 | joost | 2008-05-09 23:40:18 +0200 (Fri, 09 May 2008) | 1 line

* Restructured the ApplyRecUpdates mechanism. Update-queries are only created when necessary and improved error messages. Readonly does not depend on ParseSQL anymore (mantis 9254) + test
........
r10934 | joost | 2008-05-10 23:29:35 +0200 (Sat, 10 May 2008) | 1 line

* Disable controls while applying updates (bug 9380)
........
r10968 | joost | 2008-05-14 16:29:27 +0200 (Wed, 14 May 2008) | 1 line

* Fixed test. Behaviour is the same as Delphi.
........
r10969 | joost | 2008-05-14 16:44:08 +0200 (Wed, 14 May 2008) | 1 line

* Fixed test. Behaviour is the same as Delphi.
........

git-svn-id: branches/fixes_2_2@11109 -

joost 17 년 전
부모
커밋
ba04c53f53

+ 2 - 0
packages/fcl-db/src/base/bufdataset.pas

@@ -1553,6 +1553,7 @@ begin
   r := 0;
   FailedCount := 0;
   Response := rrApply;
+  DisableControls;
   try
     while (r < Length(FUpdateBuffer)) and (Response <> rrAbort) do
       begin
@@ -1618,6 +1619,7 @@ begin
 
     FCurrentRecBuf := StoreRecBuf;
     Resync([]);
+    EnableControls;
   end;
 {$ENDIF}
 end;

+ 2 - 2
packages/fcl-db/src/base/dbconst.pas

@@ -88,8 +88,8 @@ Resourcestring
   SFieldIsNull             = 'The field is null';
   SOnUpdateError           = 'An error occured while applying the updates in a record: %s';
   SApplyRecNotSupported    = 'Applying updates is not supported by this TDataset descendent';
-  SNoWhereFields           = 'There are no fields found to generate the where-clause';
-  SNoUpdateFields          = 'There are no fields found to include in the update- or insert-clause';
+  SNoWhereFields           = 'No %s query specified and failed to generate one. (No fields for inclusion in where statement found)';
+  SNoUpdateFields          = 'No %s query specified and failed to generate one. (No fields found for insert- or update-statement found)';
   SNotSupported            = 'Operation is not supported by this type of database';
   SDBCreateDropFailed      = 'Creation or dropping of database failed';
   SMaxIndexes              = 'The maximum amount of indexes is reached';

+ 57 - 46
packages/fcl-db/src/sqldb/sqldb.pp

@@ -293,9 +293,9 @@ type
     property Transaction;
     property ReadOnly : Boolean read FReadOnly write SetReadOnly;
     property SQL : TStringlist read FSQL write FSQL;
-    property UpdateSQL : TStringlist read FUpdateSQL write FUpdateSQL;
-    property InsertSQL : TStringlist read FInsertSQL write FInsertSQL;
-    property DeleteSQL : TStringlist read FDeleteSQL write FDeleteSQL;
+    property UpdateSQL : TStringlist read FUpdateSQL;
+    property InsertSQL : TStringlist read FInsertSQL;
+    property DeleteSQL : TStringlist read FDeleteSQL;
     property Params : TParams read FParams write FParams;
     property UpdateMode : TUpdateMode read FUpdateMode write SetUpdateMode;
     property UsePrimaryKeyAsKey : boolean read FUsePrimaryKeyAsKey write SetUsePrimaryKeyAsKey;
@@ -1134,6 +1134,11 @@ begin
                          FWhereStopPos := PhraseP-PSQL+1
                        else
                          FWhereStopPos := CurrentP-PSQL+1;
+                       end
+                     else if (s = 'UNION') then
+                       begin
+                       ParsePart := ppBogus;
+                       FUpdateable := False;
                        end;
                      end;
         end; {case}
@@ -1152,20 +1157,6 @@ end;
 
 procedure TCustomSQLQuery.InternalOpen;
 
-  procedure InitialiseModifyQuery(var qry : TCustomSQLQuery; aSQL: TSTringList);
-  
-  begin
-    qry := TCustomSQLQuery.Create(nil);
-    with qry do
-      begin
-      ParseSQL := False;
-      DataBase := Self.DataBase;
-      Transaction := Self.Transaction;
-      SQL.Assign(aSQL);
-      end;
-  end;
-
-
 var tel, fieldc : integer;
     f           : TField;
     s           : string;
@@ -1208,12 +1199,11 @@ begin
         end
       else
         BindFields(True);
-      if FUpdateable then
+      if not ReadOnly and not FUpdateable then
         begin
-        InitialiseModifyQuery(FDeleteQry,FDeleteSQL);
-        InitialiseModifyQuery(FUpdateQry,FUpdateSQL);
-        InitialiseModifyQuery(FInsertQry,FInsertSQL);
-        end;
+        if (trim(FDeleteSQL.Text) <> '') or (trim(FUpdateSQL.Text) <> '') or
+           (trim(FInsertSQL.Text) <> '') then FUpdateable := True;
+        end
       end
     else
       DatabaseError(SErrNoSelectStatement,Self);
@@ -1285,12 +1275,7 @@ procedure TCustomSQLQuery.SetReadOnly(AValue : Boolean);
 
 begin
   CheckInactive;
-  if not AValue then
-    begin
-    if FParseSQL then FReadOnly := False
-      else DatabaseErrorFmt(SNoParseSQL,['Updating ']);
-    end
-  else FReadOnly := True;
+  FReadOnly:=AValue;
 end;
 
 procedure TCustomSQLQuery.SetParseSQL(AValue : Boolean);
@@ -1299,7 +1284,6 @@ begin
   CheckInactive;
   if not AValue then
     begin
-    FReadOnly := True;
     FServerFiltered := False;
     FParseSQL := False;
     end
@@ -1330,6 +1314,19 @@ Procedure TCustomSQLQuery.ApplyRecUpdate(UpdateKind : TUpdateKind);
 
 var FieldNamesQuoteChar : char;
 
+  procedure InitialiseModifyQuery(var qry : TCustomSQLQuery; aSQL: String);
+
+  begin
+    qry := TCustomSQLQuery.Create(nil);
+    with qry do
+      begin
+      ParseSQL := False;
+      DataBase := Self.DataBase;
+      Transaction := Self.Transaction;
+      SQL.text := aSQL;
+      end;
+  end;
+
   procedure UpdateWherePart(var sql_where : string;x : integer);
 
   begin
@@ -1356,9 +1353,9 @@ var FieldNamesQuoteChar : char;
         sql_set := sql_set +FieldNamesQuoteChar + fields[x].FieldName + FieldNamesQuoteChar +'=:' + fields[x].FieldName + ',';
       end;
 
-    if length(sql_set) = 0 then DatabaseError(sNoUpdateFields,self);
+    if length(sql_set) = 0 then DatabaseErrorFmt(sNoUpdateFields,['update'],self);
     setlength(sql_set,length(sql_set)-1);
-    if length(sql_where) = 0 then DatabaseError(sNoWhereFields,self);
+    if length(sql_where) = 0 then DatabaseErrorFmt(sNoWhereFields,['update'],self);
     setlength(sql_where,length(sql_where)-5);
     result := 'update ' + FTableName + ' set ' + sql_set + ' where ' + sql_where;
 
@@ -1381,7 +1378,7 @@ var FieldNamesQuoteChar : char;
         sql_values := sql_values + ':' + fields[x].FieldName + ',';
         end;
       end;
-    if length(sql_fields) = 0 then DatabaseError(sNoUpdateFields,self);
+    if length(sql_fields) = 0 then DatabaseErrorFmt(sNoUpdateFields,['insert'],self);
     setlength(sql_fields,length(sql_fields)-1);
     setlength(sql_values,length(sql_values)-1);
 
@@ -1398,7 +1395,7 @@ var FieldNamesQuoteChar : char;
     for x := 0 to Fields.Count -1 do
       UpdateWherePart(sql_where,x);
 
-    if length(sql_where) = 0 then DatabaseError(sNoWhereFields,self);
+    if length(sql_where) = 0 then DatabaseErrorFmt(sNoWhereFields,['delete'],self);
     setlength(sql_where,length(sql_where)-5);
 
     result := 'delete from ' + FTableName + ' where ' + sql_where;
@@ -1413,20 +1410,34 @@ begin
     FieldNamesQuoteChar := '"'
   else
     FieldNamesQuoteChar := ' ';
-    case UpdateKind of
-      ukModify : begin
-                 qry := FUpdateQry;
-                 if trim(qry.sql.Text) = '' then qry.SQL.Add(ModifyRecQuery);
-                 end;
-      ukInsert : begin
-                 qry := FInsertQry;
-                 if trim(qry.sql.Text) = '' then qry.SQL.Add(InsertRecQuery);
-                 end;
-      ukDelete : begin
-                 qry := FDeleteQry;
-                 if trim(qry.sql.Text) = '' then qry.SQL.Add(DeleteRecQuery);
+
+  case UpdateKind of
+    ukModify : begin
+               if not assigned(FUpdateQry) then
+                 begin
+                 if (trim(FUpdateSQL.Text)<> '') then
+                   InitialiseModifyQuery(FUpdateQry,FUpdateSQL.Text)
+                 else
+                   InitialiseModifyQuery(FUpdateQry,ModifyRecQuery);
                  end;
-    end;
+               qry := FUpdateQry;
+               end;
+    ukInsert : begin
+               if not assigned(FInsertQry) and (trim(FInsertSQL.Text)<> '') then
+                 InitialiseModifyQuery(FInsertQry,FInsertSQL.Text)
+               else
+                 InitialiseModifyQuery(FInsertQry,InsertRecQuery);
+               qry := FInsertQry;
+               end;
+    ukDelete : begin
+               if not assigned(FDeleteQry) and (trim(FDeleteSQL.Text)<> '') then
+                 InitialiseModifyQuery(FDeleteQry,FDeleteSQL.Text)
+               else
+                 InitialiseModifyQuery(FDeleteQry,DeleteRecQuery);
+               qry := FDeleteQry;
+               end;
+  end;
+  assert(qry.sql.Text<>'');
   with qry do
     begin
     for x := 0 to Params.Count-1 do with params[x] do if leftstr(name,4)='OLD_' then

+ 64 - 48
packages/fcl-db/src/sqldb/sqlite/sqlite3conn.pp

@@ -48,10 +48,8 @@ type
   private
     fhandle: psqlite3;
     foptions: TSQLiteOptions;
-    function blobscached: boolean;
     procedure setoptions(const avalue: tsqliteoptions);
   protected
-    function stringquery(const asql: string): TStringArray;
     function stringsquery(const asql: string): TArrayStringArray;
     procedure checkerror(const aerror: integer);
     
@@ -83,11 +81,15 @@ type
     procedure LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction); override;
     // New methods
     procedure execsql(const asql: string);
-    procedure UpdateIndexDefs(var IndexDefs : TIndexDefs; const TableName : string); // Differs from SQLDB.
-    function  getprimarykeyfield(const atablename: string; const acursor: tsqlcursor): string; 
+    procedure UpdateIndexDefs(IndexDefs : TIndexDefs;TableName : string); override; // Differs from SQLDB.
+    function getprimarykeyfield(const atablename: string; const acursor: tsqlcursor): string;
     function RowsAffected(cursor: TSQLCursor): TRowsCount; override;
+    function GetSchemaInfoSQL(SchemaType : TSchemaType; SchemaObjectName, SchemaPattern : string) : string; override;
+    function StrToStatementType(s : string) : TStatementType; override;
   public
-    function GetInsertID: int64; 
+    constructor Create(AOwner : TComponent); override;
+    function GetInsertID: int64;
+    procedure GetFieldNames(const TableName : string; List :  TStrings); override;
   published
     property Options: TSqliteOptions read FOptions write SetOptions;
   end;
@@ -200,12 +202,14 @@ begin
   if assigned(aparams) and (aparams.count > 0) then 
     buf := aparams.parsesql(buf,false,false,false,psinterbase,fparambinding);
   checkerror(sqlite3_prepare(fhandle,pchar(buf),length(buf),@fstatement,@ftail));
+  FPrepared:=True;
 end;
 
 Procedure TSQLite3Cursor.UnPrepare;
 
 begin
   sqlite3_finalize(fstatement); // No check.
+  FPrepared:=False;
 end;
 
 Procedure TSQLite3Cursor.Execute;
@@ -224,8 +228,8 @@ begin
   finally  
     set8087cw(wo1);                    //restore
   end;
-{$endif}  
-  if (fstate<=sqliteerrormax) then 
+{$endif}
+  if (fstate<=sqliteerrormax) then
     checkerror(sqlite3_reset(fstatement));
   RowsAffected:=sqlite3_changes(fhandle);
   if (fstate=sqlite_row) then 
@@ -251,25 +255,20 @@ end;
 procedure TSQLite3Connection.LoadBlobIntoBuffer(FieldDef: TFieldDef;ABlobBuf: PBufBlobField; cursor: TSQLCursor; ATransaction : TSQLTransaction); 
 
 var
- blobid: integer;
- int1,int2: integer;
- str1: string;
- bo1: boolean;
-begin
-{$WARNING TSQLite3Connection.LoadBlobIntoBuffer not implemented !}
-{ if (mode = bmwrite) and (field.dataset is tmsesqlquery) then begin
-  result:= tmsebufdataset(field.dataset).createblobbuffer(field);
- end
- else begin
-  result:= nil;
-  if mode = bmread then begin
-   if field.getData(@blobId) then begin
-    result:= acursor.getcachedblob(blobid);
-   end;
-  end;
- end;
- }
- 
+ int1: integer;
+ st: psqlite3_stmt;
+ fnum: integer;
+
+begin
+  st:=TSQLite3Cursor(cursor).fstatement;
+  fnum:= FieldDef.fieldno - 1;
+
+  int1:= sqlite3_column_bytes(st,fnum);
+
+  ReAllocMem(ABlobBuf^.BlobBuffer^.Buffer,int1);
+  if int1 > 0 then
+    move(sqlite3_column_text(st,fnum)^,ABlobBuf^.BlobBuffer^.Buffer^,int1);
+  ABlobBuf^.BlobBuffer^.Size := int1;
 end;
 
 function TSQLite3Connection.AllocateTransactionHandle: TSQLHandle;
@@ -334,7 +333,7 @@ Const
    (n:'DECIMAL'; t: ftBCD),
    (n:'TEXT'; t: ftmemo),
    (n:'BLOB'; t: ftBlob)
-{ Template:   
+{ Template:
   (n:''; t: ft)
 }
   );
@@ -363,6 +362,9 @@ begin
       ft1:=FieldMap[fi].t;
       break;
       end;
+    // Empty field types are allowed and used in calculated columns (aggregates)
+    // and by pragma-statements
+    if FD='' then ft1 := ftString;
     // handle some specials.
     size1:=0;
     case ft1 of
@@ -399,6 +401,7 @@ var
             
 begin
   SC:=TSQLite3Cursor(cursor);
+  checkerror(sqlite3_reset(sc.fstatement));
   If (AParams<>Nil) and (AParams.count > 0) then
     SC.BindParams(AParams);
   SC.Execute;    
@@ -515,13 +518,7 @@ begin
                  move(sqlite3_column_text(st,fnum)^,buffer^,int1);
               end;
     ftMemo,
-    ftBlob: begin
-            CreateBlob:=True;
-            int2:= sqlite3_column_bytes(st,fnum);
-            {$WARNING Blob data not handled correctly }
-            // int1:= addblobdata(sqlite3_column_text(st,fnum),int2);
-            move(int1,buffer^,sizeof(int1)); //save id
-            end;
+    ftBlob: CreateBlob:=True;
   else { Case }
    result:= false; // unknown
   end; { Case }
@@ -631,11 +628,6 @@ begin
    databaseerror(str1);
 end;
 
-function TSQLite3Connection.blobscached: boolean;
-begin
-  result:= true;
-end;
-
 function execcallback(adata: pointer; ncols: longint; //adata = PStringArray
                 avalues: PPchar; anames: PPchar):longint; cdecl;
 var
@@ -650,12 +642,6 @@ begin
   result:= 0;
 end;
 
-function TSQLite3Connection.stringquery(const asql: string): TStringArray;
-begin
-  SetLength(result,0);
-  CheckError(sqlite3_exec(fhandle,pchar(asql),@execcallback,@result,nil));
-end;
-
 function execscallback(adata: pointer; ncols: longint; //adata = PArrayStringArray
                 avalues: PPchar; anames: PPchar):longint; cdecl;
 var
@@ -667,7 +653,7 @@ begin
  PP:=PArrayStringArray(adata);
  N:=high(PP^); // Length-1;
  setlength(PP^,N+2); // increase with 1;
- p:= @(PP^[N]); // newly added array, fill with data.
+ p:= @(PP^[N+1]); // newly added array, fill with data.
  setlength(p^,ncols); 
  for i:= 0 to ncols - 1 do 
    p^[i]:= strPas(avalues[i]);
@@ -711,8 +697,32 @@ begin
     Result := -1;
 end;
 
-procedure TSQLite3Connection.UpdateIndexDefs(var IndexDefs: TIndexDefs;
-                              const TableName: string);
+function TSQLite3Connection.GetSchemaInfoSQL(SchemaType: TSchemaType;
+  SchemaObjectName, SchemaPattern: string): string;
+  
+begin
+  case SchemaType of
+    stTables     : result := 'select name as table_name from sqlite_master where type = ''table''';
+    stColumns    : result := 'pragma table_info(''' + (SchemaObjectName) + ''')';
+  else
+    DatabaseError(SMetadataUnavailable)
+  end; {case}
+end;
+
+function TSQLite3Connection.StrToStatementType(s: string): TStatementType;
+begin
+  S:=Lowercase(s);
+  if s = 'pragma' then exit(stSelect);
+  result := inherited StrToStatementType(s);
+end;
+
+constructor TSQLite3Connection.Create(AOwner: TComponent);
+begin
+  inherited Create(AOwner);
+  FConnOptions := FConnOptions + [sqEscapeRepeat] + [sqEscapeSlash] + [sqQuoteFieldnames];
+end;
+
+procedure TSQLite3Connection.UpdateIndexDefs(IndexDefs: TIndexDefs; TableName: string);
 var
   str1: string;
   
@@ -746,6 +756,12 @@ begin
  result:= sqlite3_last_insert_rowid(fhandle);
 end;
 
+procedure TSQLite3Connection.GetFieldNames(const TableName: string;
+  List: TStrings);
+begin
+  GetDBInfo(stColumns,TableName,'name',List);
+end;
+
 procedure TSQLite3Connection.setoptions(const avalue: tsqliteoptions);
 begin
  if avalue <> foptions then 

+ 3 - 3
packages/fcl-db/tests/testdbbasics.pas

@@ -239,9 +239,9 @@ begin
 
         Next;
         if (i > ABufferCount) and not EOF then
-          AssertEquals('deCheckBrowseMode:0;deDataSetScroll:-1;',DataEvents)
+          AssertEquals('deCheckBrowseMode:0;deDataSetScroll:-1;DataSetScrolled:1;',DataEvents)
         else
-          AssertEquals('deCheckBrowseMode:0;deDataSetScroll:0;',DataEvents);
+          AssertEquals('deCheckBrowseMode:0;deDataSetScroll:0;DataSetScrolled:0;',DataEvents);
         DataEvents := '';
         end;
       AssertEquals(count,i-1);
@@ -324,7 +324,7 @@ begin
     AssertEquals('deDataSetChange:0;',DataEvents);
     DataEvents := '';
     next;
-    AssertEquals('deCheckBrowseMode:0;DataEvent;deDataSetScroll:0;',DataEvents);
+    AssertEquals('deCheckBrowseMode:0;DataEvent;deDataSetScroll:0;DataSetScrolled:1;',DataEvents);
     close;
     end;
   aDatasource.Free;

+ 115 - 0
packages/fcl-db/tests/testfieldtypes.pas

@@ -28,7 +28,9 @@ type
     procedure RunTest; override;
   published
     procedure TestClearUpdateableStatus;
+    procedure TestReadOnlyParseSQL; // bug 9254
     procedure TestParseJoins; // bug 10148
+    procedure TestParseUnion; // bug 8442
     procedure TestInsertLargeStrFields; // bug 9600
     procedure TestNumericNames; // Bug9661
     procedure Test11Params;
@@ -893,6 +895,104 @@ begin
     end;
 end;
 
+procedure TTestFieldTypes.TestReadOnlyParseSQL;
+begin
+  with TSQLDBConnector(DBConnector) do
+    begin
+
+    GetFieldDataset(True);
+    with query do
+      begin
+      AssertFalse(ReadOnly);
+      AssertTrue(ParseSQL);
+
+      // If ParseSQL is false, and no update-queries are given, the query
+      // shouldn't be updateable after open.
+      ParseSQL := False;
+      AssertFalse(ParseSQL);
+      AssertFalse(ReadOnly);
+      SQL.Text := 'select * from FPDEV;';
+      open;
+      AssertFalse(ParseSQL);
+      AssertFalse(ReadOnly);
+      AssertFalse(CanModify);
+      close;
+
+      // If ParseSQL is true, the query should be updateable after open.
+      ReadOnly := False;
+      ParseSQL := True;
+      AssertTrue(ParseSQL);
+      AssertFalse(ReadOnly);
+      SQL.Text := 'select * from FPDEV;';
+      open;
+      AssertTrue(ParseSQL);
+      AssertFalse(ReadOnly);
+      AssertTrue(CanModify);
+      edit;
+      FieldByName('ID').AsInteger:=321;
+      post;
+      Applyupdates;
+      close;
+      
+      // If ParseSQL is true, but the supplied query isn't updateable, then
+      // the query shouldn't be updateable after open.
+      ReadOnly := False;
+      SQL.Text:='select ID,NAME from FPDEV where ID<5';
+      sql.Add('union');
+      sql.Add('select ID,NAME from FPDEV where ID>5');
+      AssertTrue(ParseSQL);
+      AssertFalse(ReadOnly);
+      open;
+      AssertTrue(ParseSQL);
+      AssertFalse(ReadOnly);
+      AssertFalse(CanModify);
+      close;
+
+      // As above, but now with an update-query, so that the query should
+      // be updateable again.
+      ReadOnly := False;
+      AssertTrue(ParseSQL);
+      AssertFalse(ReadOnly);
+      UpdateSQL.Text:='update FPDEV set ID=:ID where ID=:OLD_ID';
+      open;
+      AssertTrue(ParseSQL);
+      AssertFalse(ReadOnly);
+      AssertTrue(CanModify);
+      edit;
+      post;
+      Applyupdates;
+      close;
+
+      // Also if ParseSQL is False, the query should be updateable if a update-
+      // query is given.
+      ReadOnly := False;
+      ParseSQL := False;
+      AssertFalse(ParseSQL);
+      AssertFalse(ReadOnly);
+      open;
+      AssertFalse(ParseSQL);
+      AssertFalse(ReadOnly);
+      AssertTrue(CanModify);
+      edit;
+      FieldByName('ID').AsInteger:=1;
+      post;
+      Applyupdates;
+      close;
+
+      // But if ReadOnly is true, then CanModify should always be false
+      ReadOnly := True;
+      ParseSQL := False;
+      AssertFalse(ParseSQL);
+      AssertTrue(ReadOnly);
+      open;
+      AssertFalse(ParseSQL);
+      AssertTrue(ReadOnly);
+      AssertFalse(CanModify);
+      close;
+      end;
+    end;
+end;
+
 procedure TTestFieldTypes.TestParseJoins;
 begin
   with TSQLDBConnector(DBConnector) do
@@ -906,6 +1006,21 @@ begin
     end;
 end;
 
+procedure TTestFieldTypes.TestParseUnion;
+begin
+  with TSQLDBConnector(DBConnector) do
+    begin
+    with query do
+      begin
+      SQL.Text:='select NAME from FPDEV where ID<5';
+      sql.Add('union');
+      sql.Add('select NAME from FPDEV where ID>5');
+      Open;
+      close;
+      end;
+    end;
+end;
+
 procedure TTestFieldTypes.TestInsertLargeStrFields;
 begin
   with TSQLDBConnector(DBConnector) do