Browse Source

Merged revisions 10861-10864,10875,10882,10891,10907,10909,10915-10916,10922-10923,10928,10930,10933,10935,10939,10942,10948,10952-10956,10960,10964-10965,10972,10974-10975,10977,10980,10982-10983,10985,10993,11000,11016,11032-11033,11042,11044,11046,11053,11060,11062,11064,11067,11075,11078,11080,11085-11086,11089,11094,11096,11098,11103-11104,11106,11108-11109,11111,11114,11117,11122,11124,11126,11130-11131,11133,11136,11139-11141,11146-11147,11152-11154,11157,11159,11166-11167,11170,11173,11178,11181-11182,11184-11185,11187-11189,11195-11196,11206-11209,11214-11215,11223,11225,11227,11232,11235,11239-11240,11249-11256,11258,11260-11261,11264-11265,11268,11271,11278,11280-11282,11286-11288,11292-11294,11297,11299-11300,11302,11304-11313,11315-11316,11318-11319,11324-11326,11328-11333,11335-11336,11339-11340,11346-11347,11349,11351-11352,11354-11356,11362-11366,11369,11371-11375,11387,11393-11396,11401,11411-11414,11420,11422,11427-11428,11465,11469-11470,11487-11488,11490,11518-11521,11523,11528,11535,11551,11553,11555,11557,11562,11564,11571,11588,11619,11621-11622,11628,11664-11667,11670,11682-11683,11685,11689-11692,11694-11696,11698,11701-11702,11705-11707,11712-11718,11723-11726,11728-11729,11733-11737,11778,11780-11781,11785,11810,11822,11831,11836,11848,11870 via svnmerge from
svn+ssh://svn.freepascal.org/FPC/svn/fpc/trunk

........
r10861 | michael | 2008-05-02 18:00:48 +0200 (Fri, 02 May 2008) | 1 line

* Fixed some typos
........
r11016 | michael | 2008-05-19 21:04:50 +0200 (Mon, 19 May 2008) | 1 line

* Writing to file log now also will raise an error
........
r11086 | michael | 2008-05-26 20:26:47 +0200 (Mon, 26 May 2008) | 1 line

* Added bucket lists implementation
........
r11261 | michael | 2008-06-21 23:13:25 +0200 (Sat, 21 Jun 2008) | 1 line

* Fixed reading of alternate font name
........
r11268 | michael | 2008-06-23 21:38:40 +0200 (Mon, 23 Jun 2008) | 1 line

* Fixed style reading for OOffice documents
........
r11312 | michael | 2008-07-02 18:23:13 +0200 (Wed, 02 Jul 2008) | 1 line

* Fixed memory leak when re-assigning datasource property
........
r11351 | michael | 2008-07-09 15:49:05 +0200 (Wed, 09 Jul 2008) | 1 line

* Fixed saving/restoring of precision in TDDFielddef
........
r11352 | michael | 2008-07-09 15:50:12 +0200 (Wed, 09 Jul 2008) | 1 line

* Code generator for populating a data dictionary in code
........
r11354 | michael | 2008-07-10 11:08:40 +0200 (Thu, 10 Jul 2008) | 1 line

* Improvements to tiopf code generator, and build project for use with lazarus
........
r11355 | michael | 2008-07-10 11:40:37 +0200 (Thu, 10 Jul 2008) | 1 line

* Corrected int64 type name
........
r11356 | michael | 2008-07-10 16:37:27 +0200 (Thu, 10 Jul 2008) | 1 line

* Fixes so it compiles in all cases
........
r11363 | michael | 2008-07-11 14:41:40 +0200 (Fri, 11 Jul 2008) | 1 line

* Support for indexes and creating sql from them
........
r11364 | michael | 2008-07-11 14:45:52 +0200 (Fri, 11 Jul 2008) | 1 line

* Set unit output path that does not disturb make
........
r11365 | michael | 2008-07-11 14:53:32 +0200 (Fri, 11 Jul 2008) | 1 line

* Session info in lps file
........
r11366 | michael | 2008-07-11 16:16:39 +0200 (Fri, 11 Jul 2008) | 1 line

* SqlScript committed
........
r11387 | michael | 2008-07-16 19:32:26 +0200 (Wed, 16 Jul 2008) | 1 line

* ReadListClass SQL statement needs correct where, O not assigned in MapObjectToRow
........
r11682 | michael | 2008-09-01 12:05:52 +0200 (Mon, 01 Sep 2008) | 1 line

* In case no default is specified and no NoDefault is present, act as if NoDefault was given. (bug ID 10080, now we are Delphi compatible)
........
r11870 | michael | 2008-10-09 11:55:21 +0200 (Thu, 09 Oct 2008) | 1 line

* Fixed bug #12323. Bug is in getting default value of string parameters, unterminated memory copy
........

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

michael 17 years ago
parent
commit
2479700bee

+ 5 - 0
.gitattributes

@@ -1122,13 +1122,17 @@ packages/fcl-db/src/base/dsparams.inc svneol=native#text/plain
 packages/fcl-db/src/base/fields.inc svneol=native#text/plain
 packages/fcl-db/src/base/fields.inc svneol=native#text/plain
 packages/fcl-db/src/base/fpmake.inc svneol=native#text/plain
 packages/fcl-db/src/base/fpmake.inc svneol=native#text/plain
 packages/fcl-db/src/base/fpmake.pp svneol=native#text/plain
 packages/fcl-db/src/base/fpmake.pp svneol=native#text/plain
+packages/fcl-db/src/base/sqlscript.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/Makefile svneol=native#text/plain
 packages/fcl-db/src/codegen/Makefile svneol=native#text/plain
 packages/fcl-db/src/codegen/Makefile.fpc svneol=native#text/plain
 packages/fcl-db/src/codegen/Makefile.fpc svneol=native#text/plain
+packages/fcl-db/src/codegen/buildddcg.lpi svneol=native#text/plain
+packages/fcl-db/src/codegen/buildddcg.lpr svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgcreatedbf.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgcreatedbf.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgdbcoll.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgdbcoll.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgsqlconst.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgsqlconst.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgtiopf.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpcgtiopf.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpddcodegen.pp svneol=native#text/plain
 packages/fcl-db/src/codegen/fpddcodegen.pp svneol=native#text/plain
+packages/fcl-db/src/codegen/fpddpopcode.pp svneol=native#text/plain
 packages/fcl-db/src/datadict/Makefile svneol=native#text/plain
 packages/fcl-db/src/datadict/Makefile svneol=native#text/plain
 packages/fcl-db/src/datadict/Makefile.fpc svneol=native#text/plain
 packages/fcl-db/src/datadict/Makefile.fpc svneol=native#text/plain
 packages/fcl-db/src/datadict/buildd.lpi svneol=native#text/plain
 packages/fcl-db/src/datadict/buildd.lpi svneol=native#text/plain
@@ -1286,6 +1290,7 @@ packages/fcl-db/tests/testbasics.pas svneol=native#text/plain
 packages/fcl-db/tests/testdatasources.pas svneol=native#text/plain
 packages/fcl-db/tests/testdatasources.pas svneol=native#text/plain
 packages/fcl-db/tests/testdbbasics.pas -text
 packages/fcl-db/tests/testdbbasics.pas -text
 packages/fcl-db/tests/testfieldtypes.pas svneol=native#text/plain
 packages/fcl-db/tests/testfieldtypes.pas svneol=native#text/plain
+packages/fcl-db/tests/testsqlscript.pas svneol=native#text/plain
 packages/fcl-db/tests/toolsunit.pas -text
 packages/fcl-db/tests/toolsunit.pas -text
 packages/fcl-fpcunit/Makefile svneol=native#text/plain
 packages/fcl-fpcunit/Makefile svneol=native#text/plain
 packages/fcl-fpcunit/Makefile.fpc svneol=native#text/plain
 packages/fcl-fpcunit/Makefile.fpc svneol=native#text/plain

+ 26 - 12
compiler/pdecvar.pas

@@ -214,6 +214,26 @@ implementation
              end;
              end;
           end;
           end;
 
 
+          function allow_default_property(p : tpropertysym) : boolean;
+
+          begin
+             allow_default_property:=
+               (is_ordinal(p.propdef) or
+{$ifndef cpu64bitaddr}
+               is_64bitint(p.propdef) or
+{$endif cpu64bitaddr}
+               is_class(p.propdef) or
+               is_single(p.propdef) or
+               (p.propdef.typ in [classrefdef,pointerdef]) or
+                 is_smallset(p.propdef)
+               ) and not
+               (
+                (p.propdef.typ=arraydef) and
+                (ppo_indexed in p.propoptions)
+               ) and not
+               (ppo_hasparameters in p.propoptions);
+          end;
+
       var
       var
          sym : tsym;
          sym : tsym;
          p : tpropertysym;
          p : tpropertysym;
@@ -562,18 +582,7 @@ implementation
            end;
            end;
          if try_to_consume(_DEFAULT) then
          if try_to_consume(_DEFAULT) then
            begin
            begin
-              if not(is_ordinal(p.propdef) or
-{$ifndef cpu64bit}
-                     is_64bitint(p.propdef) or
-{$endif cpu64bit}
-                     is_class(p.propdef) or
-                     is_single(p.propdef) or
-                     (p.propdef.typ in [classrefdef,pointerdef]) or
-                     ((p.propdef.typ=setdef) and
-                      (tsetdef(p.propdef).settype=smallset))) or
-                     ((p.propdef.typ=arraydef) and
-                      (ppo_indexed in p.propoptions)) or
-                 (ppo_hasparameters in p.propoptions) then
+              if not allow_default_property(p) then
                 begin
                 begin
                   Message(parser_e_property_cant_have_a_default_value);
                   Message(parser_e_property_cant_have_a_default_value);
                   { Error recovery }
                   { Error recovery }
@@ -609,9 +618,14 @@ implementation
                 end;
                 end;
            end
            end
          else if try_to_consume(_NODEFAULT) then
          else if try_to_consume(_NODEFAULT) then
+           begin
+              p.default:=longint($80000000);
+           end
+         else if allow_default_property(p) then
            begin
            begin
               p.default:=longint($80000000);
               p.default:=longint($80000000);
            end;
            end;
+  
          { Parse possible "implements" keyword }
          { Parse possible "implements" keyword }
          if try_to_consume(_IMPLEMENTS) then
          if try_to_consume(_IMPLEMENTS) then
            begin
            begin

+ 7 - 1
compiler/symdef.pas

@@ -2803,7 +2803,13 @@ implementation
                   case hpc.consttyp of
                   case hpc.consttyp of
                     conststring,
                     conststring,
                     constresourcestring :
                     constresourcestring :
-                      hs:=strpas(pchar(hpc.value.valueptr));
+                      begin
+                      If hpc.value.len>0 then
+                        begin
+                        setLength(hs,hpc.value.len);
+                        move(hpc.value.valueptr^,hs[1],hpc.value.len);
+                        end;
+                      end;
                     constreal :
                     constreal :
                       str(pbestreal(hpc.value.valueptr)^,hs);
                       str(pbestreal(hpc.value.valueptr)^,hs);
                     constpointer :
                     constpointer :

+ 382 - 4
packages/fcl-base/src/contnrs.pp

@@ -471,22 +471,113 @@ type
     Property OwnsObjects : Boolean Read FOwnsObjects Write FOwnsObjects;
     Property OwnsObjects : Boolean Read FOwnsObjects Write FOwnsObjects;
   end;
   end;
 
 
-
   EDuplicate = class(Exception);
   EDuplicate = class(Exception);
   EKeyNotFound = class(Exception);
   EKeyNotFound = class(Exception);
 
 
-
   function RSHash(const S: string; const TableSize: Longword): Longword;
   function RSHash(const S: string; const TableSize: Longword): Longword;
 
 
+{ ---------------------------------------------------------------------
+    Bucket lists as in Delphi
+  ---------------------------------------------------------------------}
+  
+
+Type
+  TBucketItem = record
+    Item, Data: Pointer;
+  end;
+  TBucketItemArray = array of TBucketItem;
+
+  TBucket = record
+    Count : Integer;
+    Items : TBucketItemArray;
+  end;
+  PBucket = ^TBucket;
+  TBucketArray = array of TBucket;
+
+  TBucketProc = procedure(AInfo, AItem, AData: Pointer; out AContinue: Boolean);
+  TBucketProcObject = procedure(AItem, AData: Pointer; out AContinue: Boolean) of Object;
+
+{ ---------------------------------------------------------------------
+  TCustomBucketList
+  ---------------------------------------------------------------------}
+
+  { TCustomBucketList }
+
+  TCustomBucketList = class(TObject)
+  private
+    FBuckets: TBucketArray;
+    function GetBucketCount: Integer;
+    function GetData(AItem: Pointer): Pointer;
+    procedure SetData(AItem: Pointer; const AData: Pointer);
+    procedure SetBucketCount(const Value: Integer);
+  protected
+    Procedure GetBucketItem(AItem: Pointer; out ABucket, AIndex: Integer);
+    function AddItem(ABucket: Integer; AItem, AData: Pointer): Pointer; virtual;
+    function BucketFor(AItem: Pointer): Integer; virtual; abstract;
+    function DeleteItem(ABucket: Integer; AIndex: Integer): Pointer; virtual;
+    Procedure Error(Msg : String; Args : Array of Const);
+    function FindItem(AItem: Pointer; out ABucket, AIndex: Integer): Boolean; virtual;
+    property Buckets: TBucketArray read FBuckets;
+    property BucketCount: Integer read GetBucketCount write SetBucketCount;
+  public
+    destructor Destroy; override;
+    procedure Clear;
+    function Add(AItem, AData: Pointer): Pointer;
+    procedure Assign(AList: TCustomBucketList);
+    function Exists(AItem: Pointer): Boolean;
+    function Find(AItem: Pointer; out AData: Pointer): Boolean;
+    function ForEach(AProc: TBucketProc; AInfo: Pointer = nil): Boolean;
+    function ForEach(AProc: TBucketProcObject): Boolean;
+    function Remove(AItem: Pointer): Pointer;
+    property Data[AItem: Pointer]: Pointer read GetData write SetData; default;
+  end;
+
+{ ---------------------------------------------------------------------
+  TBucketList
+  ---------------------------------------------------------------------}
+  
+
+  TBucketListSizes = (bl2, bl4, bl8, bl16, bl32, bl64, bl128, bl256);
+
+  { TBucketList }
+
+  TBucketList = class(TCustomBucketList)
+  private
+    FBucketMask: Byte;
+  protected
+    function BucketFor(AItem: Pointer): Integer; override;
+  public
+    constructor Create(ABuckets: TBucketListSizes = bl16);
+  end;
+
+{ ---------------------------------------------------------------------
+  TObjectBucketList
+  ---------------------------------------------------------------------}
+  
+  { TObjectBucketList }
+
+  TObjectBucketList = class(TBucketList)
+  protected
+    function GetData(AItem: TObject): TObject;
+    procedure SetData(AItem: TObject; const AData: TObject);
+  public
+    function Add(AItem, AData: TObject): TObject;
+    function Remove(AItem: TObject): TObject;
+    property Data[AItem: TObject]: TObject read GetData write SetData; default;
+  end;
+
+
 implementation
 implementation
 
 
 uses
 uses
   RtlConsts;
   RtlConsts;
 
 
 ResourceString
 ResourceString
-  DuplicateMsg = 'An item with key %0:s already exists';
+  DuplicateMsg   = 'An item with key %0:s already exists';
   KeyNotFoundMsg = 'Method: %0:s key [''%1:s''] not found in container';
   KeyNotFoundMsg = 'Method: %0:s key [''%1:s''] not found in container';
-  NotEmptyMsg = 'Hash table not empty.';
+  NotEmptyMsg    = 'Hash table not empty.';
+  SErrNoSuchItem = 'No item in list for %p';
+  SDuplicateItem = 'Item already exists in list: %p';
 
 
 const
 const
   NPRIMES = 28;
   NPRIMES = 28;
@@ -2335,4 +2426,291 @@ begin
   Inherited;
   Inherited;
 end;
 end;
 
 
+{ TCustomBucketList }
+
+function TCustomBucketList.GetData(AItem: Pointer): Pointer;
+
+Var
+  B,I : Integer;
+
+begin
+  GetBucketItem(AItem,B,I);
+  Result:=FBuckets[B].Items[I].Data;
+end;
+
+function TCustomBucketList.GetBucketCount: Integer;
+begin
+  Result:=Length(FBuckets);
+end;
+
+procedure TCustomBucketList.SetData(AItem: Pointer; const AData: Pointer);
+
+Var
+  B,I : Integer;
+
+begin
+  GetBucketItem(AItem,B,I);
+  FBuckets[B].Items[I].Data:=AData;
+end;
+
+procedure TCustomBucketList.SetBucketCount(const Value: Integer);
+
+begin
+  If (Value<>GetBucketCount) then
+    SetLength(FBuckets,Value);
+end;
+
+procedure TCustomBucketList.GetBucketItem(AItem: Pointer; out ABucket,
+  AIndex: Integer);
+begin
+  If Not FindItem(AItem,ABucket,AIndex) then
+    Error(SErrNoSuchItem,[AItem]);
+end;
+
+function TCustomBucketList.AddItem(ABucket: Integer; AItem, AData: Pointer
+  ): Pointer;
+  
+Var
+  B : PBucket;
+  L : Integer;
+  
+begin
+  B:=@FBuckets[ABucket];
+  L:=Length(B^.Items);
+  If (B^.Count=L) then
+    begin
+    If L<8 then
+      L:=8
+    else
+      L:=L+L div 2;
+    SetLength(B^.Items,L);
+    end;
+  With B^ do
+    begin
+    Items[Count].Item:=AItem;
+    Items[Count].Data:=AData;
+    Result:=AData;
+    Inc(Count);
+    end;
+end;
+
+function TCustomBucketList.DeleteItem(ABucket: Integer; AIndex: Integer): Pointer;
+  
+Var
+  B : PBucket;
+  L : Integer;
+  
+begin
+  B:=@FBuckets[ABucket];
+  Result:=B^.Items[Aindex].Data;
+  If B^.Count=1 then
+    SetLength(B^.Items,0)
+  else
+    begin
+    L:=(B^.Count-AIndex-1);// No point in moving if last one...
+    If L>0 then
+      Move(B^.Items[AIndex+1],B^.Items[AIndex],L*SizeOf(TBucketItem));
+    end;
+  Dec(B^.Count);
+end;
+
+procedure TCustomBucketList.Error(Msg: String; Args: array of const);
+begin
+  Raise ElistError.CreateFmt(Msg,Args);
+end;
+
+function TCustomBucketList.FindItem(AItem: Pointer; out ABucket, AIndex: Integer
+  ): Boolean;
+  
+Var
+  I : Integer;
+  B : TBucket;
+  
+begin
+  ABucket:=BucketFor(AItem);
+  B:=FBuckets[ABucket];
+  I:=B.Count-1;
+  While (I>=0) And (B.Items[I].Item<>AItem) do
+    Dec(I);
+  Result:=I>=0;
+  If Result then
+    AIndex:=I;
+end;
+
+destructor TCustomBucketList.Destroy;
+begin
+  Clear;
+  inherited Destroy;
+end;
+
+procedure TCustomBucketList.Clear;
+
+Var
+  B : TBucket;
+  I,J : Integer;
+
+begin
+  For I:=0 to Length(FBuckets)-1 do
+    begin
+    B:=FBuckets[I];
+    For J:=B.Count-1 downto 0 do
+      DeleteItem(I,J);
+    end;
+  SetLength(FBuckets,0);
+end;
+
+function TCustomBucketList.Add(AItem, AData: Pointer): Pointer;
+
+Var
+  B,I : Integer;
+
+begin
+  If FindItem(AItem,B,I) then
+    Error(SDuplicateItem,[AItem]);
+  Result:=AddItem(B,AItem,AData);
+end;
+
+procedure TCustomBucketList.Assign(AList: TCustomBucketList);
+
+Var
+  I,J : Integer;
+
+begin
+  Clear;
+  SetLength(FBuckets,Length(Alist.FBuckets));
+  For I:=0 to BucketCount-1 do
+    begin
+    SetLength(FBuckets[i].Items,Length(AList.Fbuckets[I].Items));
+    For J:=0 to AList.Fbuckets[I].Count-1 do
+      With AList.Fbuckets[I].Items[J] do
+        AddItem(I,Item,Data);
+    end;
+end;
+
+function TCustomBucketList.Exists(AItem: Pointer): Boolean;
+
+Var
+  B,I : Integer;
+
+begin
+  Result:=FindItem(Aitem,B,I);
+end;
+
+function TCustomBucketList.Find(AItem: Pointer; out AData: Pointer): Boolean;
+
+Var
+  B,I : integer;
+
+begin
+  Result:=FindItem(AItem,B,I);
+  If Result then
+    AData:=FBuckets[B].Items[I].Data;
+end;
+
+function TCustomBucketList.ForEach(AProc: TBucketProc; AInfo: Pointer
+  ): Boolean;
+  
+Var
+  I,J,S : Integer;
+  Bu : TBucket;
+  
+begin
+  I:=0;
+  Result:=True;
+  S:=GetBucketCount;
+  While Result and (I<S) do
+    begin
+    J:=0;
+    Bu:=FBuckets[I];
+    While Result and (J<Bu.Count) do
+      begin
+      With Bu.Items[J] do
+        AProc(AInfo,Item,Data,Result);
+      Inc(J);
+      end;
+    Inc(I);
+    end;
+end;
+
+function TCustomBucketList.ForEach(AProc: TBucketProcObject): Boolean;
+
+Var
+  I,J,S : Integer;
+  Bu : TBucket;
+
+begin
+  I:=0;
+  Result:=True;
+  S:=GetBucketCount;
+  While Result and (I<S) do
+    begin
+    J:=0;
+    Bu:=FBuckets[I];
+    While Result and (J<Bu.Count) do
+      begin
+      With Bu.Items[J] do
+        AProc(Item,Data,Result);
+      Inc(J);
+      end;
+    Inc(I);
+    end;
+end;
+
+function TCustomBucketList.Remove(AItem: Pointer): Pointer;
+
+Var
+  B,I : integer;
+
+begin
+  If FindItem(AItem,B,I) then
+    begin
+    Result:=FBuckets[B].Items[I].Data;
+    DeleteItem(B,I);
+    end
+  else
+    Result:=Nil;
+end;
+
+{ TBucketList }
+
+function TBucketList.BucketFor(AItem: Pointer): Integer;
+begin
+  // Pointers on average have a granularity of 4
+  Result:=(PtrInt(AItem) shr 2) and FBucketMask;
+end;
+
+constructor TBucketList.Create(ABuckets: TBucketListSizes);
+
+Var
+  L : Integer;
+  
+begin
+  Inherited Create;
+  L:=1 shl (Ord(Abuckets)+1);
+  SetBucketCount(L);
+  FBucketMask:=L-1;
+end;
+
+{ TObjectBucketList }
+
+function TObjectBucketList.GetData(AItem: TObject): TObject;
+begin
+  Result:=TObject(Inherited GetData(AItem));
+end;
+
+procedure TObjectBucketList.SetData(AItem: TObject; const AData: TObject);
+begin
+  Inherited SetData(Pointer(AItem),Pointer(AData));
+end;
+
+function TObjectBucketList.Add(AItem, AData: TObject): TObject;
+begin
+  Result:=TObject(Inherited Add(Pointer(AItem),Pointer(AData)));
+end;
+
+function TObjectBucketList.Remove(AItem: TObject): TObject;
+begin
+  Result:=TObject(Inherited Remove(Pointer(AItem)));
+end;
+
 end.
 end.

+ 16 - 7
packages/fcl-base/src/eventlog.pp

@@ -102,12 +102,13 @@ Type
 
 
 Resourcestring
 Resourcestring
 
 
-  SLogInfo    = 'Info';
-  SLogWarning = 'Warning';
-  SLogError   = 'Error';
-  SLogDebug   = 'Debug';
-  SLogCustom  = 'Custom (%d)';
-
+  SLogInfo      = 'Info';
+  SLogWarning   = 'Warning';
+  SLogError     = 'Error';
+  SLogDebug     = 'Debug';
+  SLogCustom    = 'Custom (%d)';
+  SErrLogFailedMsg = 'Failed to log entry (Error: %s)';
+  
 implementation
 implementation
 
 
 {$i eventlog.inc}
 {$i eventlog.inc}
@@ -190,7 +191,15 @@ begin
   TS:=FormatDateTime(FTimeStampFormat,Now);
   TS:=FormatDateTime(FTimeStampFormat,Now);
   T:=EventTypeToString(EventType);
   T:=EventTypeToString(EventType);
   S:=Format('%s [%s %s] %s%s',[Identification,TS,T,Msg,LineEnding]);
   S:=Format('%s [%s %s] %s%s',[Identification,TS,T,Msg,LineEnding]);
-  FStream.Write(S[1],Length(S));
+  try
+    FStream.WriteBuffer(S[1],Length(S));
+    S:='';
+  except
+    On E : Exception do
+      S:=E.Message;
+  end;  
+  If (S<>'') and RaiseExceptionOnError then
+    Raise ELogError.CreateFmt(SErrLogFailedMsg,[S]);
 end;
 end;
 
 
 procedure TEventLog.Log(Fmt: String; Args: array of const);
 procedure TEventLog.Log(Fmt: String; Args: array of const);

+ 47 - 33
packages/fcl-base/src/rtfpars.pp

@@ -85,6 +85,7 @@ TRTFParser = class(TObject)
     Procedure UngetToken;
     Procedure UngetToken;
     Procedure SetToken (Aclass, major, minor, param : Integer; text : string);
     Procedure SetToken (Aclass, major, minor, param : Integer; text : string);
     Procedure ExpandStyle (n : Integer);
     Procedure ExpandStyle (n : Integer);
+    Function GetRtfBuf : String;
     { Properties }
     { Properties }
     Property Colors [Index : Integer]: PRTFColor Read GetColor;
     Property Colors [Index : Integer]: PRTFColor Read GetColor;
     Property ClassCallBacks [AClass : Integer]: TRTFFuncptr
     Property ClassCallBacks [AClass : Integer]: TRTFFuncptr
@@ -728,6 +729,13 @@ While true do
      Error ('FTErr - missing font name');
      Error ('FTErr - missing font name');
   fp^.rtffname:=bp;
   fp^.rtffname:=bp;
   { Read alternate font}
   { Read alternate font}
+  if rtfclass=rtfgroup then
+    begin
+    SkipGroup;
+    if Not rtfMajor=ord(';') then
+      Error('Alternate font badly terminated');
+    GetToken;
+    end;
   if (old=0) then       { need to see "End;" here }
   if (old=0) then       { need to see "End;" here }
     Begin
     Begin
     GetToken;
     GetToken;
@@ -795,6 +803,7 @@ var
   sp          : PRTFStyle;
   sp          : PRTFStyle;
   sep,sepLast : PRTFStyleElt;
   sep,sepLast : PRTFStyleElt;
   bp          : string[rtfBufSiz];
   bp          : string[rtfBufSiz];
+  I : Integer;
 
 
 Begin
 Begin
 While true do
 While true do
@@ -815,40 +824,40 @@ While true do
   FstyleList := sp;
   FstyleList := sp;
   if not CheckCM (rtfGroup, rtfBeginGroup) then
   if not CheckCM (rtfGroup, rtfBeginGroup) then
      Error ('SSErr - missing {');
      Error ('SSErr - missing {');
-  while (GetToken=rtfControl) or (FTokenClass=rtfControl) do
+  I:=0;
+  GetToken;
+  while (fRTFClass=rtfControl) or (FTokenClass=rtfControl) or (FRTFClass=rtfGroup) do
     Begin
     Begin
-    if rtfClass=rtfUnknown then
-      continue;
-    if (CheckMM (rtfParAttr, rtfStyleNum)) then
-      Begin
-      sp^.rtfSNum:=rtfParam;
-      continue;
-      End;
-    if (CheckMM (rtfStyleAttr, rtfBasedOn)) then
-      Begin
-      sp^.rtfSBasedOn:=rtfParam;
-      continue;
-      End;
-    if (CheckMM (rtfStyleAttr, rtfNext)) then
-      Begin
-      sp^.rtfSNextPar:=rtfParam;
-      Continue;
-      End;
-    new(sep);
-    if sep=nil then
-      Error ('SSErr - cannot allocate style element');
-    sep^.rtfSEClass:=rtfClass;
-    sep^.rtfSEMajor:=rtfMajor;
-    sep^.rtfSEMinor:=rtfMinor;
-    sep^.rtfSEParam:=rtfParam;
-    sep^.rtfSEText:=rtfTextBuf;
-    if sepLast=nil then
-       sp^.rtfSSEList:=sep      { first element }
-    else                                { add to end }
-       sepLast^.rtfNextSE:=sep;
-    sep^.rtfNextSE:=nil;
-    sepLast:=sep;
-  End;
+    If CheckCM(rtfGroup, rtfBeginGroup) then
+      SkipGroup
+    else if rtfClass<>rtfUnknown then
+      begin
+      if (CheckMM (rtfParAttr, rtfStyleNum)) then
+        sp^.rtfSNum:=rtfParam
+      else if (CheckMM (rtfStyleAttr, rtfBasedOn)) then
+        sp^.rtfSBasedOn:=rtfParam
+      else if (CheckMM (rtfStyleAttr, rtfNext)) then
+        sp^.rtfSNextPar:=rtfParam
+      else
+        begin
+        new(sep);
+        if sep=nil then
+          Error ('SSErr - cannot allocate style element');
+        sep^.rtfSEClass:=rtfClass;
+        sep^.rtfSEMajor:=rtfMajor;
+        sep^.rtfSEMinor:=rtfMinor;
+        sep^.rtfSEParam:=rtfParam;
+        sep^.rtfSEText:=rtfTextBuf;
+        if sepLast=nil then
+           sp^.rtfSSEList:=sep      { first element }
+        else                                { add to end }
+           sepLast^.rtfNextSE:=sep;
+        sep^.rtfNextSE:=nil;
+        sepLast:=sep;
+        end;
+      end;
+    GetToken;
+    End;
   if sp^.rtfSNextPar=-1 then            { \snext not given }
   if sp^.rtfSNextPar=-1 then            { \snext not given }
     sp^.rtfSNextPar:=sp^.rtfSNum;       { next is itself }
     sp^.rtfSNextPar:=sp^.rtfSNum;       { next is itself }
   if rtfClass<>rtfText then
   if rtfClass<>rtfText then
@@ -994,6 +1003,11 @@ while se<>nil do
 s^.rtfExpanding:=0;     { done - clear expansion flag }
 s^.rtfExpanding:=0;     { done - clear expansion flag }
 End;
 End;
 
 
+function TRTFParser.GetRtfBuf: String;
+begin
+  Result:=rtfTextBuf;
+end;
+
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
        Initialize lookup table hash values.
        Initialize lookup table hash values.
        Only need to do this the first time it's called.
        Only need to do this the first time it's called.

+ 59 - 59
packages/fcl-db/src/base/Makefile

@@ -1,5 +1,5 @@
 #
 #
-# Don't edit, this file is generated by FPCMake Version 2.0.0 [2008/06/15]
+# Don't edit, this file is generated by FPCMake Version 2.0.0 [2008/06/23]
 #
 #
 default: all
 default: all
 MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos m68k-embedded powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macos powerpc-darwin powerpc-morphos powerpc-embedded sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-darwin x86_64-win64 x86_64-embedded arm-linux arm-palmos arm-darwin arm-wince arm-gba arm-nds arm-embedded arm-symbian powerpc64-linux powerpc64-darwin powerpc64-embedded avr-embedded armeb-linux armeb-embedded
 MAKEFILETARGETS=i386-linux i386-go32v2 i386-win32 i386-os2 i386-freebsd i386-beos i386-haiku i386-netbsd i386-solaris i386-qnx i386-netware i386-openbsd i386-wdosx i386-darwin i386-emx i386-watcom i386-netwlibc i386-wince i386-embedded i386-symbian m68k-linux m68k-freebsd m68k-netbsd m68k-amiga m68k-atari m68k-openbsd m68k-palmos m68k-embedded powerpc-linux powerpc-netbsd powerpc-amiga powerpc-macos powerpc-darwin powerpc-morphos powerpc-embedded sparc-linux sparc-netbsd sparc-solaris sparc-embedded x86_64-linux x86_64-freebsd x86_64-darwin x86_64-win64 x86_64-embedded arm-linux arm-palmos arm-darwin arm-wince arm-gba arm-nds arm-embedded arm-symbian powerpc64-linux powerpc64-darwin powerpc64-embedded avr-embedded armeb-linux armeb-embedded
@@ -261,178 +261,178 @@ PACKAGESDIR:=$(wildcard $(FPCDIR) $(FPCDIR)/packages $(FPCDIR)/packages/base $(F
 override PACKAGE_NAME=fcl-db
 override PACKAGE_NAME=fcl-db
 PACKAGEDIR_MAIN:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-db/Makefile.fpc,$(PACKAGESDIR))))))
 PACKAGEDIR_MAIN:=$(firstword $(subst /Makefile.fpc,,$(strip $(wildcard $(addsuffix /fcl-db/Makefile.fpc,$(PACKAGESDIR))))))
 ifeq ($(FULL_TARGET),i386-linux)
 ifeq ($(FULL_TARGET),i386-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-go32v2)
 ifeq ($(FULL_TARGET),i386-go32v2)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-win32)
 ifeq ($(FULL_TARGET),i386-win32)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-os2)
 ifeq ($(FULL_TARGET),i386-os2)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-freebsd)
 ifeq ($(FULL_TARGET),i386-freebsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-beos)
 ifeq ($(FULL_TARGET),i386-beos)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-haiku)
 ifeq ($(FULL_TARGET),i386-haiku)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-netbsd)
 ifeq ($(FULL_TARGET),i386-netbsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-solaris)
 ifeq ($(FULL_TARGET),i386-solaris)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-qnx)
 ifeq ($(FULL_TARGET),i386-qnx)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-netware)
 ifeq ($(FULL_TARGET),i386-netware)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-openbsd)
 ifeq ($(FULL_TARGET),i386-openbsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-wdosx)
 ifeq ($(FULL_TARGET),i386-wdosx)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-darwin)
 ifeq ($(FULL_TARGET),i386-darwin)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-emx)
 ifeq ($(FULL_TARGET),i386-emx)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-watcom)
 ifeq ($(FULL_TARGET),i386-watcom)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-netwlibc)
 ifeq ($(FULL_TARGET),i386-netwlibc)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-wince)
 ifeq ($(FULL_TARGET),i386-wince)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-embedded)
 ifeq ($(FULL_TARGET),i386-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-symbian)
 ifeq ($(FULL_TARGET),i386-symbian)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-linux)
 ifeq ($(FULL_TARGET),m68k-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-freebsd)
 ifeq ($(FULL_TARGET),m68k-freebsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-netbsd)
 ifeq ($(FULL_TARGET),m68k-netbsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-amiga)
 ifeq ($(FULL_TARGET),m68k-amiga)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-atari)
 ifeq ($(FULL_TARGET),m68k-atari)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-openbsd)
 ifeq ($(FULL_TARGET),m68k-openbsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-palmos)
 ifeq ($(FULL_TARGET),m68k-palmos)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),m68k-embedded)
 ifeq ($(FULL_TARGET),m68k-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc-linux)
 ifeq ($(FULL_TARGET),powerpc-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc-netbsd)
 ifeq ($(FULL_TARGET),powerpc-netbsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc-amiga)
 ifeq ($(FULL_TARGET),powerpc-amiga)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc-macos)
 ifeq ($(FULL_TARGET),powerpc-macos)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc-darwin)
 ifeq ($(FULL_TARGET),powerpc-darwin)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc-morphos)
 ifeq ($(FULL_TARGET),powerpc-morphos)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc-embedded)
 ifeq ($(FULL_TARGET),powerpc-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),sparc-linux)
 ifeq ($(FULL_TARGET),sparc-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),sparc-netbsd)
 ifeq ($(FULL_TARGET),sparc-netbsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),sparc-solaris)
 ifeq ($(FULL_TARGET),sparc-solaris)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),sparc-embedded)
 ifeq ($(FULL_TARGET),sparc-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),x86_64-linux)
 ifeq ($(FULL_TARGET),x86_64-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),x86_64-freebsd)
 ifeq ($(FULL_TARGET),x86_64-freebsd)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),x86_64-darwin)
 ifeq ($(FULL_TARGET),x86_64-darwin)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),x86_64-win64)
 ifeq ($(FULL_TARGET),x86_64-win64)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),x86_64-embedded)
 ifeq ($(FULL_TARGET),x86_64-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-linux)
 ifeq ($(FULL_TARGET),arm-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-palmos)
 ifeq ($(FULL_TARGET),arm-palmos)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-darwin)
 ifeq ($(FULL_TARGET),arm-darwin)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-wince)
 ifeq ($(FULL_TARGET),arm-wince)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-gba)
 ifeq ($(FULL_TARGET),arm-gba)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-nds)
 ifeq ($(FULL_TARGET),arm-nds)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-embedded)
 ifeq ($(FULL_TARGET),arm-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),arm-symbian)
 ifeq ($(FULL_TARGET),arm-symbian)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc64-linux)
 ifeq ($(FULL_TARGET),powerpc64-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc64-darwin)
 ifeq ($(FULL_TARGET),powerpc64-darwin)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),powerpc64-embedded)
 ifeq ($(FULL_TARGET),powerpc64-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),avr-embedded)
 ifeq ($(FULL_TARGET),avr-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),armeb-linux)
 ifeq ($(FULL_TARGET),armeb-linux)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),armeb-embedded)
 ifeq ($(FULL_TARGET),armeb-embedded)
-override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+override TARGET_UNITS+=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 endif
 endif
 ifeq ($(FULL_TARGET),i386-linux)
 ifeq ($(FULL_TARGET),i386-linux)
 override TARGET_RSTS+=dbwhtml dbconst dbcoll
 override TARGET_RSTS+=dbwhtml dbconst dbcoll

+ 1 - 1
packages/fcl-db/src/base/Makefile.fpc

@@ -6,7 +6,7 @@
 main=fcl-db
 main=fcl-db
 
 
 [target]
 [target]
-units=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll
+units=dbconst db dbwhtml bufdataset_parser bufdataset dbcoll sqlscript
 rsts=dbwhtml dbconst dbcoll
 rsts=dbwhtml dbconst dbcoll
 
 
 [require]
 [require]

+ 662 - 0
packages/fcl-db/src/base/sqlscript.pp

@@ -0,0 +1,662 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2008 by the Free Pascal development team
+
+    Abstract SQL scripting engine.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit sqlscript;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils; 
+
+type
+
+  TSQLScriptStatementEvent = procedure(Sender: TObject; Statement: TStrings; var StopExecution: Boolean) of object;
+  TSQLScriptDirectiveEvent = procedure(Sender: TObject; Directive, Argument: AnsiString; var StopExecution: Boolean) of object;
+  TSQLScriptExceptionEvent = procedure(Sender: TObject; Statement: TStrings; TheException: Exception; var Continue: boolean) of object;
+  TSQLSkipMode = (smNone, smIfBranch, smElseBranch, smAll);
+
+  { TCustomSQLScript }
+
+  TCustomSQLScript = class(TComponent)
+  private
+    FLine: Integer;
+    FCol: Integer;
+    FDefines: TStrings;
+    FOnException: TSQLScriptExceptionEvent;
+    FSkipMode: TSQLSkipMode;
+    FIsSkipping: Boolean;
+    FSkipStackIndex: Integer;
+    FSkipModeStack: array[0..255] of TSQLSkipMode;
+    FIsSkippingStack: array[0..255] of Boolean;
+    FAborted: Boolean;
+    FUseSetTerm, FUseDefines, FUseCommit,
+    FCommentsInSQL: Boolean;
+    FTerminator: AnsiString;
+    FSQL: TStrings;
+    FCurrentStatement: TStrings;
+    FDirectives: TStrings;
+    FEmitLine: Boolean;
+    procedure SetDefines(const Value: TStrings);
+    function FindNextSeparator(sep: array of string): AnsiString;
+    procedure AddToStatement(value: AnsiString; ForceNewLine : boolean);
+    procedure SetDirectives(value: TStrings);
+    procedure SetSQL(value: TStrings);
+    procedure SQLChange(Sender: TObject);
+    function GetLine: Integer;
+    Function ProcessConditional(Directive : String; Param : String) : Boolean; virtual;
+    function NextStatement: AnsiString;
+    procedure ProcessStatement;
+    function Available: Boolean;
+    procedure InternalStatement (Statement: TStrings; var StopExecution: Boolean);
+    procedure InternalDirective (Directive, Argument: String; var StopExecution: Boolean);
+    procedure InternalCommit;
+  protected
+    procedure DefaultDirectives; virtual;
+    procedure ExecuteStatement (Statement: TStrings; var StopExecution: Boolean); virtual; abstract;
+    procedure ExecuteDirective (Directive, Argument: String; var StopExecution: Boolean); virtual; abstract;
+    procedure ExecuteCommit; virtual; abstract;
+  public
+    constructor Create (AnOwner: TComponent); override;
+    destructor Destroy; override;
+    procedure Execute; virtual;
+  protected
+    property Aborted: Boolean read FAborted;
+    property Line: Integer read GetLine;
+    property CommentsInSQL: Boolean read FCommentsInSQL write FCommentsInSQL;
+    property UseSetTerm: Boolean read FUseSetTerm write FUseSetTerm;
+    property UseCommit: Boolean read FUseCommit write FUseCommit;
+    property UseDefines: Boolean read FUseDefines write FUseDefines;
+    property Defines : TStrings Read FDefines Write SetDefines;
+    property Directives: TStrings read FDirectives write SetDirectives;
+    property Script: TStrings read FSQL write SetSQL;  // script to execute
+    property Terminator: AnsiString read FTerminator write FTerminator;
+    property OnException : TSQLScriptExceptionEvent read FOnException write FOnException;
+  end;
+
+  { TEventSQLScript }
+
+  TEventSQLScript = class (TCustomSQLScript)
+  private
+    FAfterExec: TNotifyEvent;
+    FBeforeExec: TNotifyEvent;
+    FOnCommit: TNotifyEvent;
+    FOnSQLStatement: TSQLScriptStatementEvent;
+    FOnDirective: TSQLScriptDirectiveEvent;
+  protected
+    procedure ExecuteStatement (SQLStatement: TStrings; var StopExecution: Boolean); override;
+    procedure ExecuteDirective (Directive, Argument: String; var StopExecution: Boolean); override;
+    procedure ExecuteCommit; override;
+  public
+    procedure Execute; override;
+    property Aborted;
+    property Line;
+  published
+    property Directives;
+    property Defines;
+    property Script;
+    property Terminator;
+    property CommentsinSQL;
+    property UseSetTerm;
+    property UseCommit;
+    property UseDefines;
+    property OnException;
+    property OnSQLStatement: TSQLScriptStatementEvent read FOnSQLStatement write FOnSQLStatement;
+    property OnDirective: TSQLScriptDirectiveEvent read FOnDirective write FOnDirective;
+    property OnCommit: TNotifyEvent read FOnCommit write FOnCommit;
+    property BeforeExecute : TNotifyEvent read FBeforeExec write FBeforeExec;
+    property AfterExecute : TNotifyEvent read FAfterExec write FAfterExec;
+  end;
+
+  ESQLScript = Class(Exception);
+
+implementation
+
+Resourcestring
+ SErrIfXXXNestingLimitReached = '#IFDEF nesting limit reached';
+ SErrInvalidEndif = '#ENDIF without #IFDEF';
+ SErrInvalidElse  = '#ELSE without #IFDEF';
+
+{ ---------------------------------------------------------------------
+    Auxiliary Functions
+  ---------------------------------------------------------------------}
+  
+function StartsWith(S1, S2: AnsiString): Boolean;
+
+var
+  L1,L2 : Integer;
+
+begin
+  Result:=False;
+  L1:=Length(S1);
+  L2:=Length(S2);
+  if (L2=0) or (L1<L2) then
+    Exit;
+  Result:=(AnsiCompareStr(Copy(s1,1,L2),S2)=0);
+  Result := Result and ((L2 = L1) or (s1[L2+1] = ' '));
+end;
+
+function GetFirstSeparator(S: AnsiString; Sep: array of string): AnsiString;
+
+var
+  i, C, M: Integer;
+
+begin
+  M:=length(S) + 1;
+  Result:='';
+  for i:=0 to high(Sep) do
+    begin
+    C:=Pos(Sep[i],S);
+    if (C<>0) and (C<M) then
+      begin
+      M:=C;
+      Result:=Sep[i];
+      end;
+    end;
+end;
+
+Function ConvertWhiteSpace(S : String) : String;
+
+begin
+  Result:=StringReplace(S,#13,' ',[rfReplaceAll]);
+  Result:=StringReplace(Result,#10,' ',[rfReplaceAll]);
+  Result:=Trim(Result);
+end;
+
+function DeleteComments(SQL_Text: AnsiString; ATerminator: AnsiString = ';'): AnsiString;
+
+begin
+  With TCustomSQLScript.Create (Nil) do
+    try
+      Terminator:=ATerminator;
+      Script.Add(SQL_Text);
+      Script.Add(Terminator);
+      CommentsInSQL:=False;
+      Result:=ConvertWhiteSpace(NextStatement);
+    finally
+      Free;
+    end;
+end;
+
+{ ---------------------------------------------------------------------
+    TSQLScript
+  ---------------------------------------------------------------------}
+
+procedure TCustomSQLScript.SQLChange(Sender: TObject);
+begin
+  FLine:=1;
+  FCol:=1;
+end;
+
+procedure TCustomSQLScript.SetDirectives(value: TStrings);
+
+var 
+  i : Integer;
+  S : AnsiString;
+  
+begin
+  FDirectives.Clear();
+  if (Value<>Nil) then
+    begin
+    for i:=0 to value.Count - 1 do
+      begin
+      S:=UpperCase(ConvertWhiteSpace(value[i]));
+      if Length(S)>0 then 
+        FDirectives.Add(S);
+      end;
+    end;
+  DefaultDirectives;
+end;
+
+procedure TCustomSQLScript.SetSQL(value: TStrings);
+begin
+  FSQL.Assign(value);
+  FLine:=1;
+  FCol:=1;
+end;
+
+function TCustomSQLScript.GetLine: Integer;
+begin
+  Result:=FLine - 1;
+end;
+
+procedure TCustomSQLScript.AddToStatement(value: AnsiString; ForceNewLine : Boolean);
+
+begin
+  With FCurrentStatement do
+    if ForceNewLine or (Count=0) then
+      Add(value)
+    else
+      Strings[Count-1]:=Strings[Count-1] + value;
+end;
+
+function TCustomSQLScript.FindNextSeparator(Sep: array of string): AnsiString;
+
+var
+  S: AnsiString;
+
+begin
+  Result:='';
+  while (FLine<=FSQL.Count) do
+    begin
+    S:=FSQL.Strings[FLine-1];
+    if (FCol>1) then
+      begin
+      S:=Copy(S,FCol,length(S));
+      end;
+    Result:=GetFirstSeparator(S,Sep);
+    if (Result='') then
+      begin
+      if FEmitLine then
+        AddToStatement(S,(FCol=1));
+      FCol:=1;
+      FLine:=FLine+1;
+      end
+    else
+      begin
+      if FEmitLine then
+        AddToStatement(Copy(S,1,Pos(Result,S)-1),(FCol=1));
+      FCol:=(FCol-1)+Pos(Result,S);
+      break;
+      end;
+    end;
+end;
+
+function TCustomSQLScript.Available: Boolean;
+
+var 
+  SCol, 
+  SLine: Integer;
+  
+begin
+  SCol:=FCol;
+  SLine:=FLine;
+  try
+    Result:=Length(Trim(NextStatement()))>0;
+  Finally  
+    FCol:=SCol;
+    FLine:=SLine;
+  end;  
+end;
+
+procedure TCustomSQLScript.InternalStatement(Statement: TStrings;  var StopExecution: Boolean);
+
+var 
+  cont : boolean;
+  
+begin
+  try
+    ExecuteStatement(Statement, StopExecution);
+  except
+    on E : Exception do
+      begin
+      cont := false;
+      if assigned (FOnException) then
+        FOnException (self, Statement, E, cont);
+      if not cont then
+        Raise;
+      end;
+  end;
+end;
+
+procedure TCustomSQLScript.InternalDirective(Directive, Argument: String;  var StopExecution: Boolean);
+
+var 
+  cont : boolean;
+  l : TStrings;
+  
+begin
+  try
+    ExecuteDirective(Directive, Argument, StopExecution);
+  except
+    on E : Exception do
+      begin
+      cont := false;
+      if assigned (FOnException) then
+        begin
+        l := TStringlist.Create;
+        try
+          L.Add(Directive);
+          if Argument <> '' then
+            L.Add(Argument);
+          FOnException (self, l, E, cont);
+        finally
+          L.Free;
+        end;
+        end;
+      if not cont then
+        Raise;
+      end;
+  end;
+end;
+
+procedure TCustomSQLScript.InternalCommit;
+
+var 
+  cont : boolean;
+  l : TStrings;
+  
+begin
+  try
+    ExecuteCommit;
+  except
+    on E : Exception do
+      begin
+      cont := false;
+      if assigned (FOnException) then
+        begin
+        l := TStringlist.Create;
+        try
+          L.Add('COMMIT');
+          FOnException (self, l, E, cont);
+        finally
+          L.Free;
+        end;
+        end;
+      if not cont then
+        Raise;
+      end;
+  end;
+end;
+
+procedure TCustomSQLScript.ProcessStatement;
+
+Var
+  S,
+  Directive : String;
+  I : longint;
+
+begin
+  if (FCurrentStatement.Count=0) then
+    Exit;
+  S:=DeleteComments(FCurrentStatement.Text, Terminator);
+  I:=0;
+  Directive:='';
+  While (i<FDirectives.Count) and (Directive='') do
+    begin
+    If StartsWith(AnsiUpperCase(S), FDirectives[i]) Then
+      Directive:=FDirectives[i];
+    Inc(I);
+    end;
+  If (Directive<>'') then
+    begin
+    S:=Trim(Copy(S,Length(Directive)+1,length(S)));
+    If (Directive[1]='#') then
+      begin
+      if not FUseDefines or not ProcessConditional(Directive,S) then
+        if Not FIsSkipping then
+          InternalDirective (Directive, S, FAborted);
+      end
+    else If Not FIsSkipping then
+      begin
+      if FUseCommit and (Directive = 'COMMIT') then
+        InternalCommit
+      else if FUseSetTerm and (Directive = 'SET TERM') then
+        FTerminator:=S
+      else
+        InternalDirective (Directive,S,FAborted)
+      end
+    end
+  else
+    if (not FIsSkipping) then
+      InternalStatement(FCurrentStatement,FAborted);
+end;
+
+procedure TCustomSQLScript.Execute;
+
+begin
+  FSkipMode:=smNone;
+  FIsSkipping:=False;
+  FSkipStackIndex:=0;
+  Faborted:=False;
+  DefaultDirectives;
+  while not FAborted and Available() do
+    begin
+    NextStatement();
+    ProcessStatement;
+    end;
+end;
+
+function TCustomSQLScript.NextStatement: AnsiString;
+
+var
+  pnt: AnsiString;
+  terminator_found: Boolean;
+
+begin
+  terminator_found:=False;
+  FCurrentStatement.Clear;
+  while FLine <= FSQL.Count do
+    begin
+    pnt:=FindNextSeparator([FTerminator, '/*', '"', '''']);
+    if (pnt=FTerminator) then
+      begin
+      FCol:=FCol + length(pnt);
+      terminator_found:=True;
+      break;
+      end
+    else if pnt = '/*' then
+      begin
+      if FCommentsInSQL then
+        AddToStatement(pnt,false)
+      else
+        FEmitLine:=False;
+      FCol:=FCol + length(pnt);
+      pnt:=FindNextSeparator(['*/']);
+      if FCommentsInSQL then
+        AddToStatement(pnt,false)
+      else
+        FEmitLine:=True;
+      FCol:=FCol + length(pnt);
+      end
+    else if pnt = '"' then
+      begin
+      AddToStatement(pnt,false);
+      FCol:=FCol + length(pnt);
+      pnt:=FindNextSeparator(['"']);
+      AddToStatement(pnt,false);
+      FCol:=FCol + length(pnt);
+      end
+    else if pnt = '''' then
+      begin
+      AddToStatement(pnt,False);
+      FCol:=FCol + length(pnt);
+      pnt:=FindNextSeparator(['''']);
+      AddToStatement(pnt,false);
+      FCol:=FCol + length(pnt);
+      end;
+    end;
+  if not terminator_found then
+    FCurrentStatement.Clear();
+  while (FCurrentStatement.Count > 0) and (trim(FCurrentStatement.Strings[0]) = '') do
+    FCurrentStatement.Delete(0);
+  Result:=FCurrentStatement.Text;
+end;
+
+Constructor TCustomSQLScript.Create (AnOwner: TComponent);
+
+Var
+  L : TStringList;
+
+begin
+  inherited;
+  L:=TStringList.Create;
+  With L do
+    begin
+    Sorted:=True;
+    Duplicates:=dupIgnore;
+    end;
+  FDefines:=L;  
+  FCommentsInSQL:=True;
+  FTerminator:=';';
+  L:=TStringList.Create();
+  L.OnChange:=@SQLChange;
+  FSQL:=L;
+  FDirectives:=TStringList.Create();
+  FCurrentStatement:=TStringList.Create();
+  FLine:=1;
+  FCol:=1;
+  FEmitLine:=True;
+  FUseCommit := true;
+  FUseDefines := True;
+  FUseSetTerm := True;
+  DefaultDirectives;
+end;
+
+destructor TCustomSQLScript.Destroy;
+begin
+  FreeAndNil(FCurrentStatement);
+  FreeAndNil(FSQL);
+  FreeAndNil(FDirectives);
+  FreeAndNil(FDefines);
+  inherited Destroy;
+end;
+
+procedure TCustomSQLScript.SetDefines(const Value: TStrings);
+begin
+  FDefines.Assign(Value);
+end;
+
+procedure TCustomSQLScript.DefaultDirectives;
+begin
+  With FDirectives do
+    begin
+    if FUseSetTerm then
+      Add('SET TERM');
+    if FUseCommit then
+      Add('COMMIT');
+    if FUseDefines then
+      begin
+      Add('#IFDEF');
+      Add('#IFNDEF');
+      Add('#ELSE');
+      Add('#ENDIF');
+      Add('#DEFINE');
+      Add('#UNDEF');
+      Add('#UNDEFINE');
+      end;
+    end;
+end;
+
+Function TCustomSQLScript.ProcessConditional(Directive: String; Param : String) : Boolean;
+
+  Procedure PushSkipMode;
+
+  begin
+    if FSkipStackIndex=High(FSkipModeStack) then
+      Raise ESQLScript.Create(SErrIfXXXNestingLimitReached);
+    FSkipModeStack[FSkipStackIndex]:=FSkipMode;
+    FIsSkippingStack[FSkipStackIndex]:=FIsSkipping;
+    Inc(FSkipStackIndex);
+  end;
+
+  Procedure PopSkipMode;
+
+  begin
+    if FSkipStackIndex = 0 then
+      Raise ESQLScript.Create(SErrInvalidEndif);
+    Dec(FSkipStackIndex);
+    FSkipMode := FSkipModeStack[FSkipStackIndex];
+    FIsSkipping := FIsSkippingStack[FSkipStackIndex];
+  end;
+
+Var
+  Index : Integer;
+
+begin
+  Result:=True;
+  if (Directive='#DEFINE') then
+    begin
+    if not FIsSkipping then
+      FDefines.Add(Param);
+    end
+  else if (Directive='#UNDEF') or (Directive='#UNDEFINE') then
+    begin
+    if not FIsSkipping then
+      begin
+      Index:=FDefines.IndexOf(Param);
+      if (Index>=0) then
+        FDefines.Delete(Index);
+      end;
+    end
+  else if (Directive='#IFDEF') or (Directive='#IFNDEF') then
+    begin
+    PushSkipMode;
+    if FIsSkipping then
+      begin
+      FSkipMode:=smAll;
+      FIsSkipping:=true;
+      end
+    else
+      begin
+      Index:=FDefines.IndexOf(Param);
+      if ((Directive='#IFDEF') and (Index<0)) or
+         ((Directive='#IFNDEF') and (Index>=0)) then
+        begin
+        FSkipMode:=smIfBranch;
+        FIsSkipping:=true;
+        end
+      else
+        FSkipMode := smElseBranch;
+      end;
+    end
+  else if (Directive='#ELSE') then
+    begin
+    if (FSkipStackIndex=0) then
+      Raise ESQLScript.Create(SErrInvalidElse);
+    if (FSkipMode=smIfBranch) then
+      FIsSkipping:=false
+    else if (FSkipMode=smElseBranch) then
+      FIsSkipping:=true;
+    end
+  else if (Directive='#ENDIF') then
+    PopSkipMode
+  else
+    Result:=False;
+end;
+
+{ TEventSQLScript }
+
+procedure TEventSQLScript.ExecuteStatement(SQLStatement: TStrings;
+  var StopExecution: Boolean);
+begin
+  if assigned (FOnSQLStatement) then
+    FOnSQLStatement (self, SQLStatement, StopExecution);
+end;
+
+procedure TEventSQLScript.ExecuteDirective(Directive, Argument: String;
+  var StopExecution: Boolean);
+begin
+  if assigned (FOnDirective) then
+    FOnDirective (Self, Directive, Argument, StopExecution);
+end;
+
+procedure TEventSQLScript.ExecuteCommit;
+begin
+  if assigned (FOnCommit) then
+    FOnCommit (Self);
+end;
+
+procedure TEventSQLScript.Execute;
+begin
+  if assigned (FBeforeExec) then
+    FBeforeExec (Self);
+  inherited Execute;
+  if assigned (FAfterExec) then
+    FAfterExec (Self);
+end;
+
+end.
+

+ 77 - 0
packages/fcl-db/src/codegen/buildddcg.lpi

@@ -0,0 +1,77 @@
+<?xml version="1.0"?>
+<CONFIG>
+  <ProjectOptions>
+    <PathDelim Value="/"/>
+    <Version Value="6"/>
+    <General>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <IconPath Value="./"/>
+      <TargetFileExt Value=""/>
+    </General>
+    <VersionInfo>
+      <ProjectVersion Value=""/>
+    </VersionInfo>
+    <PublishOptions>
+      <Version Value="2"/>
+      <IgnoreBinaries Value="False"/>
+      <IncludeFileFilter Value="*.(pas|pp|inc|lfm|lpr|lrs|lpi|lpk|sh|xml)"/>
+      <ExcludeFileFilter Value="*.(bak|ppu|ppw|o|so);*~;backup"/>
+    </PublishOptions>
+    <RunParams>
+      <local>
+        <FormatVersion Value="1"/>
+        <LaunchingApplication PathPlusParams="/usr/X11R6/bin/xterm -T 'Lazarus Run Output' -e $(LazarusDir)/tools/runwait.sh $(TargetCmdLine)"/>
+      </local>
+    </RunParams>
+    <Units Count="7">
+      <Unit0>
+        <Filename Value="buildddcg.lpr"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="buildddcg"/>
+      </Unit0>
+      <Unit1>
+        <Filename Value="fpddpopcode.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpddpopcode"/>
+      </Unit1>
+      <Unit2>
+        <Filename Value="fpcgcreatedbf.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpcgcreatedbf"/>
+      </Unit2>
+      <Unit3>
+        <Filename Value="fpcgdbcoll.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpcgdbcoll"/>
+      </Unit3>
+      <Unit4>
+        <Filename Value="fpcgsqlconst.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpcgsqlconst"/>
+      </Unit4>
+      <Unit5>
+        <Filename Value="fpcgtiopf.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpcgtiopf"/>
+      </Unit5>
+      <Unit6>
+        <Filename Value="fpddcodegen.pp"/>
+        <IsPartOfProject Value="True"/>
+        <UnitName Value="fpddcodegen"/>
+      </Unit6>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="5"/>
+    <SearchPaths>
+      <UnitOutputDirectory Value="../../units/$(TARGETCPU)-$(TARGETOS)"/>
+    </SearchPaths>
+    <CodeGeneration>
+      <Generate Value="Faster"/>
+    </CodeGeneration>
+    <Other>
+      <CompilerPath Value="$(CompPath)"/>
+    </Other>
+  </CompilerOptions>
+</CONFIG>

+ 15 - 0
packages/fcl-db/src/codegen/buildddcg.lpr

@@ -0,0 +1,15 @@
+program buildddcg;
+
+{$mode objfpc}{$H+}
+
+uses
+  {$IFDEF UNIX}{$IFDEF UseCThreads}
+  cthreads,
+  {$ENDIF}{$ENDIF}
+  Classes
+  { you can add units after this }, fpddpopcode, fpcgcreatedbf, fpcgdbcoll,
+  fpcgsqlconst, fpcgtiopf, fpddcodegen;
+
+begin
+end.
+

+ 279 - 103
packages/fcl-db/src/codegen/fpcgtiopf.pp

@@ -23,7 +23,7 @@ uses
   Classes, SysUtils, db, fpddcodegen;
   Classes, SysUtils, db, fpddcodegen;
   
   
 TYpe
 TYpe
-  TClassOption = (caConstructor,caDestructor,caCreateList,caListAddMethod,caListItemsProperty);
+  TClassOption = (caCreateClass,caConstructor,caDestructor,caCreateList,caListAddMethod,caListItemsProperty);
   TClassOptions = Set of TClassOption;
   TClassOptions = Set of TClassOption;
   TVisitorOption = (voRead,voReadList,voCreate,voDelete,voUpdate);
   TVisitorOption = (voRead,voReadList,voCreate,voDelete,voUpdate);
   TVisitorOptions = set of TVisitorOption;
   TVisitorOptions = set of TVisitorOption;
@@ -61,13 +61,16 @@ TYpe
     procedure DeclareObjectvariable(Strings: TStrings;
     procedure DeclareObjectvariable(Strings: TStrings;
       const ObjectClassName: String);
       const ObjectClassName: String);
   private
   private
+    Function CreateSQLStatement(V: TVisitorOption) : String;
     function GetOpt: TTiOPFCodeOptions;
     function GetOpt: TTiOPFCodeOptions;
     procedure WriteCreateVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteCreateVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteDeleteVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteDeleteVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteFieldAssign(Strings: TStrings; F: TFieldPropDef);
     procedure WriteFieldAssign(Strings: TStrings; F: TFieldPropDef);
-    procedure WriteParamAssign(Strings: TStrings; F: TFieldPropDef);
+    procedure WriteAssignToParam(Strings: TStrings; F: TFieldPropDef);
     procedure WriteReadListVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteReadListVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteReadVisitor(Strings: TStrings; const ObjectClassName: String );
     procedure WriteReadVisitor(Strings: TStrings; const ObjectClassName: String );
+    procedure WriteSetSQL(Strings: TStrings; const ASQL: String);
+    procedure WriteSQLConstants(Strings: TStrings);
     procedure WriteUpdateVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteUpdateVisitor(Strings: TStrings; const ObjectClassName: String);
     procedure WriteVisitorDeclaration(Strings: TStrings; V: TVisitorOption; const ObjectClassName: String);
     procedure WriteVisitorDeclaration(Strings: TStrings; V: TVisitorOption; const ObjectClassName: String);
     procedure WriteVisitorImplementation(Strings: TStrings; V: TVisitorOption; const ObjectClassName: String);
     procedure WriteVisitorImplementation(Strings: TStrings; V: TVisitorOption; const ObjectClassName: String);
@@ -75,6 +78,7 @@ TYpe
     // Not to be overridden.
     // Not to be overridden.
     procedure WriteListAddObject(Strings: TStrings; const ListClassName, ObjectClassName: String);
     procedure WriteListAddObject(Strings: TStrings; const ListClassName, ObjectClassName: String);
     // Overrides of parent objects
     // Overrides of parent objects
+    function AllowPropertyDeclaration(F: TFieldPropDef; AVisibility: TVisibilities): Boolean; override;
     Function GetInterfaceUsesClause : string; override;
     Function GetInterfaceUsesClause : string; override;
     Procedure DoGenerateInterface(Strings: TStrings); override;
     Procedure DoGenerateInterface(Strings: TStrings); override;
     Procedure DoGenerateImplementation(Strings: TStrings); override;
     Procedure DoGenerateImplementation(Strings: TStrings); override;
@@ -92,6 +96,9 @@ TYpe
     Property TiOPFOptions : TTiOPFCodeOptions Read GetOpt;
     Property TiOPFOptions : TTiOPFCodeOptions Read GetOpt;
   end;
   end;
 
 
+Const
+  SOID = 'OID'; // OID property.
+  
 implementation
 implementation
 
 
 { TTiOPFCodeOptions }
 { TTiOPFCodeOptions }
@@ -118,7 +125,7 @@ end;
 constructor TTiOPFCodeOptions.Create;
 constructor TTiOPFCodeOptions.Create;
 begin
 begin
   inherited Create;
   inherited Create;
-  FListAncestorName:='TObjectList';
+  FListAncestorName:='TTiObjectList';
   AncestorClass:='TTiObject';
   AncestorClass:='TTiObject';
   ObjectClassName:='MyObject';
   ObjectClassName:='MyObject';
   FVisitorOptions:=[voRead,voCreate,voDelete,voUpdate];
   FVisitorOptions:=[voRead,voCreate,voDelete,voUpdate];
@@ -179,7 +186,7 @@ begin
   Result:=inherited GetInterfaceUsesClause;
   Result:=inherited GetInterfaceUsesClause;
   If (Result<>'') then
   If (Result<>'') then
     Result:=Result+',';
     Result:=Result+',';
-  Result:=Result+'tiVisitor, tiObject';
+  Result:=Result+'tiVisitor, tiVisitorDB, tiObject';
 end;
 end;
 
 
 procedure TTiOPFCodeGenerator.DoGenerateInterface(Strings: TStrings);
 procedure TTiOPFCodeGenerator.DoGenerateInterface(Strings: TStrings);
@@ -188,7 +195,13 @@ Var
   V : TVisitorOption;
   V : TVisitorOption;
 
 
 begin
 begin
-  inherited DoGenerateInterface(Strings);
+  If (caCreateClass in TiOPFOptions.ClassOptions) then
+    inherited DoGenerateInterface(Strings)
+  else
+    begin
+    Addln(Strings,'Type');
+    Addln(Strings);
+    end;
   With TiOPFOptions do
   With TiOPFOptions do
     begin
     begin
     IncIndent;
     IncIndent;
@@ -247,6 +260,110 @@ begin
   AddlN(Strings);
   AddlN(Strings);
 end;
 end;
 
 
+Function TTiOPFCodeGenerator.CreateSQLStatement(V : TVisitorOption) : String;
+
+  Function AddToS(Const S,Add : String) : string;
+  
+  begin
+    Result:=S;
+    If (Result<>'') then
+      Result:=Result+', ';
+    Result:=Result+Add;
+  end;
+
+Var
+  I : integer;
+  W,S,VS,TN : String;
+  F : TFieldPropDef;
+
+begin
+  TN:='MyTable';
+  S:='';
+  VS:='';
+  W:='Your condition here';
+  Result:='';
+  Case V of
+   voRead,
+   voReadList : begin
+                Result:='SELECT ';
+                For I:=0 to Fields.Count-1 do
+                  begin
+                  F:=Fields[i];
+                  If F.Enabled then
+                    begin
+                    S:=AddToS(S,F.FieldName);
+                    If (V=voRead) and (F.PropertyName=SOID) then
+                      W:=Format('%s = :%s',[F.FieldName,F.FieldName]);
+                    end;
+                  end;
+                Result:=Result+S+Format(' FROM %s WHERE (%s);',[TN,W]);
+                end;
+   voCreate : begin
+              Result:=Format('INSERT INTO %s (',[TN]);
+              For I:=0 to Fields.Count-1 do
+                begin
+                F:=Fields[i];
+                If F.Enabled then
+                  begin
+                  S:=AddToS(S,F.FieldName);
+                  VS:=AddToS(VS,':'+F.FieldName);
+                  end;
+                end;
+              Result:=Result+S+') VALUES ('+VS+');';
+              end;
+   voDelete : begin
+              For I:=0 to Fields.Count-1 do
+                begin
+                F:=Fields[i];
+                If (F.PropertyName=SOID) then
+                  W:=Format('%s = :%s',[F.FieldName,F.FieldName]);
+                end;
+              Result:=Format('DELETE FROM %s WHERE (%s);',[TN,W]);
+              end;
+   voUpdate : begin
+              Result:=Format('UPDATE %s SET ',[TN]);
+              For I:=0 to Fields.Count-1 do
+                 begin
+                  F:=Fields[i];
+                  If F.Enabled then
+                    If (F.PropertyName=SOID) then
+                      W:=Format('%s = :%s',[F.FieldName,F.FieldName])
+                    else
+                      S:=AddToS(S,F.FieldName+' = :'+F.FieldName);
+                  end;
+              Result:=Result+S+Format(' WHERE (%s);',[W]);
+              end;
+  end;
+end;
+
+procedure TTiOPFCodeGenerator.WriteSQLConstants(Strings : TStrings);
+
+Const
+  VisSQL : Array [TVisitorOption] of string
+         = ('Read','ReadList','Create','Delete','Update');
+
+Var
+  OCN,S : String;
+  V : TVisitorOption;
+
+begin
+  AddLn(Strings,'Const');
+  IncIndent;
+  try
+    OCN:=StripType(TiOPFOptions.ObjectClassName);
+    For V:=Low(TVisitorOption) to High(TVisitorOption) do
+      If V in TiOPFOptions.VisitorOptions then
+        begin
+        S:=CreateSQLStatement(V);
+        S:=Format('SQL%s%s = ''%s'';',[VisSQL[V],OCN,S]);
+        AddLn(Strings,S);
+        end;
+  finally
+    DecIndent;
+  end;
+  AddLn(Strings,'');
+end;
+
 
 
 procedure TTiOPFCodeGenerator.DoGenerateImplementation(Strings: TStrings);
 procedure TTiOPFCodeGenerator.DoGenerateImplementation(Strings: TStrings);
 
 
@@ -254,9 +371,12 @@ Var
   V : TVisitorOption;
   V : TVisitorOption;
 
 
 begin
 begin
-  inherited DoGenerateImplementation(Strings);
+  If (caCreateClass in TiOPFOptions.ClassOptions) then
+    inherited DoGenerateImplementation(Strings);
   With TiOPFOptions do
   With TiOPFOptions do
     begin
     begin
+    If (VisitorOptions<>[])   then
+      WriteSQLConstants(Strings);
     If caCreateList in ClassOptions then
     If caCreateList in ClassOptions then
       CreateListImplementation(Strings,ObjectClassName,ListClassName);
       CreateListImplementation(Strings,ObjectClassName,ListClassName);
     For V:=Low(TVisitorOption) to High(TVisitorOption) do
     For V:=Low(TVisitorOption) to High(TVisitorOption) do
@@ -308,9 +428,9 @@ begin
   If DeclareObject Then
   If DeclareObject Then
     DeclareObjectVariable(Strings,ObjectClassName);
     DeclareObjectVariable(Strings,ObjectClassName);
   AddLn(Strings,'begin');
   AddLn(Strings,'begin');
+  IncIndent;
   If DeclareObject Then
   If DeclareObject Then
     Addln(Strings,'O:=%s(Visited);',[ObjectClassName]);
     Addln(Strings,'O:=%s(Visited);',[ObjectClassName]);
-  IncIndent;
 end;
 end;
 
 
 Procedure TTiOPFCodeGenerator.DeclareObjectvariable(Strings : TStrings; Const ObjectClassName : String);
 Procedure TTiOPFCodeGenerator.DeclareObjectvariable(Strings : TStrings; Const ObjectClassName : String);
@@ -343,16 +463,19 @@ end;
 procedure TTiOPFCodeGenerator.WriteReadVisitor(Strings : TStrings; Const ObjectClassName : String);
 procedure TTiOPFCodeGenerator.WriteReadVisitor(Strings : TStrings; Const ObjectClassName : String);
 
 
 Var
 Var
-  C,S : String;
+  OCN,CS,C,S : String;
   I : Integer;
   I : Integer;
+  F : TFieldPropDef;
 
 
 begin
 begin
-  C:=Format('TRead%sVisitor',[StripType(ObjectClassName)]);
+  OCN:=StripType(ObjectClassName);
+  CS:=Format('SQLRead%s',[OCN]);
+  C:=Format('TRead%sVisitor',[OCN]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings);
   Addln(Strings);
   // Init
   // Init
   S:=BeginInit(Strings,C);
   S:=BeginInit(Strings,C);
-  Addln(Strings,'Query.SQL.Text:=SQLReadList;');
+  WriteSetSQL(Strings,CS);
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // AcceptVisitor
   // AcceptVisitor
@@ -360,12 +483,17 @@ begin
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // AcceptSetupParams
   // AcceptSetupParams
-  S:=BeginSetupParams(Strings,C,'',False);
-  AddLn(Strings,'// Set up as needed');
+  F:=Fields.FindPropName('OID');
+  S:=BeginSetupParams(Strings,C,ObjectClassName,F<>Nil);
+  If (F<>Nil) then
+    WriteAssignToParam(Strings,F)
+  else
+    AddLn(Strings,'// Set up as needed');
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // MapRowToObject
   // MapRowToObject
   S:=BeginMapRowToObject(Strings,C,ObjectClassName);
   S:=BeginMapRowToObject(Strings,C,ObjectClassName);
+  Addln(Strings,'O:=%s(Visited);',[ObjectClassName]);
   Addln(Strings,'With Query do',[ObjectClassName]);
   Addln(Strings,'With Query do',[ObjectClassName]);
   IncINdent;
   IncINdent;
   try
   try
@@ -373,7 +501,7 @@ begin
     For I:=0 to Fields.Count-1 do
     For I:=0 to Fields.Count-1 do
       If Fields[i].Enabled then
       If Fields[i].Enabled then
         WriteFieldAssign(Strings,Fields[i]);
         WriteFieldAssign(Strings,Fields[i]);
-    Addln(Strings,'end');
+    Addln(Strings,'end;');
   finally
   finally
     DecIndent;
     DecIndent;
   end;
   end;
@@ -390,43 +518,46 @@ begin
   PN:=F.PropertyName;
   PN:=F.PropertyName;
   FN:=F.FieldName;
   FN:=F.FieldName;
   SFN:=CreateString(FN);
   SFN:=CreateString(FN);
-  Case F.PropertyType of
-    ptBoolean :
-      S:='AsBoolean';
-    ptShortint, ptByte,
-    ptSmallInt, ptWord,
-    ptLongint, ptCardinal :
-      S:='AsInteger';
-    ptInt64, ptQWord:
-      If F.FieldType=ftLargeInt then
-        R:=Format('O.%s:=(FieldByName(%s) as TLargeIntField).AsLargeInt;',[PN,SFN])
-      else
+  If (PN=SOID) then
+    R:=Format('O.OID.AssignFromTIQuery(''%s'',Query);',[FN])
+  else
+    Case F.PropertyType of
+      ptBoolean :
+        S:='AsBoolean';
+      ptShortint, ptByte,
+      ptSmallInt, ptWord,
+      ptLongint, ptCardinal :
         S:='AsInteger';
         S:='AsInteger';
-    ptShortString, ptAnsiString, ptWideString :
-      S:='AsString';
-    ptSingle, ptDouble, ptExtended, ptComp :
-      S:='AsFloat';
-    ptCurrency :
-      S:='AsCurrency';
-    ptDateTime :
-      S:='AsDateTime';
-    ptEnumerated :
-      R:=Format('Integer(O.%s):=FieldAsInteger[%s];',[PN,SFN]);
-    ptSet :
-      S:=Format('// Add custom set loading code here for %s from %s',[PN,FN]);
-    ptStream :
-      R:=Format('FieldByName(%s).SaveToStream(O.%s);',[SFN,PN]);
-    ptTStrings :
-      R:=Format('O.%s.Text:=FieldAsString[%s];',[PN,SFN]);
-    ptCustom :
-      R:=Format('// Add custom loading code here for %s from %s',[PN,FN]);
-  end;
+      ptInt64, ptQWord:
+        If F.FieldType=ftLargeInt then
+          R:=Format('O.%s:=(FieldByName(%s) as TLargeIntField).AsLargeInt;',[PN,SFN])
+        else
+          S:='AsInteger';
+      ptShortString, ptAnsiString, ptWideString :
+        S:='AsString';
+      ptSingle, ptDouble, ptExtended, ptComp :
+        S:='AsFloat';
+      ptCurrency :
+        S:='AsCurrency';
+      ptDateTime :
+        S:='AsDateTime';
+      ptEnumerated :
+        R:=Format('Integer(O.%s):=FieldAsInteger[%s];',[PN,SFN]);
+      ptSet :
+        S:=Format('// Add custom set loading code here for %s from %s',[PN,FN]);
+      ptStream :
+        R:=Format('FieldByName(%s).SaveToStream(O.%s);',[SFN,PN]);
+      ptTStrings :
+        R:=Format('O.%s.Text:=FieldAsString[%s];',[PN,SFN]);
+      ptCustom :
+        R:=Format('// Add custom loading code here for %s from %s',[PN,FN]);
+    end;
   If (S<>'') then
   If (S<>'') then
     R:=Format('O.%s:=Field%s[%s];',[PN,S,SFN]);
     R:=Format('O.%s:=Field%s[%s];',[PN,S,SFN]);
   AddLn(Strings,R);
   AddLn(Strings,R);
 end;
 end;
 
 
-procedure TTiOPFCodeGenerator.WriteParamAssign(Strings : TStrings; F : TFieldPropDef);
+procedure TTiOPFCodeGenerator.WriteAssignToParam(Strings : TStrings; F : TFieldPropDef);
 
 
 Var
 Var
   PN,FN,SFN,R,S : String;
   PN,FN,SFN,R,S : String;
@@ -435,39 +566,42 @@ begin
   PN:=F.PropertyName;
   PN:=F.PropertyName;
   FN:=F.FieldName;
   FN:=F.FieldName;
   SFN:=CreateString(FN);
   SFN:=CreateString(FN);
-  Case F.PropertyType of
-    ptBoolean :
-      S:='AsBoolean';
-    ptShortint, ptByte,
-    ptSmallInt, ptWord,
-    ptLongint, ptCardinal :
-      S:='AsInteger';
-    ptInt64, ptQWord:
-      If F.FieldType=ftLargeInt then
-        R:=Format('O.%s:=(Name(%s) as TLargeIntField).AsLargeInt;',[PN,SFN])
-      else
+  If (PN=SOID) then
+    R:=Format('O.OID.AssignToTIQuery(''%s'',Query);',[FN])
+  else
+    Case F.PropertyType of
+      ptBoolean :
+        S:='AsBoolean';
+      ptShortint, ptByte,
+      ptSmallInt, ptWord,
+      ptLongint, ptCardinal :
         S:='AsInteger';
         S:='AsInteger';
-    ptShortString, ptAnsiString, ptWideString :
-      S:='AsString';
-    ptSingle, ptDouble, ptExtended, ptComp :
-      S:='AsFloat';
-    ptCurrency :
-      S:='AsCurrency';
-    ptDateTime :
-      S:='AsDateTime';
-    ptEnumerated :
-      R:=Format('ParamAsInteger[%s]:=Integer(O.%s);',[SFN,PN]);
-    ptSet :
-      S:=Format('// Add custom set loading code here for %s from %s',[PN,FN]);
-    ptStream :
-      R:=Format('AssignParamFromStream(%s,%s);',[SFN,PN]);
-    ptTStrings :
-      R:=Format('ParamAsString[%s]:=O.%s.Text;',[SFN,PN]);
-    ptCustom :
-      R:=Format('// Add custom loading code here for %s from %s',[PN,FN]);
-  end;
+      ptInt64, ptQWord:
+        If F.FieldType=ftLargeInt then
+          R:=Format('O.%s:=(Name(%s) as TLargeIntField).AsLargeInt;',[PN,SFN])
+        else
+          S:='AsInteger';
+      ptShortString, ptAnsiString, ptWideString :
+        S:='AsString';
+      ptSingle, ptDouble, ptExtended, ptComp :
+        S:='AsFloat';
+      ptCurrency :
+        S:='AsCurrency';
+      ptDateTime :
+        S:='AsDateTime';
+      ptEnumerated :
+        R:=Format('ParamAsInteger[%s]:=Integer(O.%s);',[SFN,PN]);
+      ptSet :
+        S:=Format('// Add custom set loading code here for %s from %s',[PN,FN]);
+      ptStream :
+        R:=Format('AssignParamFromStream(%s,%s);',[SFN,PN]);
+      ptTStrings :
+        R:=Format('ParamAsString[%s]:=O.%s.Text;',[SFN,PN]);
+      ptCustom :
+        R:=Format('// Add custom loading code here for %s from %s',[PN,FN]);
+    end;
   If (S<>'') then
   If (S<>'') then
-    R:=Format('O.%s:=Param%s[%s];',[PN,S,SFN]);
+    R:=Format('Param%s[%s]:=O.%s;',[S,SFN,PN]);
   AddLn(Strings,R);
   AddLn(Strings,R);
 end;
 end;
 
 
@@ -478,17 +612,19 @@ end;
 procedure TTiOPFCodeGenerator.WriteReadListVisitor(Strings : TStrings; Const ObjectClassName : String);
 procedure TTiOPFCodeGenerator.WriteReadListVisitor(Strings : TStrings; Const ObjectClassName : String);
 
 
 Var
 Var
-  C,S,LN : String;
+  OCN,CS,C,S,LN : String;
   I : Integer;
   I : Integer;
 
 
 begin
 begin
   LN:=tiOPFOptions.ListClassName;
   LN:=tiOPFOptions.ListClassName;
-  C:=Format('TRead%sVisitor',[StripType(LN)]);
+  OCN:=StripType(ObjectClassName);
+  CS:=Format('SQLReadList%s',[OCN]);
+  C:=Format('TReadList%sVisitor',[StripType(OCN)]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings);
   Addln(Strings);
   // Init
   // Init
   S:=BeginInit(Strings,C);
   S:=BeginInit(Strings,C);
-  Addln(Strings,'Query.SQL.Text:=SQLReadList;');
+  WriteSetSQL(Strings,CS);
   DecIndent;
   DecIndent;
   EndMethod(Strings,C);
   EndMethod(Strings,C);
   // AcceptVisitor
   // AcceptVisitor
@@ -500,11 +636,19 @@ begin
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // MapRowToObject
   // MapRowToObject
-  S:=BeginMapRowToObject(Strings,S,ObjectClassName);
+  S:=BeginMapRowToObject(Strings,C,ObjectClassName);
   Addln(Strings,'O:=%s.Create;',[ObjectClassName]);
   Addln(Strings,'O:=%s.Create;',[ObjectClassName]);
-  For I:=0 to Fields.Count-1 do
-    If Fields[i].Enabled then
-      WriteFieldAssign(Strings,Fields[i]);
+  Addln(Strings,'With Query do',[ObjectClassName]);
+  IncINdent;
+  try
+    Addln(Strings,'begin');
+    For I:=0 to Fields.Count-1 do
+      If Fields[i].Enabled then
+        WriteFieldAssign(Strings,Fields[i]);
+    Addln(Strings,'end;');
+  finally
+    DecIndent;
+  end;
   Addln(Strings,'O.ObjectState:=posClean;');
   Addln(Strings,'O.ObjectState:=posClean;');
   Addln(Strings,'%s(Visited).Add(O);',[LN]);
   Addln(Strings,'%s(Visited).Add(O);',[LN]);
   DecIndent;
   DecIndent;
@@ -519,16 +663,18 @@ procedure TTiOPFCodeGenerator.WriteCreateVisitor(Strings : TStrings; Const Objec
 
 
 
 
 Var
 Var
-  C,S : String;
+  OCN,CS,C,S : String;
   I : Integer;
   I : Integer;
 
 
 begin
 begin
-  C:=Format('TCreate%sVisitor',[StripType(ObjectClassName)]);
+  OCN:=StripType(ObjectClassName);
+  CS:=Format('SQLCreate%s',[OCN]);
+  C:=Format('TCreate%sVisitor',[OCN]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings);
   Addln(Strings);
   // Init
   // Init
   S:=BeginInit(Strings,C);
   S:=BeginInit(Strings,C);
-  Addln(Strings,'Query.SQL.Text:=SQLCreateObject;');
+  WriteSetSQL(Strings,CS);
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // AcceptVisitor
   // AcceptVisitor
@@ -544,7 +690,7 @@ begin
     Addln(Strings,'begin');
     Addln(Strings,'begin');
     For I:=0 to Fields.Count-1 do
     For I:=0 to Fields.Count-1 do
       If Fields[i].Enabled then
       If Fields[i].Enabled then
-        WriteParamAssign(Strings,Fields[i]);
+        WriteAssignToParam(Strings,Fields[i]);
     Addln(Strings,'end;');
     Addln(Strings,'end;');
   finally
   finally
     DecIndent;
     DecIndent;
@@ -553,17 +699,26 @@ begin
   EndMethod(Strings,S);
   EndMethod(Strings,S);
 end;
 end;
 
 
+procedure TTiOPFCodeGenerator.WriteSetSQL(Strings : TStrings; Const ASQL : String);
+
+begin
+  Addln(Strings,Format('Query.SQL.Text:=%s;',[ASQL]));
+end;
+
 procedure TTiOPFCodeGenerator.WriteDeleteVisitor(Strings : TStrings; Const ObjectClassName : String);
 procedure TTiOPFCodeGenerator.WriteDeleteVisitor(Strings : TStrings; Const ObjectClassName : String);
 
 
 Var
 Var
-  C,S : String;
-
+  OCN,CS, C,S : String;
+  F : TFieldPropDef;
+  
 begin
 begin
+  OCN:=StripType(ObjectClassName);
+  CS:=Format('SQLDelete%s',[OCN]);
   C:=Format('TDelete%sVisitor',[StripType(ObjectClassName)]);
   C:=Format('TDelete%sVisitor',[StripType(ObjectClassName)]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings,'{ %s }',[C]);
   // Init
   // Init
   S:=BeginInit(Strings,C);
   S:=BeginInit(Strings,C);
-  Addln(Strings,'Query.SQL.Text:=SQLDeleteObject;');
+  WriteSetSQL(Strings,CS);
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // AcceptVisitor
   // AcceptVisitor
@@ -573,7 +728,11 @@ begin
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // SetupParams
   // SetupParams
   S:=BeginSetupParams(Strings,C,ObjectClassName,True);
   S:=BeginSetupParams(Strings,C,ObjectClassName,True);
-  AddLn(Strings,'// Add parameter setup code here ');
+  F:=Fields.FindPropName('OID');
+  If (F<>Nil) then
+    WriteAssignToParam(Strings,F)
+  else
+    AddLn(Strings,'// Add parameter setup code here ');
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
 end;
 end;
@@ -581,16 +740,18 @@ end;
 procedure TTiOPFCodeGenerator.WriteUpdateVisitor(Strings : TStrings; Const ObjectClassName : String);
 procedure TTiOPFCodeGenerator.WriteUpdateVisitor(Strings : TStrings; Const ObjectClassName : String);
 
 
 Var
 Var
-  C,S : String;
+  OCN,CS,C,S : String;
   I : Integer;
   I : Integer;
 
 
 begin
 begin
-  C:=Format('TUpdate%sVisitor',[StripType(ObjectClassName)]);
+  OCN:=StripType(ObjectClassName);
+  CS:=Format('SQLUpdate%s',[OCN]);
+  C:=Format('TUpdate%sVisitor',[OCN]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings,'{ %s }',[C]);
   Addln(Strings);
   Addln(Strings);
   // Init
   // Init
   S:=BeginInit(Strings,C);
   S:=BeginInit(Strings,C);
-  Addln(Strings,'Query.SQL.Text:=SQLUpdateObject;');
+  WriteSetSQl(Strings,CS);
   DecIndent;
   DecIndent;
   EndMethod(Strings,S);
   EndMethod(Strings,S);
   // AcceptVisitor
   // AcceptVisitor
@@ -606,7 +767,7 @@ begin
     Addln(Strings,'begin');
     Addln(Strings,'begin');
     For I:=0 to Fields.Count-1 do
     For I:=0 to Fields.Count-1 do
       If Fields[i].Enabled then
       If Fields[i].Enabled then
-        WriteParamAssign(Strings,Fields[i]);
+        WriteAssignToParam(Strings,Fields[i]);
     Addln(Strings,'end;');
     Addln(Strings,'end;');
   finally
   finally
     DecIndent;
     DecIndent;
@@ -630,8 +791,8 @@ begin
     AddLn(Strings,'Private');
     AddLn(Strings,'Private');
     IncIndent;
     IncIndent;
     Try
     Try
-      AddLn(Strings,'Function GetObj(Index : Integer) : %s;',[ObjectClassname]);
-      AddLn(Strings,'Procedure SetObj(Index : Integer; AValue : %s);',[ObjectClassname]);
+      AddLn(Strings,'Function GetObj(AIndex : Integer) : %s;',[ObjectClassname]);
+      AddLn(Strings,'Procedure SetObj(AIndex : Integer; AValue : %s);',[ObjectClassname]);
     Finally
     Finally
       DecIndent;
       DecIndent;
     end;
     end;
@@ -641,7 +802,7 @@ begin
     AddLn(Strings,'Public');
     AddLn(Strings,'Public');
     IncIndent;
     IncIndent;
     Try
     Try
-      Addln(Strings,'Procedure Add(AnItem : %s); reintroduce;',[ObjectClassName]);
+      Addln(Strings,'Function Add(AnItem : %s) : Integer; reintroduce;',[ObjectClassName]);
     Finally
     Finally
       DecIndent;
       DecIndent;
     end;
     end;
@@ -668,6 +829,7 @@ begin
   Addln(Strings,'%s = Class(%s)',[ListClassName,ListAncestorName]);
   Addln(Strings,'%s = Class(%s)',[ListClassName,ListAncestorName]);
   DoCreateListDeclaration(Strings,ObjectClassName,ListClassName,ListAncestorName);
   DoCreateListDeclaration(Strings,ObjectClassName,ListClassName,ListAncestorName);
   AddLn(Strings,'end;');
   AddLn(Strings,'end;');
+  Addln(Strings);
 end;
 end;
 
 
 procedure TTiOPFCodeGenerator.WriteListAddObject(Strings: TStrings;
 procedure TTiOPFCodeGenerator.WriteListAddObject(Strings: TStrings;
@@ -677,16 +839,26 @@ Var
   S : String;
   S : String;
   
   
 begin
 begin
-   S:=Format('Procedure %s.Add(AnItem : %s);',[ListClassName,ObjectClassName]);
+   S:=Format('Function %s.Add(AnItem : %s) : Integer;',[ListClassName,ObjectClassName]);
    BeginMethod(Strings,S);
    BeginMethod(Strings,S);
    Addln(Strings,'begin');
    Addln(Strings,'begin');
    IncIndent;
    IncIndent;
    try
    try
-     Addln(Strings,'inherited Add(AnItem);');
+     Addln(Strings,'Result:=inherited Add(AnItem);');
    finally
    finally
      DecIndent;
      DecIndent;
    end;
    end;
    EndMethod(Strings,S);
    EndMethod(Strings,S);
+   Addln(Strings);
+end;
+
+function TTiOPFCodeGenerator.AllowPropertyDeclaration(F: TFieldPropDef;
+  AVisibility: TVisibilities): Boolean;
+begin
+  If F.PropertyName=SOID then
+    Result:=False
+  else
+    Result:=inherited AllowPropertyDeclaration(F, AVisibility);
 end;
 end;
 
 
 
 
@@ -700,27 +872,31 @@ begin
     begin
     begin
     AddLn(Strings,'{ %s }',[ListClassName]);
     AddLn(Strings,'{ %s }',[ListClassName]);
     AddLn(Strings);
     AddLn(Strings);
-    S:=Format('Function %s.GetObj(Index : Integer) : %s;',[ListClassName,ObjectClassname]);
+    S:=Format('Function %s.GetObj(AIndex : Integer) : %s;',[ListClassName,ObjectClassname]);
     BeginMethod(Strings,S);
     BeginMethod(Strings,S);
     AddLn(Strings,'begin');
     AddLn(Strings,'begin');
     IncIndent;
     IncIndent;
     try
     try
-      AddLn(Strings,'Result:=%s(Inherited Items[Index]);',[ObjectClassname]);
+      AddLn(Strings,'Result:=%s(Inherited Items[AIndex]);',[ObjectClassname]);
     finally
     finally
       DecIndent;
       DecIndent;
     end;
     end;
     EndMethod(Strings,S);
     EndMethod(Strings,S);
-    S:=Format('Procedure %s.SetObj(Index : Integer; AValue : %s);',[ListClassName,ObjectClassname]);
+    Addln(Strings);
+    S:=Format('Procedure %s.SetObj(AIndex : Integer; AValue : %s);',[ListClassName,ObjectClassname]);
     BeginMethod(Strings,S);
     BeginMethod(Strings,S);
     AddLn(Strings,'begin');
     AddLn(Strings,'begin');
     IncIndent;
     IncIndent;
     try
     try
-      AddLn(Strings,'Inherited Items[Index]:=AValue;');
+      AddLn(Strings,'Inherited Items[AIndex]:=AValue;');
     finally
     finally
       DecIndent;
       DecIndent;
     end;
     end;
     EndMethod(Strings,S);
     EndMethod(Strings,S);
+    Addln(Strings);
     end;
     end;
+  If (caListAddMethod in tiOPFOptions.ClassOptions) then
+    WriteListAddObject(Strings,ListClassName,ObjectClassName);
 end;
 end;
 
 
 Initialization
 Initialization

+ 38 - 8
packages/fcl-db/src/codegen/fpddcodegen.pp

@@ -36,6 +36,7 @@ Type
                ptCustom);
                ptCustom);
                
                
   TVisibility = (vPrivate,vProtected,vPublic,vPublished);
   TVisibility = (vPrivate,vProtected,vPublic,vPublished);
+  TVisibilities = Set of TVisibility;
   TPropAccess = (paReadWrite,paReadonly,paWriteonly);
   TPropAccess = (paReadWrite,paReadonly,paWriteonly);
 
 
 
 
@@ -108,8 +109,12 @@ Type
 
 
   TCodeGeneratorOptions = Class(TPersistent)
   TCodeGeneratorOptions = Class(TPersistent)
   private
   private
+    FImplementationUnits: String;
+    FInterfaceUnits: String;
     FOptions: TCodeOptions;
     FOptions: TCodeOptions;
     FUnitName: String;
     FUnitName: String;
+    procedure SetImplementationUnits(const AValue: String);
+    procedure SetInterfaceUnits(const AValue: String);
     procedure SetUnitname(const AValue: String);
     procedure SetUnitname(const AValue: String);
   Protected
   Protected
     procedure SetOPtions(const AValue: TCodeOptions); virtual;
     procedure SetOPtions(const AValue: TCodeOptions); virtual;
@@ -119,6 +124,8 @@ Type
   Published
   Published
     Property Options : TCodeOptions Read FOptions Write SetOPtions;
     Property Options : TCodeOptions Read FOptions Write SetOPtions;
     Property UnitName : String Read FUnitName Write SetUnitname;
     Property UnitName : String Read FUnitName Write SetUnitname;
+    Property InterfaceUnits : String Read FInterfaceUnits Write SetInterfaceUnits;
+    Property ImplementationUnits : String Read FImplementationUnits Write SetImplementationUnits;
   end;
   end;
   TCodeGeneratorOptionsClass = Class of TCodeGeneratorOptions;
   TCodeGeneratorOptionsClass = Class of TCodeGeneratorOptions;
 
 
@@ -230,7 +237,9 @@ Type
     procedure CreateClassEnd(Strings : TStrings); virtual;
     procedure CreateClassEnd(Strings : TStrings); virtual;
     // Called right after section start is written.
     // Called right after section start is written.
     procedure WriteVisibilityStart(V: TVisibility; Strings: TStrings); virtual;
     procedure WriteVisibilityStart(V: TVisibility; Strings: TStrings); virtual;
-    // Writes a property declaration.
+    // Should a property declaration be written ?
+    function AllowPropertyDeclaration(F: TFieldPropDef; AVisibility: TVisibilities): Boolean; virtual;
+    // Creates a property declaration.
     Function PropertyDeclaration(Strings: TStrings; Def: TFieldPropDef) : String; virtual;
     Function PropertyDeclaration(Strings: TStrings; Def: TFieldPropDef) : String; virtual;
     // Writes private fields for class.
     // Writes private fields for class.
     procedure WritePrivateFields(Strings: TStrings); virtual;
     procedure WritePrivateFields(Strings: TStrings); virtual;
@@ -727,6 +736,12 @@ begin
   end;
   end;
 end;
 end;
 
 
+Function TDDClassCodeGenerator.AllowPropertyDeclaration(F : TFieldPropDef; AVisibility : TVisibilities) : Boolean;
+
+begin
+  Result:=Assigned(f) and F.Enabled and ((AVisibility=[]) or (F.PropertyVisibility in AVisibility));
+end;
+
 Procedure TDDClassCodeGenerator.CreateDeclaration(Strings : TStrings);
 Procedure TDDClassCodeGenerator.CreateDeclaration(Strings : TStrings);
 
 
 Const
 Const
@@ -751,7 +766,7 @@ begin
       For I:=0 to Fields.Count-1 do
       For I:=0 to Fields.Count-1 do
         begin
         begin
         F:=Fields[i];
         F:=Fields[i];
-        if F.Enabled and (F.PropertyVisibility=v) then
+        if AllowPropertyDeclaration(F,[V]) then
           AddLn(Strings,PropertyDeclaration(Strings,F)+';');
           AddLn(Strings,PropertyDeclaration(Strings,F)+';');
         end;
         end;
     Finally
     Finally
@@ -773,7 +788,7 @@ begin
     For I:=0 to Fields.Count-1 do
     For I:=0 to Fields.Count-1 do
       begin
       begin
       F:=Fields[i];
       F:=Fields[i];
-      if F.Enabled then
+      if AllowPropertyDeclaration(F,[]) then
         AddLn(Strings,'F%s : %s;',[F.PropertyName,F.ObjPasTypeDef]);
         AddLn(Strings,'F%s : %s;',[F.PropertyName,F.ObjPasTypeDef]);
       end;
       end;
   Finally
   Finally
@@ -802,7 +817,7 @@ begin
   For I:=0 to Fields.Count-1 do
   For I:=0 to Fields.Count-1 do
     begin
     begin
     F:=Fields[i];
     F:=Fields[i];
-    if F.Enabled and F.HasGetter then
+    if AllowPropertyDeclaration(F,[]) and F.HasGetter then
       begin
       begin
       If not B then
       If not B then
         begin
         begin
@@ -817,7 +832,7 @@ begin
   For I:=0 to Fields.Count-1 do
   For I:=0 to Fields.Count-1 do
     begin
     begin
     F:=Fields[i];
     F:=Fields[i];
-    if F.Enabled and F.HasGetter then
+    if AllowPropertyDeclaration(F,[]) and F.HasGetter then
       begin
       begin
       If not B then
       If not B then
         begin
         begin
@@ -1028,11 +1043,11 @@ begin
     For I:=0 to Fields.Count-1 do
     For I:=0 to Fields.Count-1 do
       begin
       begin
       F:=Fields[i];
       F:=Fields[i];
-      If F.Enabled then
+      If AllowPropertyDeclaration(F,[]) then
         begin
         begin
         if (F.Hasgetter) then
         if (F.Hasgetter) then
           AddLn(Strings,PropertyGetterDeclaration(F,False));
           AddLn(Strings,PropertyGetterDeclaration(F,False));
-        if (Fields[i].HasSetter) then
+        if (F.HasSetter) then
           AddLn(Strings,PropertySetterDeclaration(F,False));
           AddLn(Strings,PropertySetterDeclaration(F,False));
         end;
         end;
       end;
       end;
@@ -1217,11 +1232,13 @@ end;
 function TDDCustomCodeGenerator.GetInterfaceUsesClause: String;
 function TDDCustomCodeGenerator.GetInterfaceUsesClause: String;
 begin
 begin
   Result:='Classes, SysUtils';
   Result:='Classes, SysUtils';
+  If (CodeOptions.InterfaceUnits<>'') then
+    Result:=Result+','+CodeOptions.InterfaceUnits;
 end;
 end;
 
 
 function TDDCustomCodeGenerator.GetImplementationUsesClause: String;
 function TDDCustomCodeGenerator.GetImplementationUsesClause: String;
 begin
 begin
-  Result:='';
+  Result:=CodeOptions.ImplementationUnits;
 end;
 end;
 
 
 procedure TDDCustomCodeGenerator.GenerateCode(Stream: TStream);
 procedure TDDCustomCodeGenerator.GenerateCode(Stream: TStream);
@@ -1473,6 +1490,19 @@ begin
   FUnitName:=AValue;
   FUnitName:=AValue;
 end;
 end;
 
 
+procedure TCodeGeneratorOptions.SetInterfaceUnits(const AValue: String);
+begin
+  if FInterfaceUnits=AValue then exit;
+  FInterfaceUnits:=AValue;
+  // Do some checks here
+end;
+
+procedure TCodeGeneratorOptions.SetImplementationUnits(const AValue: String);
+begin
+  if FImplementationUnits=AValue then exit;
+  FImplementationUnits:=AValue;
+end;
+
 { TClassCodeGeneratorOptions }
 { TClassCodeGeneratorOptions }
 
 
 procedure TClassCodeGeneratorOptions.SetClassName(const AValue: String);
 procedure TClassCodeGeneratorOptions.SetClassName(const AValue: String);

+ 389 - 0
packages/fcl-db/src/codegen/fpddpopcode.pp

@@ -0,0 +1,389 @@
+unit fpddpopcode;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, typinfo, fpdatadict, db;
+
+Type
+  TDDCodeGenOption = (dcoFields,dcoIndexes,dcoProcedurePerTable,dcoUseWith,dcoClassDecl);
+  TDDCodeGenOptions = Set of TDDCodeGenoption;
+  
+  { TFPDDPopulateCodeGenerator }
+
+  TFPDDPopulateCodeGenerator = Class(TComponent)
+  private
+    FClassName: String;
+    FDD: TFPDataDictionary;
+    FDDV: String;
+    FIndent: Integer;
+    FCurrentIndent: Integer;
+    FOptions: TDDCodeGenOptions;
+    FTables: TStrings;
+    FProcedures : TStrings;
+    procedure SetOptions(const AValue: TDDCodeGenOptions);
+    procedure SetTables(const AValue: TStrings);
+  Protected
+    // General code generating routines
+    procedure AddProperty(const ObjName, PropName, PropValue: String; Lines: TStrings);
+    procedure AddProperty(const ObjName, PropName: String; PropValue: Boolean; Lines: TStrings);
+    procedure AddStringProperty(const ObjName, PropName, PropValue: String;  Lines: TStrings);
+    procedure AddProcedure(AProcedureName: String; Lines: TStrings); virtual;
+    procedure EndProcedure(Lines: TStrings);
+    Procedure Indent;
+    Procedure Undent;
+    procedure AddLine(ALine: String; Lines: TStrings); virtual;
+    Function EscapeString(Const S : String) : string;
+    procedure CreateClassDecl(Lines: TStrings); virtual;
+    // Data dictionare specific
+    procedure CheckDatadict;
+    procedure CreateFooter(Lines: TStrings);
+    procedure CreateHeader(Lines: TStrings);
+    // Table code
+    Function DoTable (Const ATable : TDDtableDef) : Boolean; virtual;
+    procedure CreateTableCode(T: TDDTableDef; Lines: TStrings);
+    procedure AddTableVars(Lines: TStrings);
+    procedure DoTableHeader(ATable: TDDTableDef; Lines: TStrings);
+    procedure DoTableFooter(ATable: TDDTableDef; Lines: TStrings);
+    // Field code
+    Function DoField (Const ATable : TDDtableDef; Const AField : TDDFieldDef) : Boolean; virtual;
+    procedure CreateFieldCode(ATable: TDDTableDef; AField: TDDFieldDef;  Lines: TStrings);
+  Public
+    Constructor Create(AOwner : TComponent); override;
+    Destructor Destroy; override;
+    Procedure CreateCode(Lines : TStrings);
+    Property DataDictionary : TFPDataDictionary Read FDD Write FDD;
+  Published
+    Property Options : TDDCodeGenOptions Read FOptions Write SetOptions;
+    Property Tables : TStrings Read FTables Write SetTables;
+    Property IndentSize : Integer Read FIndent Write FIndent;
+    Property DDVarName : String Read FDDV Write FDDV;
+    Property ClassName : String Read FClassName Write FClassName;
+  end;
+  
+implementation
+
+Resourcestring
+  SErrNoDataDictionary = 'Cannot perform this operation without datadictionary';
+  SErrNoDataDictionaryName = 'Cannot perform this operation without datadictionary name';
+  
+
+{ TFPDDPopulateCodeGenerator }
+
+procedure TFPDDPopulateCodeGenerator.SetOptions(const AValue: TDDCodeGenOptions);
+begin
+  if FOptions=AValue then exit;
+  FOptions:=AValue;
+end;
+
+procedure TFPDDPopulateCodeGenerator.SetTables(const AValue: TStrings);
+begin
+  if FTables=AValue then exit;
+  FTables.Assign(AValue);
+end;
+
+function TFPDDPopulateCodeGenerator.DoTable(Const ATable: TDDtableDef): Boolean;
+begin
+  Result:=Assigned(ATable) and ((FTables.Count=0) or (FTables.IndexOf(ATable.TableName)<>-1));
+end;
+
+function TFPDDPopulateCodeGenerator.DoField(const ATable: TDDtableDef;
+  const AField: TDDFieldDef): Boolean;
+begin
+  Result:=Assigned(ATable) and Assigned(AField);
+end;
+
+constructor TFPDDPopulateCodeGenerator.Create(AOwner: TComponent);
+
+Var
+  T : TStringList;
+
+begin
+  inherited Create(AOwner);
+  T:=TStringList.Create;
+  T.Sorted:=true;
+  T.Duplicates:=dupIgnore;
+  FTables:=T;
+  IndentSize:=2;
+end;
+
+destructor TFPDDPopulateCodeGenerator.Destroy;
+begin
+  FreeAndNil(FTables);
+  inherited Destroy;
+end;
+
+procedure TFPDDPopulateCodeGenerator.CheckDatadict;
+
+begin
+  If (FDD=Nil) then
+    Raise EDataDict.Create(SErrNoDataDictionary);
+  If (FDDV='') then
+    Raise EDataDict.Create(SErrNoDataDictionaryName);
+end;
+
+function TFPDDPopulateCodeGenerator.EscapeString(const S: String): string;
+begin
+  Result:=StringReplace(S,'''','''''',[rfReplaceAll]);
+end;
+
+procedure TFPDDPopulateCodeGenerator.AddProcedure(AProcedureName : String; Lines: TStrings);
+
+Var
+  S : String;
+
+begin
+  S:=AProcedureName;
+  FProcedures.Add(S);
+  If (FClassName<>'') then
+    S:=FClassName+'.'+S;
+  AddLine('Procedure '+S+';',Lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.EndProcedure(Lines: TStrings);
+
+begin
+  Undent;
+  AddLine('end;',lines);
+  AddLine('',Lines)
+end;
+
+
+procedure TFPDDPopulateCodeGenerator.AddLine(ALine: String; Lines: TStrings);
+begin
+  If (ALine<>'') and (FCurrentIndent<>0) then
+    Aline:=StringOfChar(' ',FCurrentIndent)+ALine;
+  Lines.Add(ALine);
+end;
+
+procedure TFPDDPopulateCodeGenerator.Indent;
+begin
+  Inc(FCurrentIndent,FIndent);
+end;
+
+procedure TFPDDPopulateCodeGenerator.Undent;
+begin
+  Dec(FCurrentIndent,FIndent);
+  If (FCurrentIndent<0) then
+    FCurrentIndent:=0;
+end;
+
+procedure TFPDDPopulateCodeGenerator.AddTableVars(Lines: TStrings);
+
+begin
+  AddLine('',Lines);
+  AddLine('Var',Lines);
+  Indent;
+  AddLine('T : TDDTableDef;',lines);
+  If dcoFields in Options then
+    AddLine('F : TDDFieldDef;',lines);
+  Undent;
+end;
+
+
+procedure TFPDDPopulateCodeGenerator.DoTableHeader(ATable : TDDTableDef; Lines: TStrings);
+
+begin
+  If dcoProcedurePerTable in Options then
+    begin
+    AddProcedure('PopulateTable'+ATable.TableName,Lines);
+    AddTableVars(Lines);
+    AddLine('',Lines);
+    AddLine('begin',Lines);
+    Indent;
+    end;
+  AddLine(Format('T:=%s.Tables.AddTable(''%s'');',[FDDV,ATable.TableName]),Lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.DoTableFooter(ATable : TDDTableDef; Lines: TStrings);
+
+begin
+  If dcoProcedurePerTable in Options then
+    EndProcedure(Lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.AddProperty(Const ObjName,PropName : String; PropValue : Boolean; Lines: TStrings);
+
+begin
+  If PropValue then
+    AddProperty(ObjName,PropName,'True',Lines)
+  else
+    AddProperty(ObjName,PropName,'False',Lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.AddProperty(Const ObjName,PropName,PropValue : String; Lines: TStrings);
+
+begin
+  If Not (dcoUseWith in Options) then
+    AddLine(Format('%s.%s:=%s;',[Objname,Propname,PropValue]),lines)
+  else
+    AddLine(Format('%s:=%s;',[Propname,PropValue]),lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.AddStringProperty(Const ObjName,PropName,PropValue : String; Lines: TStrings);
+
+begin
+  If (PropValue<>'') then
+    If Not (dcoUseWith in Options) then
+      AddLine(Format('%s.%s:=''%s'';',[Objname,Propname,EscapeString(PropValue)]),lines)
+    else
+      AddLine(Format('%s:=''%s'';',[Propname,EscapeString(PropValue)]),lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.CreateFieldCode(ATable : TDDTableDef; AField : TDDFieldDef; Lines: TStrings);
+
+begin
+  AddLine(Format('F:=T.Fields.AddField(''%s'');',[AField.FieldName]),Lines);
+  If (dcoUseWith in Options) then
+     begin
+     AddLine('With F do',Lines);
+     Indent;
+     AddLine('begin',Lines);
+     end;
+  if (AField.FieldType<>ftUnknown) then
+    AddProperty('F','FieldType',GetEnumName(TypeInfo(TFieldType),Ord(AField.FieldType)),Lines);
+  If (AField.AlignMent<>taLeftJustify) then
+    AddProperty('F','AlignMent',GetEnumName(TypeInfo(TAlignMent),Ord(AField.AlignMent)),Lines);
+  AddStringProperty('F','CustomConstraint',AField.CustomConstraint,Lines);
+  AddStringProperty('F','ConstraintErrorMessage',AField.ConstraintErrorMessage,Lines);
+  AddStringProperty('F','DBDefault',AField.DBDefault,Lines);
+  AddStringProperty('F','DefaultExpression',AField.DefaultExpression,Lines);
+  AddStringProperty('F','DisplayLabel',AField.DisplayLabel,Lines);
+  If (AField.DisplayWidth<>0) then
+    AddProperty('F','DisplayWidth',IntToStr(AField.DisplayWidth),Lines);
+  AddStringProperty('F','Constraint',AField.Constraint,Lines);
+  AddProperty('F','ReadOnly',AField.ReadOnly,Lines);
+  AddProperty('F','Required',AField.Required,Lines);
+  AddProperty('F','Visible',AField.Visible,Lines);
+  If (AField.Size<>0) then
+    AddProperty('F','Size',IntToStr(AField.Size),Lines);
+  If (AField.Precision<>0) then
+    AddProperty('F','Precision',IntToStr(AField.Precision),Lines);
+  AddStringProperty('F','Hint',AField.Hint,Lines);
+  If (dcoUseWith in Options) then
+     begin
+     AddLine('end;',Lines);
+     Undent;
+     end;
+end;
+
+procedure TFPDDPopulateCodeGenerator.CreateHeader(Lines: TStrings);
+
+begin
+  If Not (dcoProcedurePerTable in Options) then
+    begin
+    AddProcedure('PopulateDataDictionary',Lines);
+    AddTableVars(Lines);
+    AddLine('',Lines);
+    AddLine('begin',Lines);
+    Indent;
+    end
+end;
+
+procedure TFPDDPopulateCodeGenerator.CreateFooter(Lines: TStrings);
+
+Var
+  i : integer;
+  L : TStrings;
+
+begin
+  If (dcoProcedurePerTable in Options) then
+    begin
+    L:=TStringList.Create;
+    try
+      L.Assign(FProcedures);
+      AddProcedure('PopulateDataDictionary',Lines);
+      AddLine('',Lines);
+      AddLine('begin',Lines);
+      Indent;
+      For I:=0 to L.Count-1 do
+        begin
+        AddLine(L[i]+';',Lines);
+        end;
+    finally
+      L.Free;
+    end;
+    end;
+  Undent;
+  EndProcedure(Lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.CreateTableCode(T : TDDTableDef; Lines: TStrings);
+
+Var
+  I : Integer;
+  F : TDDFieldDef;
+  
+begin
+  DoTableHeader(T,Lines);
+  try
+    If dcoFields in Options then
+      For I:=0 to T.Fields.Count-1 Do
+        begin
+        F:=T.Fields[I];
+        If DoField(T,F) then
+          CreateFieldcode(T,F,Lines);
+        end;
+  Finally
+    DoTableFooter(T,Lines);
+  end;
+end;
+
+procedure TFPDDPopulateCodeGenerator.CreateClassDecl(Lines: TStrings);
+
+Var
+  I : integer;
+
+begin
+  AddLine('(*',Lines);
+  Indent;
+  AddLine(Format('%s = Class(TObject)',[ClassName]),Lines);
+  AddLine('Private',lines);
+  Indent;
+  AddLine(Format('F%s : TFPDataDictionary;',[FDDV]),Lines);
+  Undent;
+  AddLine('Public',Lines);
+  Indent;
+  For I:=0 to FProcedures.Count-1 do
+    AddLine(Format('Procedure %s;',[FProcedures[i]]),Lines);
+  AddLine(Format('Property %s : TFPDataDictionary Read F%:0s Write F%:0s;',[FDDV]),Lines);
+  Undent;
+  AddLine('end;',lines);
+  Undent;
+  AddLine('*)',Lines);
+end;
+
+procedure TFPDDPopulateCodeGenerator.CreateCode(Lines: TStrings);
+
+Var
+  I : Integer;
+  T : TDDTableDef;
+  F : TDDFieldDef;
+
+begin
+  FCurrentIndent:=0;
+  CheckDataDict;
+  FProcedures:=TStringList.Create;
+  try
+    CreateHeader(Lines);
+    Try
+    For I:=0 to FDD.Tables.Count-1 do
+      begin
+      T:=FDD.Tables[i];
+      If DoTable(T) then
+        CreateTableCode(T,Lines);
+      end;
+    Finally
+      CreateFooter(Lines);
+    end;
+    If (dcoClassDecl in Options) and (FClassName<>'') then
+      CreateClassDecl(Lines);
+  finally
+    FreeAndNil(FProcedures);
+  end;
+end;
+
+end.
+

+ 3 - 0
packages/fcl-db/src/datadict/buildd.lpi

@@ -94,6 +94,9 @@
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>
     <Version Value="5"/>
     <Version Value="5"/>
+    <SearchPaths>
+      <UnitOutputDirectory Value="../../units/$(TARGETCPU)-$(TARGETOS)"/>
+    </SearchPaths>
     <CodeGeneration>
     <CodeGeneration>
       <Generate Value="Faster"/>
       <Generate Value="Faster"/>
     </CodeGeneration>
     </CodeGeneration>

+ 280 - 4
packages/fcl-db/src/datadict/fpdatadict.pp

@@ -28,6 +28,9 @@ Type
                  otConnection,otTableData,otIndexDefs,otIndexDef);
                  otConnection,otTableData,otIndexDefs,otIndexDef);
   TDDProgressEvent = Procedure(Sender : TObject; Const Msg : String) of Object;
   TDDProgressEvent = Procedure(Sender : TObject; Const Msg : String) of Object;
 
 
+  TFPDDFieldList = Class;
+  TFPDDIndexList = Class;
+
   { TDDFieldDef }
   { TDDFieldDef }
 
 
   TDDFieldDef = Class(TIniCollectionItem)
   TDDFieldDef = Class(TIniCollectionItem)
@@ -44,6 +47,7 @@ Type
     FFieldType: TFieldType;
     FFieldType: TFieldType;
     FHint: String;
     FHint: String;
     FPrecision: Integer;
     FPrecision: Integer;
+    FProviderFlags: TProviderFlags;
     FReadOnly: Boolean;
     FReadOnly: Boolean;
     FRequired: Boolean;
     FRequired: Boolean;
     FSize: Integer;
     FSize: Integer;
@@ -77,6 +81,7 @@ Type
     Property Size : Integer Read FSize Write FSize Stored IsSizeStored;
     Property Size : Integer Read FSize Write FSize Stored IsSizeStored;
     Property Precision : Integer Read FPrecision Write FPrecision Stored IsPrecisionStored;
     Property Precision : Integer Read FPrecision Write FPrecision Stored IsPrecisionStored;
     Property Hint : String Read FHint Write FHint;
     Property Hint : String Read FHint Write FHint;
+    Property ProviderFlags : TProviderFlags Read FProviderFlags Write FProviderFlags;
   end;
   end;
   
   
   { TDDFieldDefs }
   { TDDFieldDefs }
@@ -93,6 +98,7 @@ Type
     Function IndexOfField(AFieldName : String) : Integer;
     Function IndexOfField(AFieldName : String) : Integer;
     Function FindField(AFieldName : String) : TDDFieldDef;
     Function FindField(AFieldName : String) : TDDFieldDef;
     Function FieldByName(AFieldName : String) : TDDFieldDef;
     Function FieldByName(AFieldName : String) : TDDFieldDef;
+    Procedure FillFieldList(Const AFieldNames: String; List : TFPDDFieldList);
     Property Fields[Index : Integer] : TDDFieldDef Read GetField Write SetField; default;
     Property Fields[Index : Integer] : TDDFieldDef Read GetField Write SetField; default;
     Property TableName : String Read FTableName Write SetTableName;
     Property TableName : String Read FTableName Write SetTableName;
   end;
   end;
@@ -111,6 +117,9 @@ Type
     function GetSectionName: String; override;
     function GetSectionName: String; override;
     procedure SetSectionName(const Value: String); override;
     procedure SetSectionName(const Value: String); override;
     procedure Assign(ASource : TPersistent); override;
     procedure Assign(ASource : TPersistent); override;
+  Public
+    Procedure SaveToIni(Ini: TCustomInifile; ASection : String); override;
+    Procedure LoadFromIni(Ini: TCustomInifile; ASection : String); override;
   Published
   Published
     Property IndexName : String Read FIndexName Write FIndexName;
     Property IndexName : String Read FIndexName Write FIndexName;
     property Expression: string read FExpression write FExpression;
     property Expression: string read FExpression write FExpression;
@@ -232,6 +241,17 @@ Type
     Property FieldDefs[Index : Integer] : TDDFieldDef Read GetFieldDef Write SetFieldDef; default;
     Property FieldDefs[Index : Integer] : TDDFieldDef Read GetFieldDef Write SetFieldDef; default;
   end;
   end;
   
   
+  { TFPDDIndexList }
+
+  TFPDDIndexList = Class(TObjectList)
+  private
+    function GetIndexDef(AIndex : Integer): TDDIndexDef;
+    procedure SetIndexDef(AIndex : Integer; const AValue: TDDIndexDef);
+  Public
+    Constructor CreateFromIndexDefs(FD : TDDIndexDefs);
+    Property IndexDefs[AIndex : Integer] : TDDIndexDef Read GetIndexDef Write SetIndexDef; default;
+  end;
+
   
   
 
 
   
   
@@ -249,6 +269,7 @@ Type
     FOptions: TSQLEngineOptions;
     FOptions: TSQLEngineOptions;
     FTableDef: TDDTableDef;
     FTableDef: TDDTableDef;
     FNoIndent : Boolean;
     FNoIndent : Boolean;
+    FTerminator: String;
     FTerminatorChar : Char;
     FTerminatorChar : Char;
   Protected
   Protected
     procedure CheckTableDef;
     procedure CheckTableDef;
@@ -279,12 +300,18 @@ Type
     Procedure CreateDeleteSQLStrings(KeyFields : TFPDDFieldList; SQL : TStrings);
     Procedure CreateDeleteSQLStrings(KeyFields : TFPDDFieldList; SQL : TStrings);
     Procedure CreateCreateSQLStrings(Fields,KeyFields : TFPDDFieldList; SQL : TStrings);
     Procedure CreateCreateSQLStrings(Fields,KeyFields : TFPDDFieldList; SQL : TStrings);
     Procedure CreateCreateSQLStrings(KeyFields : TFPDDFieldList; SQL : TStrings);
     Procedure CreateCreateSQLStrings(KeyFields : TFPDDFieldList; SQL : TStrings);
+    Procedure CreateIndexesSQLStrings(Indexes : TFPDDIndexList; SQL : TStrings);
     Function  CreateSelectSQL(FieldList,KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateSelectSQL(FieldList,KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateInsertSQL(FieldList : TFPDDFieldList) : String; virtual;
     Function  CreateInsertSQL(FieldList : TFPDDFieldList) : String; virtual;
     Function  CreateUpdateSQL(FieldList,KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateUpdateSQL(FieldList,KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateDeleteSQL(KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateDeleteSQL(KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateCreateSQL(Fields,KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateCreateSQL(Fields,KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateCreateSQL(KeyFields : TFPDDFieldList) : String; virtual;
     Function  CreateCreateSQL(KeyFields : TFPDDFieldList) : String; virtual;
+    Function  CreateIndexSQL(Index : TDDIndexDef) : String; virtual;
+    Function  CreateIndexesSQL(Indexes : TFPDDIndexList) : String;
+    Function  CreateIndexesSQL(Indexes : TDDIndexDefs) : String;
+    Function  CreateTableSQL : String;
+    Procedure CreateTableSQLStrings(SQL : TStrings);
     Property TableDef : TDDTableDef Read FTableDef Write FTableDef;
     Property TableDef : TDDTableDef Read FTableDef Write FTableDef;
   Published
   Published
     Property MaxLineLength : Integer Read FMaxLineLength Write FMaxLineLength default 72;
     Property MaxLineLength : Integer Read FMaxLineLength Write FMaxLineLength default 72;
@@ -404,8 +431,18 @@ Const
   KeyRequired               = 'Required';
   KeyRequired               = 'Required';
   KeyVisible                = 'Visible';
   KeyVisible                = 'Visible';
   KeySize                   = 'Size';
   KeySize                   = 'Size';
+  KeyPrecision              = 'Precision';
   KeyFieldType              = 'FieldType';
   KeyFieldType              = 'FieldType';
   KeyHint                   = 'Hint';
   KeyHint                   = 'Hint';
+  KeyProviderFlags          = 'Providerflags';
+  
+  // Index saving
+  KeyExpression             = 'Expression';
+  KeyFields                 = 'Fields';
+  KeyCaseInsFields          = 'CaseInsFields';
+  KeyDescFields             = 'DescFields';
+  KeySource                 = 'Source';
+  KeyOptions                = 'Options';
 
 
   // SQL Keywords
   // SQL Keywords
   SSelect      = 'SELECT';
   SSelect      = 'SELECT';
@@ -430,7 +467,7 @@ Const
     'BOOL', 'FLOAT', 'DECIMAL','DECIMAL','DATE', 'TIME', 'TIMESTAMP',
     'BOOL', 'FLOAT', 'DECIMAL','DECIMAL','DATE', 'TIME', 'TIMESTAMP',
     '', '', 'INT', 'BLOB', 'BLOB', 'BLOB', 'BLOB',
     '', '', 'INT', 'BLOB', 'BLOB', 'BLOB', 'BLOB',
     '', '', '', '', 'CHAR',
     '', '', '', '', 'CHAR',
-    'CHAR', 'DOUBLE PRECISION', '', '', '',
+    'CHAR', 'BIGINT', '', '', '',
     '', '', '', '', '',
     '', '', '', '', '',
     '', '', 'TIMESTAMP', 'DECIMAL','CHAR','BLOB');
     '', '', 'TIMESTAMP', 'DECIMAL','CHAR','BLOB');
     
     
@@ -729,6 +766,7 @@ begin
     ReadOnly:=F.ReadOnly;
     ReadOnly:=F.ReadOnly;
     Required:=F.Required;
     Required:=F.Required;
     Visible:=F.Visible;
     Visible:=F.Visible;
+    ProviderFlags:=F.ProviderFlags;
   end;
   end;
 end;
 end;
 
 
@@ -749,7 +787,7 @@ begin
   F.ReadOnly               := ReadOnly;
   F.ReadOnly               := ReadOnly;
   F.Required               := Required;
   F.Required               := Required;
   F.Visible                := Visible;
   F.Visible                := Visible;
-
+  F.ProviderFlags          := ProviderFlags;
 end;
 end;
 
 
 procedure TDDFieldDef.Assign(Source: TPersistent);
 procedure TDDFieldDef.Assign(Source: TPersistent);
@@ -779,18 +817,26 @@ begin
     ReadOnly:=DF.ReadOnly;
     ReadOnly:=DF.ReadOnly;
     Required:=DF.Required;
     Required:=DF.Required;
     Visible:=DF.Visible;
     Visible:=DF.Visible;
+    ProviderFlags:=DF.ProviderFlags;
     end
     end
   else
   else
     Inherited;
     Inherited;
 end;
 end;
 
 
 procedure TDDFieldDef.SaveToIni(Ini: TCustomInifile; ASection: String);
 procedure TDDFieldDef.SaveToIni(Ini: TCustomInifile; ASection: String);
+
+Var
+  T : PTypeInfo;
+  O : Integer;
+
 begin
 begin
   With Ini do
   With Ini do
     begin
     begin
     WriteInteger(ASection,KeyFieldType,Ord(Fieldtype));
     WriteInteger(ASection,KeyFieldType,Ord(Fieldtype));
     If IsSizeStored then
     If IsSizeStored then
-      WriteInteger(ASection,KeySize,Ord(Size));
+      WriteInteger(ASection,KeySize,Size);
+    If IsPrecisionStored then
+      WriteInteger(ASection,KeyPrecision,Precision);
     WriteInteger(ASection,KeyAlignMent,Ord(AlignMent));
     WriteInteger(ASection,KeyAlignMent,Ord(AlignMent));
     WriteInteger(ASection,KeyDisplayWidth,DisplayWidth);
     WriteInteger(ASection,KeyDisplayWidth,DisplayWidth);
     WriteString(ASection,KeyCustomConstraint,CustomConstraint);
     WriteString(ASection,KeyCustomConstraint,CustomConstraint);
@@ -801,6 +847,9 @@ begin
     WriteString(ASection,KeyFieldName,FieldName);
     WriteString(ASection,KeyFieldName,FieldName);
     WriteString(ASection,KeyConstraint,Constraint);
     WriteString(ASection,KeyConstraint,Constraint);
     WriteString(ASection,KeyHint,Hint);
     WriteString(ASection,KeyHint,Hint);
+    O:=Integer(ProviderFlags);
+    T:=TypeInfo(TProviderFlags);
+    WriteString(ASection,KeyProviderFlags,SetToString(T,O,False));
     WriteBool(ASection,KeyReadOnly,ReadOnly);
     WriteBool(ASection,KeyReadOnly,ReadOnly);
     WriteBool(ASection,KeyRequired,Required);
     WriteBool(ASection,KeyRequired,Required);
     WriteBool(ASection,KeyVisible,Visible);
     WriteBool(ASection,KeyVisible,Visible);
@@ -809,12 +858,20 @@ end;
 
 
 procedure TDDFieldDef.LoadFromIni(Ini: TCustomInifile; ASection: String);
 procedure TDDFieldDef.LoadFromIni(Ini: TCustomInifile; ASection: String);
 
 
+Var
+  T : PTypeInfo;
+  O : Integer;
+  PF : TProviderFlags;
+  S : String;
+
 begin
 begin
   With Ini do
   With Ini do
     begin
     begin
     FieldType:=TFieldType(ReadInteger(ASection,KeyFieldType,Ord(Fieldtype)));
     FieldType:=TFieldType(ReadInteger(ASection,KeyFieldType,Ord(Fieldtype)));
     If IsSizeStored then
     If IsSizeStored then
       Size:=ReadInteger(ASection,KeySize,0);
       Size:=ReadInteger(ASection,KeySize,0);
+    If IsPrecisionStored then
+      Precision:=ReadInteger(ASection,KeyPrecision,0);
     Alignment:=TAlignment(ReadInteger(ASection,KeyAlignMent,Ord(AlignMent)));
     Alignment:=TAlignment(ReadInteger(ASection,KeyAlignMent,Ord(AlignMent)));
     DisplayWidth:=ReadInteger(ASection,KeyDisplayWidth,DisplayWidth);
     DisplayWidth:=ReadInteger(ASection,KeyDisplayWidth,DisplayWidth);
     CustomConstraint:=ReadString(ASection,KeyCustomConstraint,CustomConstraint);
     CustomConstraint:=ReadString(ASection,KeyCustomConstraint,CustomConstraint);
@@ -825,6 +882,11 @@ begin
     FieldName:=ReadString(ASection,KeyFieldName,FieldName);
     FieldName:=ReadString(ASection,KeyFieldName,FieldName);
     Constraint:=ReadString(ASection,KeyConstraint,Constraint);
     Constraint:=ReadString(ASection,KeyConstraint,Constraint);
     Hint:=ReadString(ASection,KeyHint,Hint);
     Hint:=ReadString(ASection,KeyHint,Hint);
+    S:=ReadString(ASection,KeyProviderFlags,'');
+    T:=TypeInfo(TProviderFlags);
+    O:=StringToSet(T,S);
+    Integer(PF):=O;
+    ProviderFlags:=PF;
     ReadOnly:=ReadBool(ASection,KeyReadOnly,ReadOnly);
     ReadOnly:=ReadBool(ASection,KeyReadOnly,ReadOnly);
     Required:=ReadBool(ASection,KeyRequired,Required);
     Required:=ReadBool(ASection,KeyRequired,Required);
     Visible:=ReadBool(ASection,KeyVisible,Visible);
     Visible:=ReadBool(ASection,KeyVisible,Visible);
@@ -906,6 +968,26 @@ begin
     Raise EDatadict.CreateFmt(SErrFieldNotFound,[TableName,AFieldName]);
     Raise EDatadict.CreateFmt(SErrFieldNotFound,[TableName,AFieldName]);
 end;
 end;
 
 
+procedure TDDFieldDefs.FillFieldList(const AFieldNames: String;
+  List: TFPDDFieldList);
+
+Var
+  I : Integer;
+  S,T : String;
+  F : TDDFieldDef;
+  
+begin
+  T:=Trim(AFieldNames);
+  Repeat
+    I:=Pos(';',T);
+    If I=0 Then
+      I:=Length(T)+1;
+    S:=Trim(Copy(T,1,I-1));
+    System.Delete(T,1,I);
+    List.Add(FieldByName(S));
+  Until (T='');
+end;
+
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
   TDDTableDef
   TDDTableDef
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
@@ -1572,7 +1654,7 @@ begin
       Result:=Result+Format('(%d)',[FD.Size]);
       Result:=Result+Format('(%d)',[FD.Size]);
     ftBCD,
     ftBCD,
     ftFMTBCD :
     ftFMTBCD :
-      Result:=Result+Format('(%d,%d)',[FD.Precision,FD.Size]);
+      Result:=Result+Format('(%d,%d)',[FD.Size,FD.Precision]);
   end;
   end;
 end;
 end;
 
 
@@ -1776,12 +1858,130 @@ begin
   CheckTableDef;
   CheckTableDef;
   FL:=TFPDDfieldList.CreateFromTableDef(TableDef);
   FL:=TFPDDfieldList.CreateFromTableDef(TableDef);
   try
   try
+    FL.OwnsObjects:=False;
     Result:=CreateCreateSQL(FL,KeyFields);
     Result:=CreateCreateSQL(FL,KeyFields);
   finally
   finally
     FL.Free;
     FL.Free;
   end;
   end;
 end;
 end;
 
 
+function TFPDDSQLEngine.CreateIndexSQL(Index: TDDIndexDef): String;
+
+Var
+  L : TFPDDFieldList;
+  I : Integer;
+  
+begin
+  Result:='CREATE ';
+  If ixUnique in Index.Options then
+    Result:=Result+'UNIQUE ';
+  If ixDescending in Index.Options then
+    Result:=Result+'DESCENDING ';
+  Result:=Result+'INDEX '+Index.IndexName;
+  Result:=Result+' ON '+TableDef.TableName+' (';
+  L:=TFPDDFieldList.Create;
+  try
+    L.OwnsObjects:=False;
+    TableDef.Fields.FillFieldList(Index.Fields,L);
+    For I:=0 to L.Count-1 do
+      begin
+      If (I>0) then
+        Result:=Result+',';
+      Result:=Result+L[I].FieldName;
+      end;
+  finally
+    L.Free;
+  end;
+  Result:=Result+')';
+end;
+
+function TFPDDSQLEngine.CreateIndexesSQL(Indexes: TFPDDIndexList): String;
+
+Var
+  SQL : TStringList;
+
+begin
+  SQL:=TStringList.Create;
+  try
+    CreateIndexesSQLStrings(Indexes,SQL);
+    Result:=SQL.Text;
+  finally
+    SQL.free;
+  end;
+end;
+
+function TFPDDSQLEngine.CreateIndexesSQL(Indexes: TDDIndexDefs): String;
+
+Var
+  IL : TFPDDIndexList;
+
+begin
+  IL:=TFPDDIndexList.CreateFromIndexDefs(Indexes);
+  try
+    IL.OwnsObjects:=False;
+    Result:=CreateIndexesSQL(IL);
+  finally
+    IL.Free;
+  end;
+end;
+
+function TFPDDSQLEngine.CreateTableSQL: String;
+
+Var
+  SQL : TStrings;
+
+begin
+  SQL:=TStringList.Create;
+  try
+    CreateTableSQLStrings(SQL);
+    Result:=SQL.Text;
+  finally
+    SQL.Free;
+  end;
+end;
+
+procedure TFPDDSQLEngine.CreateTableSQLStrings(SQL: TStrings);
+
+Var
+  L : TStrings;
+  I : Integer;
+  KF : TFPDDFieldlist;
+  ID : TDDIndexDef;
+  FD : TDDFieldDef;
+  
+begin
+  CheckTableDef;
+  L:=TStringList.Create;
+  try
+    KF:=TFPDDFieldlist.Create(False);
+    try
+      KF.OwnsObjects:=False;
+      I:=0;
+      While (I<TableDef.Indexes.Count) and (KF.Count=0) do
+        begin
+        ID:=TableDef.Indexes[i];
+        If (ixPrimary in ID.Options) then
+          TableDef.Fields.FillFieldList(ID.Fields,KF);
+        Inc(I);
+        end;
+      If (KF.Count=0) then
+        For I:=0 to TableDef.Fields.Count-1 do
+          begin
+          FD:=TableDef.Fields[I];
+          If pfInKey in FD.ProviderFlags then
+            KF.Add(FD);
+          end;
+      CreateCreateSQLStrings(KF,SQL);
+      L.Text:=CreateIndexesSQL(TableDef.Indexes);
+      SQL.AddStrings(L);
+    finally
+      KF.Free;
+    end;
+  finally
+    L.Free;
+  end;
+end;
+
 { TStrings versions of SQL creation statements. }
 { TStrings versions of SQL creation statements. }
 
 
 procedure TFPDDSQLEngine.CreateSelectSQLStrings(FieldList,KeyFields: TFPDDFieldList; SQL: TStrings);
 procedure TFPDDSQLEngine.CreateSelectSQLStrings(FieldList,KeyFields: TFPDDFieldList; SQL: TStrings);
@@ -1819,6 +2019,16 @@ begin
   SQL.Text:=CreateCreateSQL(KeyFields);
   SQL.Text:=CreateCreateSQL(KeyFields);
 end;
 end;
 
 
+procedure TFPDDSQLEngine.CreateIndexesSQLStrings(Indexes: TFPDDIndexList; SQL: TStrings);
+
+Var
+  I : integer;
+
+begin
+  For I:=0 to Indexes.Count-1 do
+    SQL.Add(CreateIndexSQL(Indexes[i])+TerminatorChar);
+end;
+
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
   TDDFieldList
   TDDFieldList
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
@@ -1851,6 +2061,28 @@ begin
     Add(FD[i]);
     Add(FD[i]);
 end;
 end;
 
 
+function TFPDDIndexList.GetIndexDef(AIndex: Integer): TDDIndexDef;
+begin
+  Result:=TDDIndexDef(Items[AIndex]);
+end;
+
+procedure TFPDDIndexList.SetIndexDef(AIndex: Integer; const AValue: TDDIndexDef
+  );
+begin
+  Items[AIndex]:=AValue
+end;
+
+constructor TFPDDIndexList.CreateFromIndexDefs(FD: TDDIndexDefs);
+
+var
+  I : Integer;
+
+begin
+  Inherited Create;
+  For I:=0 to FD.Count-1 do
+    Add(FD[I]);
+end;
+
 { TDDIndexDef }
 { TDDIndexDef }
 
 
 function TDDIndexDef.GetSectionName: String;
 function TDDIndexDef.GetSectionName: String;
@@ -1896,6 +2128,50 @@ begin
     inherited Assign(ASource);
     inherited Assign(ASource);
 end;
 end;
 
 
+procedure TDDIndexDef.SaveToIni(Ini: TCustomInifile; ASection: String);
+
+Var
+  O : Integer;
+  T : PTypeInfo;
+  
+begin
+  With Ini do
+    begin
+    WriteString(ASection,KeyExpression,Expression);
+    WriteString(ASection,KeyFields,Fields);
+    WriteString(ASection,KeyCaseInsFields,CaseInsFields);
+    WriteString(ASection,KeyDescFields,DescFields);
+    WriteString(ASection,KeySource,Source);
+    O:=Integer(Options);
+    T:=TypeInfo(TIndexOptions);
+    WriteString(ASection,KeyOptions,SetToString(T,O,False));
+    end;
+end;
+
+procedure TDDIndexDef.LoadFromIni(Ini: TCustomInifile; ASection: String);
+
+Var
+  O : Integer;
+  OP : TIndexOptions;
+  T : PTypeInfo;
+  S : String;
+
+begin
+  With Ini do
+    begin
+    Expression:=ReadString(ASection,KeyExpression,'');
+    Fields:=ReadString(ASection,KeyFields,'');
+    CaseInsFields:=ReadString(ASection,KeyCaseInsFields,'');
+    DescFields:=ReadString(ASection,KeyDescFields,'');
+    Source:=ReadString(ASection,KeySource,'');
+    S:=ReadString(ASection,KeyOptions,'');
+    T:=TypeInfo(TIndexOptions);
+    O:=StringToSet(T,S);
+    OP:=TIndexOptions(O);
+    Options:=OP;
+    end;
+end;
+
 { TDDIndexDefs }
 { TDDIndexDefs }
 
 
 function TDDIndexDefs.GetIndex(Index : Integer): TDDIndexDef;
 function TDDIndexDefs.GetIndex(Index : Integer): TDDIndexDef;

+ 50 - 37
packages/fcl-db/src/sqldb/sqldb.pp

@@ -20,7 +20,7 @@ unit sqldb;
 
 
 interface
 interface
 
 
-uses SysUtils, Classes, DB, bufdataset;
+uses SysUtils, Classes, DB, bufdataset, sqlscript;
 
 
 type TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages);
 type TSchemaType = (stNoSchema, stTables, stSysTables, stProcedures, stColumns, stProcedureParams, stIndexes, stPackages);
      TConnOption = (sqSupportParams,sqEscapeSlash,sqEscapeRepeat,sqQuoteFieldnames);
      TConnOption = (sqSupportParams,sqEscapeSlash,sqEscapeRepeat,sqQuoteFieldnames);
@@ -359,24 +359,37 @@ type
 
 
 { TSQLScript }
 { TSQLScript }
 
 
-  TSQLScript = class (Tcomponent)
+  TSQLScript = class (TCustomSQLscript)
   private
   private
-    FScript  : TStrings;
+    FOnDirective: TSQLScriptDirectiveEvent;
     FQuery   : TCustomSQLQuery;
     FQuery   : TCustomSQLQuery;
     FDatabase : TDatabase;
     FDatabase : TDatabase;
     FTransaction : TDBTransaction;
     FTransaction : TDBTransaction;
   protected
   protected
-    procedure SetScript(const AValue: TStrings);
+    procedure ExecuteStatement (SQLStatement: TStrings; var StopExecution: Boolean); override;
+    procedure ExecuteDirective (Directive, Argument: String; var StopExecution: Boolean); override;
+    procedure ExecuteCommit; override;
     Procedure SetDatabase (Value : TDatabase); virtual;
     Procedure SetDatabase (Value : TDatabase); virtual;
     Procedure SetTransaction(Value : TDBTransaction); virtual;
     Procedure SetTransaction(Value : TDBTransaction); virtual;
     Procedure CheckDatabase;
     Procedure CheckDatabase;
   public
   public
     constructor Create(AOwner : TComponent); override;
     constructor Create(AOwner : TComponent); override;
     destructor Destroy; override;
     destructor Destroy; override;
+    procedure Execute; override;
     procedure ExecuteScript;
     procedure ExecuteScript;
-    Property Script : TStrings Read FScript Write SetScript;
+  published
     Property DataBase : TDatabase Read FDatabase Write SetDatabase;
     Property DataBase : TDatabase Read FDatabase Write SetDatabase;
     Property Transaction : TDBTransaction Read FTransaction Write SetTransaction;
     Property Transaction : TDBTransaction Read FTransaction Write SetTransaction;
+    property OnDirective: TSQLScriptDirectiveEvent read FOnDirective write FOnDirective;
+    property Directives;
+    property Defines;
+    property Script;
+    property Terminator;
+    property CommentsinSQL;
+    property UseSetTerm;
+    property UseCommit;
+    property UseDefines;
+    property OnException;
   end;
   end;
 
 
   { TSQLConnector }
   { TSQLConnector }
@@ -1521,7 +1534,8 @@ begin
     If Assigned(AValue) then
     If Assigned(AValue) then
       begin
       begin
       AValue.FreeNotification(Self);  
       AValue.FreeNotification(Self);  
-      FMasterLink:=TMasterParamsDataLink.Create(Self);
+      If (FMasterLink=Nil) then
+        FMasterLink:=TMasterParamsDataLink.Create(Self);
       FMasterLink.Datasource:=AValue;
       FMasterLink.Datasource:=AValue;
       end
       end
     else
     else
@@ -1548,9 +1562,29 @@ end;
 
 
 { TSQLScript }
 { TSQLScript }
 
 
-procedure TSQLScript.SetScript(const AValue: TStrings);
+procedure TSQLScript.ExecuteStatement(SQLStatement: TStrings;
+  var StopExecution: Boolean);
+begin
+  fquery.SQL.assign(SQLStatement);
+  fquery.ExecSQL;
+end;
+
+procedure TSQLScript.ExecuteDirective(Directive, Argument: String;
+  var StopExecution: Boolean);
 begin
 begin
-  FScript.assign(AValue);
+  if assigned (FOnDirective) then
+    FOnDirective (Self, Directive, Argument, StopExecution);
+end;
+
+procedure TSQLScript.ExecuteCommit;
+begin
+  if FTransaction is TSQLTransaction then
+    TSQLTransaction(FTransaction).CommitRetaining
+  else
+    begin
+    FTransaction.Active := false;
+    FTransaction.Active := true;
+    end;
 end;
 end;
 
 
 procedure TSQLScript.SetDatabase(Value: TDatabase);
 procedure TSQLScript.SetDatabase(Value: TDatabase);
@@ -1572,49 +1606,28 @@ end;
 constructor TSQLScript.Create(AOwner: TComponent);
 constructor TSQLScript.Create(AOwner: TComponent);
 begin
 begin
   inherited Create(AOwner);
   inherited Create(AOwner);
-  FScript := TStringList.Create;
-  FQuery := TCustomSQLQuery.Create(nil);
+  FQuery := TCustomSQLQuery.Create(nil); 
 end;
 end;
 
 
 destructor TSQLScript.Destroy;
 destructor TSQLScript.Destroy;
 begin
 begin
-  FScript.Free;
   FQuery.Free;
   FQuery.Free;
   inherited Destroy;
   inherited Destroy;
 end;
 end;
 
 
-procedure TSQLScript.ExecuteScript;
-
-var BufStr         : String;
-    pBufStatStart,
-    pBufPos        : PChar;
-    Statement      : String;
-
+procedure TSQLScript.Execute;
 begin
 begin
   FQuery.DataBase := FDatabase;
   FQuery.DataBase := FDatabase;
   FQuery.Transaction := FTransaction;
   FQuery.Transaction := FTransaction;
+  inherited Execute;
+end;
 
 
-  BufStr := FScript.Text;
-  pBufPos := @BufStr[1];
-
-  repeat
-
-  pBufStatStart := pBufPos;
-  repeat
-  inc(pBufPos);
-  until (pBufPos^ = ';') or (pBufPos^ = #0);
-  SetLength(statement,pbufpos-pBufStatStart);
-  move(pBufStatStart^,Statement[1],pbufpos-pBufStatStart);
-  if trim(statement) <> '' then
-    begin
-    fquery.SQL.Text := Statement;
-    fquery.ExecSQL;
-    inc(pBufPos);
-    end;
-
-  until pBufPos^ = #0;
+procedure TSQLScript.ExecuteScript;
+begin
+  Execute;
 end;
 end;
 
 
+
 { Connection definitions }
 { Connection definitions }
 
 
 Var
 Var

+ 808 - 0
packages/fcl-db/tests/testsqlscript.pas

@@ -0,0 +1,808 @@
+{
+    This file is part of the Free Pascal run time library.
+    Copyright (c) 2008 by the Free Pascal development team
+
+    FPCUnit SQLScript test.
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit testcsqlscript;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, testregistry, sqlscript, fpcunit;
+
+type
+
+  { TMyScript }
+
+  TMyScript = class (TCustomSQLScript)
+  private
+    FExcept: string;
+    FStatements : TStrings;
+    FDirectives : TStrings;
+    FCommits : integer;
+  protected
+    procedure ExecuteStatement (SQLStatement: TStrings; var StopExecution: Boolean); override;
+    procedure ExecuteDirective (Directive, Argument: String; var StopExecution: Boolean); override;
+    procedure ExecuteCommit; override;
+    procedure DefaultDirectives; override;
+  public
+    constructor create (AnOwner: TComponent); override;
+    destructor destroy; override;
+    function StatementsExecuted : string;
+    function DirectivesExecuted : string;
+    property DoException : string read FExcept write FExcept;
+    property Aborted;
+    property Line;
+    property Directives;
+    property Defines;
+    property Script;
+    property Terminator;
+    property CommentsinSQL;
+    property UseSetTerm;
+    property UseCommit;
+    property UseDefines;
+    property OnException;
+  end;
+
+  { TTestSQLScript }
+
+  TTestSQLScript = class (TTestCase)
+  private
+    Script : TMyScript;
+    exceptionstatement,
+    exceptionmessage : string;
+    UseContinue : boolean;
+    procedure Add (s :string);
+    procedure AssertStatDir (Statements, Directives : string);
+    procedure DoExecution;
+    procedure ExceptionHandler(Sender: TObject; Statement: TStrings; TheException: Exception; var Continue: boolean);
+    procedure TestDirectiveOnException3;
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+  published
+    procedure TestCreateDefaults;
+    procedure TestTerminator;
+    procedure TestSetTerm;
+    procedure TestUseSetTerm;
+    procedure TestComments;
+    procedure TestUseComments;
+    procedure TestCommit;
+    procedure TestUseCommit;
+    procedure TestDefine;
+    procedure TestUndefine;
+    procedure TestUndef;
+    procedure TestIfdef1;
+    procedure TestIfdef2;
+    procedure TestIfndef1;
+    procedure TestIfndef2;
+    procedure TestElse1;
+    procedure TestElse2;
+    procedure TestEndif1;
+    procedure TestEndif2;
+    procedure TestUseDefines;
+    procedure TestTermInComment;
+    procedure TestTermInQuotes1;
+    procedure TestTermInQuotes2;
+    procedure TestCommentInComment;
+    procedure TestCommentInQuotes1;
+    procedure TestCommentInQuotes2;
+    procedure TestQuote1InComment;
+    procedure TestQuote2InComment;
+    procedure TestQuoteInQuotes1;
+    procedure TestQuoteInQuotes2;
+    procedure TestStatementStop;
+    procedure TestDirectiveStop;
+    procedure TestStatementExeception;
+    procedure TestDirectiveException;
+    procedure TestCommitException;
+    procedure TestStatementOnExeception1;
+    procedure TestStatementOnExeception2;
+    procedure TestDirectiveOnException1;
+    procedure TestDirectiveOnException2;
+    procedure TestCommitOnException1;
+    procedure TestCommitOnException2;
+  end;
+
+  { TTestEventSQLScript }
+
+  TTestEventSQLScript = class (TTestCase)
+  private
+    Script : TEventSQLScript;
+    StopToSend : boolean;
+    Received : string;
+    notifycount : integer;
+    LastSender : TObject;
+    procedure Notify (Sender : TObject);
+    procedure NotifyStatement (Sender: TObject; SQL_Statement: TStrings; var StopExecution: Boolean);
+    procedure NotifyDirective (Sender: TObject; Directive, Argument: AnsiString; var StopExecution: Boolean);
+  protected
+    procedure SetUp; override;
+    procedure TearDown; override;
+  published
+    procedure TestStatement;
+    procedure TestStatementStop;
+    procedure TestDirective;
+    procedure TestDirectiveStop;
+    procedure TestCommit;
+    procedure TestBeforeExec;
+    procedure TestAfterExec;
+  end;
+
+implementation
+
+{ TMyScript }
+
+procedure TMyScript.ExecuteStatement(SQLStatement: TStrings; var StopExecution: Boolean);
+var s : string;
+    r : integer;
+begin
+  if (SQLStatement.count = 1) and (compareText(SQLStatement[0],'END')=0) then
+    StopExecution := true;
+  s := '';
+  for r := 0 to SQLstatement.count-1 do
+    begin
+    if s <> '' then
+      s := s + ' ';
+    s := s + SQLStatement[r];
+    end;
+  FStatements.Add (s);
+  if DoException <> '' then
+    raise exception.create(DoException);
+end;
+
+procedure TMyScript.ExecuteDirective(Directive, Argument: String; var StopExecution: Boolean);
+begin
+  if Directive = 'STOP' then
+    StopExecution := true;
+  if Argument = '' then
+    FDirectives.Add (Directive)
+  else
+    FDirectives.Add (format('%s(%s)', [Directive, Argument]));
+  if DoException <> '' then
+    raise exception.create(DoException);
+end;
+
+procedure TMyScript.ExecuteCommit;
+begin
+  inc (FCommits);
+  if DoException <> '' then
+    raise exception.create(DoException);
+end;
+
+procedure TMyScript.DefaultDirectives;
+begin
+  inherited DefaultDirectives;
+  directives.add ('STOP');
+end;
+
+constructor TMyScript.create (AnOwner: TComponent);
+begin
+  inherited create (AnOwner);
+  FStatements := TStringlist.Create;
+  FDirectives := TStringlist.Create;
+  FCommits := 0;
+  DoException := '';
+end;
+
+destructor TMyScript.destroy;
+begin
+  FStatements.Free;
+  FDirectives.Free;
+  inherited destroy;
+end;
+
+function TMyScript.StatementsExecuted: string;
+begin
+  result := FStatements.Commatext;
+end;
+
+function TMyScript.DirectivesExecuted: string;
+begin
+  result := FDirectives.Commatext;
+end;
+
+
+{ TTestSQLScript }
+
+procedure TTestSQLScript.Add(s: string);
+begin
+  Script.Script.Add (s);
+end;
+
+procedure TTestSQLScript.AssertStatDir(Statements, Directives: string);
+begin
+  AssertEquals ('Executed Statements', Statements, script.StatementsExecuted);
+  AssertEquals ('Executed Directives', Directives, script.DirectivesExecuted);
+end;
+
+procedure TTestSQLScript.DoExecution;
+begin
+  script.execute;
+end;
+
+procedure TTestSQLScript.ExceptionHandler(Sender: TObject; Statement: TStrings;
+  TheException: Exception; var Continue: boolean);
+var r : integer;
+    s : string;
+begin
+  Continue := UseContinue;
+  if Statement.count > 0 then
+    s := Statement[0];
+  for r := 1 to Statement.count-1 do
+    s := s + ',' + Statement[r];
+  exceptionstatement := s;
+  exceptionmessage := TheException.message;
+end;
+
+procedure TTestSQLScript.SetUp;
+begin
+  inherited SetUp;
+  Script := TMyscript.Create (nil);
+end;
+
+procedure TTestSQLScript.TearDown;
+begin
+  Script.Free;
+  inherited TearDown;
+end;
+
+procedure TTestSQLScript.TestCreateDefaults;
+begin
+  with Script do
+    begin
+    AssertEquals ('Terminator', ';', Terminator);
+    AssertTrue ('UseCommit', UseCommit);
+    AssertTrue ('UseSetTerm', UseSetTerm);
+    AssertTrue ('UseDefines', UseDefines);
+    AssertTrue ('CommentsInSQL', CommentsInSQL);
+    AssertFalse ('Aborted', Aborted);
+    AssertEquals ('Line', 0, Line);
+    AssertEquals ('Defines', 0, Defines.count);
+    AssertEquals ('Directives', 10, Directives.count);
+    end;
+end;
+
+procedure TTestSQLScript.TestTerminator;
+begin
+  script.terminator := '!';
+  Add('doe!iets!');
+  Add('anders!');
+  script.execute;
+  AssertStatDir('doe,iets,anders', '');
+end;
+
+procedure TTestSQLScript.TestSetTerm;
+begin
+  script.UseSetTerm:=true;
+  Add('SET TERM !;');
+  script.execute;
+  AssertEquals ('terminator', '!', script.terminator);
+  AssertStatDir('', '');
+end;
+
+procedure TTestSQLScript.TestUseSetTerm;
+begin
+  script.UseSetTerm:=false;
+  Script.Directives.Add ('SET TERM');
+  Add('SET TERM !;');
+  script.execute;
+  AssertEquals ('terminator', ';', script.terminator);
+  AssertStatDir('', '"SET TERM(!)"');
+end;
+
+procedure TTestSQLScript.TestComments;
+begin
+  script.CommentsInSQL := true;
+  Add('/* comment */');
+  Add('statement;');
+  script.execute;
+  AssertStatDir ('"/* comment */ statement"', '');
+end;
+
+procedure TTestSQLScript.TestUseComments;
+begin
+  script.CommentsInSQL := false;
+  Add('/* comment */');
+  Add('statement;');
+  script.execute;
+  AssertStatDir ('statement', '');
+end;
+
+procedure TTestSQLScript.TestCommit;
+begin
+  script.UseCommit := true;
+  Add('commit;');
+  script.execute;
+  AssertEquals ('Commits', 1, script.FCommits);
+  AssertStatDir ('', '');
+end;
+
+procedure TTestSQLScript.TestUseCommit;
+begin
+  script.UseCommit := false;
+  with script.Directives do
+    Delete(IndexOf('COMMIT'));
+  Add('commit;');
+  script.execute;
+  AssertEquals ('Commits', 0, script.FCommits);
+  AssertStatDir ('commit', '');
+end;
+
+procedure TTestSQLScript.TestDefine;
+begin
+  script.UseDefines := true;
+  Add ('#define iets;');
+  script.execute;
+  AssertStatDir ('', '');
+  AssertEquals ('Aantal defines', 1, script.defines.count);
+  AssertEquals ('Juiste define', 'iets', script.Defines[0]);
+end;
+
+procedure TTestSQLScript.TestUndefine;
+begin
+  script.UseDefines := true;
+  script.defines.Add ('iets');
+  Add ('#undefine iets;');
+  script.execute;
+  AssertStatDir ('', '');
+  AssertEquals ('Aantal defines', 0, script.defines.count);
+end;
+
+procedure TTestSQLScript.TestUndef;
+begin
+  script.UseDefines := true;
+  script.defines.Add ('iets');
+  Add ('#Undef iets;');
+  script.execute;
+  AssertStatDir ('', '');
+  AssertEquals ('Aantal defines', 0, script.defines.count);
+end;
+
+procedure TTestSQLScript.TestIfdef1;
+begin
+  script.UseDefines := true;
+  script.defines.add ('iets');
+  Add('#ifdef iets;');
+  Add('doe iets;');
+  script.execute;
+  AssertStatDir('"doe iets"', '');
+end;
+
+procedure TTestSQLScript.TestIfdef2;
+begin
+  script.UseDefines := true;
+  Add('#ifdef iets;');
+  Add('doe iets;');
+  script.execute;
+  AssertStatDir('', '');
+end;
+
+procedure TTestSQLScript.TestIfndef1;
+begin
+  script.UseDefines := true;
+  Add('#ifndef iets;');
+  Add('doe iets;');
+  script.execute;
+  AssertStatDir('"doe iets"', '');
+end;
+
+procedure TTestSQLScript.TestIfndef2;
+begin
+  script.UseDefines := true;
+  script.defines.add ('iets');
+  Add('#ifndef iets;');
+  Add('doe iets;');
+  script.execute;
+  AssertStatDir('', '');
+end;
+
+procedure TTestSQLScript.TestElse1;
+begin
+  script.UseDefines := true;
+  script.defines.add ('iets');
+  Add('#ifdef iets;');
+  Add('doe iets;');
+  add('#else;');
+  add('anders;');
+  script.execute;
+  AssertStatDir('"doe iets"', '');
+end;
+
+procedure TTestSQLScript.TestElse2;
+begin
+  script.UseDefines := true;
+  script.defines.add ('iets');
+  Add('#ifndef iets;');
+  Add('doe iets;');
+  add('#else;');
+  add('anders;');
+  script.execute;
+  AssertStatDir('anders', '');
+end;
+
+procedure TTestSQLScript.TestEndif1;
+begin
+  script.UseDefines := true;
+  Add('#ifdef iets;');
+  Add('doe iets;');
+  add('#endif;');
+  add('anders;');
+  script.execute;
+  AssertStatDir('anders', '');
+end;
+
+procedure TTestSQLScript.TestEndif2;
+begin
+  script.UseDefines := true;
+  Add('#ifndef iets;');
+  Add('doe iets;');
+  add('#endif;');
+  add('anders;');
+  script.execute;
+  AssertStatDir('"doe iets",anders', '');
+end;
+
+procedure TTestSQLScript.TestUseDefines;
+begin
+  script.UseDefines := false;
+  Add('#ifndef iets;');
+  Add('doe iets;');
+  add('#endif;');
+  add('anders;');
+  script.execute;
+  AssertStatDir('"doe iets",anders', '#IFNDEF(iets),#ENDIF');
+end;
+
+procedure TTestSQLScript.TestTermInComment;
+begin
+  script.CommentsInSQL := false;
+  Add('/* terminator ; */iets;');
+  script.execute;
+  AssertStatDir('iets', '');
+end;
+
+procedure TTestSQLScript.TestTermInQuotes1;
+begin
+  script.CommentsInSQL := false;
+  Add('iets '';'';');
+  script.execute;
+  AssertStatDir('"iets '';''"', '');
+end;
+
+procedure TTestSQLScript.TestTermInQuotes2;
+begin
+  script.CommentsInSQL := false;
+  Add('iets ";";');
+  script.execute;
+  AssertStatDir('"iets "";"""', '');
+end;
+
+procedure TTestSQLScript.TestCommentInComment;
+begin
+  script.CommentsInSQL := false;
+  Add('/* meer /* */iets;');
+  script.execute;
+  AssertStatDir('iets', '');
+end;
+
+procedure TTestSQLScript.TestCommentInQuotes1;
+begin
+  script.CommentsInSQL := false;
+  Add('iets ''/* meer */'';');
+  script.execute;
+  AssertStatDir('"iets ''/* meer */''"', '');
+end;
+
+procedure TTestSQLScript.TestCommentInQuotes2;
+begin
+  script.CommentsInSQL := false;
+  Add('iets "/* meer */";');
+  script.execute;
+  AssertStatDir('"iets ""/* meer */"""', '');
+end;
+
+procedure TTestSQLScript.TestQuote1InComment;
+begin
+  script.CommentsInSQL := false;
+  Add('/* s''morgens */iets;');
+  script.execute;
+  AssertStatDir('iets', '');
+end;
+
+procedure TTestSQLScript.TestQuote2InComment;
+begin
+  script.CommentsInSQL := false;
+  Add('/* s"morgens */iets;');
+  script.execute;
+  AssertStatDir('iets', '');
+end;
+
+procedure TTestSQLScript.TestQuoteInQuotes1;
+begin
+  script.CommentsInSQL := false;
+  Add('iets ''s"morgens'';');
+  script.execute;
+  AssertStatDir('"iets ''s""morgens''"', '');
+end;
+
+procedure TTestSQLScript.TestQuoteInQuotes2;
+begin
+  script.CommentsInSQL := false;
+  Add('iets "s''morgens";');
+  script.execute;
+  AssertStatDir('"iets ""s''morgens"""', '');
+end;
+
+procedure TTestSQLScript.TestStatementStop;
+begin
+  Add('END;meer;');
+  script.execute;
+  AssertStatDir('END', '');
+end;
+
+procedure TTestSQLScript.TestDirectiveStop;
+begin
+  Add('Stop;meer;');
+  script.execute;
+  AssertStatDir('', 'STOP');
+end;
+
+procedure TTestSQLScript.TestStatementExeception;
+begin
+  Add('iets;');
+  script.DoException:='FOUT';
+  AssertException (exception, @DoExecution);
+  AssertStatDir('iets', '');
+end;
+
+procedure TTestSQLScript.TestDirectiveException;
+begin
+  Add('iets;');
+  script.Directives.Add('IETS');
+  script.DoException := 'FOUT';
+  AssertException (exception, @DoExecution);
+  AssertStatDir('', 'IETS');
+end;
+
+procedure TTestSQLScript.TestCommitException;
+begin
+  Add ('commit;');
+  script.DoException := 'FOUT';
+  AssertException (exception, @DoExecution);
+  AssertStatDir('', '');
+  AssertEquals ('Commit count', 1, Script.FCommits);
+end;
+
+procedure TTestSQLScript.TestStatementOnExeception1;
+begin
+  UseContinue := true;
+  script.DoException := 'Fout';
+  Add ('foutief;');
+  script.OnException:=@ExceptionHandler;
+  Script.Execute;
+  AssertEquals ('exception message', 'Fout', exceptionmessage);
+  AssertEquals ('exception statement', 'foutief', exceptionstatement);
+end;
+
+procedure TTestSQLScript.TestStatementOnExeception2;
+begin
+  UseContinue := false;
+  script.DoException := 'Fout';
+  Add ('foutief;');
+  script.OnException:=@ExceptionHandler;
+  AssertException (exception, @DoExecution);
+  AssertEquals ('exception message', 'Fout', exceptionmessage);
+  AssertEquals ('exception statement', 'foutief', exceptionstatement);
+end;
+
+procedure TTestSQLScript.TestDirectiveOnException1;
+begin
+  UseContinue := true;
+  script.DoException := 'Fout';
+  Add ('foutief;');
+  Script.Directives.Add ('FOUTIEF');
+  script.OnException:=@ExceptionHandler;
+  Script.Execute;
+  AssertEquals ('exception message', 'Fout', exceptionmessage);
+  AssertEquals ('exception statement', 'FOUTIEF', exceptionstatement);
+end;
+
+procedure TTestSQLScript.TestDirectiveOnException2;
+begin
+  UseContinue := False;
+  script.DoException := 'Fout';
+  Add ('foutief;');
+  Script.Directives.Add ('FOUTIEF');
+  script.OnException:=@ExceptionHandler;
+  AssertException (exception, @DoExecution);
+  AssertEquals ('exception message', 'Fout', exceptionmessage);
+  AssertEquals ('exception statement', 'FOUTIEF', exceptionstatement);
+end;
+
+procedure TTestSQLScript.TestDirectiveOnException3;
+begin
+  UseContinue := true;
+  script.DoException := 'Fout';
+  Add ('foutief probleem;');
+  Script.Directives.Add ('FOUTIEF');
+  script.OnException:=@ExceptionHandler;
+  Script.Execute;
+  AssertEquals ('exception message', 'Fout', exceptionmessage);
+  AssertEquals ('exception statement', 'FOUTIEF,probleem', exceptionstatement);
+end;
+
+procedure TTestSQLScript.TestCommitOnException1;
+begin
+  UseContinue := true;
+  script.DoException := 'Fout';
+  Add ('Commit;');
+  script.OnException:=@ExceptionHandler;
+  Script.Execute;
+  AssertEquals ('exception message', 'Fout', exceptionmessage);
+  AssertEquals ('exception statement', 'COMMIT', exceptionstatement);
+  AssertEquals ('commit count', 1, Script.FCommits);
+end;
+
+procedure TTestSQLScript.TestCommitOnException2;
+begin
+  UseContinue := false;
+  script.DoException := 'Fout';
+  Add ('Commit;');
+  script.OnException:=@ExceptionHandler;
+  AssertException (exception, @DoExecution);
+  AssertEquals ('exception message', 'Fout', exceptionmessage);
+  AssertEquals ('exception statement', 'COMMIT', exceptionstatement);
+  AssertEquals ('commit count', 1, Script.FCommits);
+end;
+
+{ TTestEventSQLScript }
+
+procedure TTestEventSQLScript.Notify(Sender: TObject);
+begin
+  inc (NotifyCount);
+  LastSender := Sender;
+end;
+
+procedure TTestEventSQLScript.NotifyStatement(Sender: TObject;
+  SQL_Statement: TStrings; var StopExecution: Boolean);
+var r : integer;
+    s : string;
+begin
+  StopExecution := StopToSend;
+  if SQL_Statement.count > 0 then
+    begin
+    s := SQL_Statement[0];
+    for r := 1 to SQL_Statement.count-1 do
+      s := s + ';' + SQL_Statement[r];
+    if SQL_Statement.count > 1 then
+      s := '"' + s + '"';
+    end
+  else
+    s := '';
+  if received <> '' then
+    received := received + ';' + s
+  else
+    received := s;
+  LastSender := Sender;
+end;
+
+procedure TTestEventSQLScript.NotifyDirective(Sender: TObject; Directive,
+  Argument: AnsiString; var StopExecution: Boolean);
+var s : string;
+begin
+  StopExecution := StopToSend;
+  if Argument = '' then
+    s := Directive
+  else
+    s := format ('%s(%s)', [Directive, Argument]);
+  if received <> '' then
+    received := received + ';' + s
+  else
+    received := s;
+  LastSender := Sender;
+end;
+
+procedure TTestEventSQLScript.SetUp;
+begin
+  inherited SetUp;
+  Script := TEventSQLScript.Create (nil);
+  notifycount := 0;
+  Received := '';
+  LastSender := nil;
+end;
+
+procedure TTestEventSQLScript.TearDown;
+begin
+  Script.Free;
+  inherited TearDown;
+end;
+
+procedure TTestEventSQLScript.TestStatement;
+begin
+  StopToSend:=false;
+  Script.OnSQLStatement := @NotifyStatement;
+  Script.Script.Text := 'stat1;stat2;';
+  script.execute;
+  AssertEquals ('Received', 'stat1;stat2', received);
+  AssertSame ('Sender', script, LastSender);
+end;
+
+procedure TTestEventSQLScript.TestStatementStop;
+begin
+  StopToSend:=true;
+  Script.OnSQLStatement := @NotifyStatement;
+  Script.Script.Text := 'stat1;stat2;';
+  script.execute;
+  AssertEquals ('Received', 'stat1', received);
+  AssertSame ('Sender', script, LastSender);
+end;
+
+procedure TTestEventSQLScript.TestDirective;
+begin
+  StopToSend:=false;
+  Script.OnSQLStatement := @NotifyStatement;
+  Script.OnDirective := @NotifyDirective;
+  script.Directives.Add ('STAT1');
+  Script.Script.Text := 'stat1 ik;stat2;';
+  script.execute;
+  AssertEquals ('Received', 'STAT1(ik);stat2', received);
+  AssertSame ('Sender', script, LastSender);
+end;
+
+procedure TTestEventSQLScript.TestDirectiveStop;
+begin
+  StopToSend:=true;
+  Script.OnSQLStatement := @NotifyStatement;
+  Script.OnDirective := @NotifyDirective;
+  script.Directives.Add ('STAT1');
+  Script.Script.Text := 'stat1 ik;stat2;';
+  script.execute;
+  AssertEquals ('Received', 'STAT1(ik)', received);
+  AssertSame ('Sender', script, LastSender);
+end;
+
+procedure TTestEventSQLScript.TestCommit;
+begin
+  Script.OnCommit := @Notify;
+  Script.Script.Text := 'iets; commit; anders;';
+  script.execute;
+  AssertEquals ('NotifyCount', 1, NotifyCount);
+  AssertSame ('Sender', script, LastSender);
+end;
+
+procedure TTestEventSQLScript.TestBeforeExec;
+begin
+  Script.BeforeExecute := @Notify;
+  Script.Script.Text := 'update iets; anders iets;';
+  script.execute;
+  AssertEquals ('NotifyCount', 1, NotifyCount);
+  AssertSame ('Sender', script, LastSender);
+end;
+
+procedure TTestEventSQLScript.TestAfterExec;
+begin
+  Script.AfterExecute := @Notify;
+  Script.Script.Text := 'update iets; anders iets; en meer;';
+  script.execute;
+  AssertEquals ('NotifyCount', 1, NotifyCount);
+  AssertSame ('Sender', script, LastSender);
+end;
+
+initialization
+
+  RegisterTests ([TTestSQLScript, TTestEventSQLScript]);
+
+end.
+