Ver código fonte

Merged revisions 1655-1657,1667,1677,1680 via svnmerge from
svn+ssh://www.freepascal.org/FPC/svn/fpc/trunk

........
r1655 | joost | 2005-11-05 12:38:49 +0100 (Sat, 05 Nov 2005) | 1 line

+ added support for ftFixedChar
........
r1656 | joost | 2005-11-05 12:48:16 +0100 (Sat, 05 Nov 2005) | 1 line

+ Centralized all parameter-parsing in TParams.ParseSQL (bug 4374)
........
r1657 | joost | 2005-11-05 13:33:06 +0100 (Sat, 05 Nov 2005) | 1 line

+ Removed TDatabase.Notification (as suggested by Martin Schreiber, bug #4468
........
r1667 | joost | 2005-11-05 23:35:06 +0100 (Sat, 05 Nov 2005) | 1 line

+ localized Baseres to avoid a second clear
........
r1677 | joost | 2005-11-06 10:50:19 +0100 (Sun, 06 Nov 2005) | 5 lines

+ Patch from Bram Kuijvenhoven:
- pass ODBC api call return values to ODBCCheckResult function explicitly
instead of using SQLGetDiagRec (improves error reporting with unixODBC)
- fixed TODBCConnection.Param property; no semicolons were inserted to separate
the parameters in the connection string
........
r1680 | joost | 2005-11-06 19:50:12 +0100 (Sun, 06 Nov 2005) | 1 line

+ Added empty TMySQLconnection.UnPrepareStatement
........

git-svn-id: branches/fixes_2_0@1686 -

joost 20 anos atrás
pai
commit
40b7a41d70

+ 1 - 1
compiler/msgidx.inc

@@ -661,7 +661,7 @@ const
   option_info=11024;
   option_help_pages=11025;
 
-  MsgTxtSize = 39102;
+  MsgTxtSize = 39103;
 
   MsgIdxMax : array[1..20] of longint=(
     19,73,216,59,59,47,100,20,135,60,

+ 23 - 23
compiler/msgtxt.inc

@@ -805,7 +805,7 @@ const msgtxt : array[0..000162,1..240] of char=(
   '*g2gg_use gsym'#010+
   '*g2gh_use heap trace unit (for memory leak debugging)'#010+
   '*g2gl_use line info unit to show more info for backtraces'#010+
-  '*g2gv_generates programs tracable with val','grind'#010+
+  '*g2gv_generates programs traceable with va','lgrind'#010+
   '*g2gw_generate dwarf debugging info'#010+
   '**1i_information'#010+
   '**2iD_return compiler date'#010+
@@ -814,55 +814,55 @@ const msgtxt : array[0..000162,1..240] of char=(
   '**2iSP_return compiler processor'#010+
   '**2iTO_return target OS'#010+
   '**2iTP_return target processor'#010+
-  '**1I<x>_ad','ds <x> to include path'#010+
+  '**1I<x>_a','dds <x> to include path'#010+
   '**1k<x>_Pass <x> to the linker'#010+
   '**1l_write logo'#010+
   '**1M<x>_set language mode to <x>'#010+
   '**2Mfpc_free pascal dialect (default)'#010+
   '**2Mobjfpc_switch some Delphi 2 extensions on'#010+
   '**2Mdelphi_tries to be Delphi compatible'#010+
-  '**2Mtp_tries',' to be TP/BP 7.0 compatible'#010+
+  '**2Mtp_trie','s to be TP/BP 7.0 compatible'#010+
   '**2Mgpc_tries to be gpc compatible'#010+
   '**2Mmacpas_tries to be compatible to the macintosh pascal dialects'#010+
   '**1n_don'#039't read the default config file'#010+
   '**1o<x>_change the name of the executable produced to <x>'#010+
-  '**1O<x>_opti','mizations:'#010+
+  '**1O<x>_opt','imizations:'#010+
   '3*2Oa_<type>=<values> set alignment'#010+
   '3*2Og_generate smaller code'#010+
   '3*2OG_generate faster code (default)'#010+
   '**2Or_keep certain variables in registers'#010+
   '3*2Ou_enable uncertain optimizations (see docs)'#010+
-  '3*2O1_level 1 optimizations (quick opt','imizations)'#010+
+  '3*2O1_level 1 optimizations (quick op','timizations)'#010+
   '3*2O2_level 2 optimizations (-O1 + slower optimizations)'#010+
   '3*2O3_level 3 optimizations (-O2 repeatedly, max 5 times)'#010+
   '3*2Op<x>_target processor:'#010+
   '3*3Op1_set target processor to 386/486'#010+
-  '3*3Op2_set target processor to Pentium/PentiumM','MX (tm)'#010+
+  '3*3Op2_set target processor to Pentium/Pentium','MMX (tm)'#010+
   '3*3Op3_set target processor to PPro/PII/c6x86/K6 (tm)'#010+
   '6*2Og_generate smaller code'#010+
   '6*2OG_generate faster code (default)'#010+
   '6*2Ox_optimize maximum (still BUGGY!!!)'#010+
   '6*2O0_set target processor to a MC68000'#010+
-  '6*2O2_set target processor to a M','C68020+ (default)'#010+
+  '6*2O2_set target processor to a ','MC68020+ (default)'#010+
   '**1pg_generate profile code for gprof (defines FPC_PROFILE)'#010+
   '**1R<x>_assembler reading style:'#010+
   '**2Rdefault_use default assembler'#010+
   '3*2Ratt_read AT&T style assembler'#010+
   '3*2Rintel_read Intel style assembler'#010+
-  '6*2RMOT_read motorola st','yle assembler'#010+
+  '6*2RMOT_read motorola s','tyle assembler'#010+
   '**1S<x>_syntax options:'#010+
   '**2S2_same as -Mobjfpc'#010+
   '**2Sc_supports operators like C (*=,+=,/= and -=)'#010+
   '**2Sa_include assertion code.'#010+
   '**2Sd_same as -Mdelphi'#010+
   '**2Se<x>_error options. <x> is a combination of the following:'#010+
-  '**3*_<n> : co','mpiler stops after the <n> errors (default is 1)'#010+
+  '**3*_<n> : c','ompiler stops after the <n> errors (default is 1)'#010+
   '**3*_w : compiler stops also after warnings'#010+
   '**3*_n : compiler stops also after notes'#010+
   '**3*_h : compiler stops also after hints'#010+
   '**2Sg_allow LABEL and GOTO'#010+
   '**2Sh_Use ansistrings'#010+
-  '**2Si_support C+','+ styled INLINE'#010+
+  '**2Si_support C','++ styled INLINE'#010+
   '**2Sk_load fpcylix unit'#010+
   '**2SI<x>_set interface style to <x>'#010+
   '**3SIcom_COM compatible interface (default)'#010+
@@ -870,19 +870,19 @@ const msgtxt : array[0..000162,1..240] of char=(
   '**2Sm_support macros like C (global)'#010+
   '**2So_same as -Mtp'#010+
   '**2Sp_same as -Mgpc'#010+
-  '**2Ss_','constructor name must be init (destructor must be done)'#010+
+  '**2Ss','_constructor name must be init (destructor must be done)'#010+
   '**2St_allow static keyword in objects'#010+
   '**1s_don'#039't call assembler and linker'#010+
   '**2sh_Generate script to link on host'#010+
   '**2st_Generate script to link on target'#010+
-  '**2sr_Skip register allocation ','phase (use with -alr)'#010+
+  '**2sr_Skip register allocation',' phase (use with -alr)'#010+
   '**1T<x>_Target operating system:'#010+
   '3*2Temx_OS/2 via EMX (including EMX/RSX extender)'#010+
   '3*2Tfreebsd_FreeBSD'#010+
   '3*2Tgo32v2_Version 2 of DJ Delorie DOS extender'#010+
   '3*2Tlinux_Linux'#010+
   '3*2Tnetbsd_NetBSD'#010+
-  '3*2Tnetware_Novell Netware Module',' (clib)'#010+
+  '3*2Tnetware_Novell Netware Modul','e (clib)'#010+
   '3*2Tnetwlibc_Novell Netware Module (libc)'#010+
   '3*2Topenbsd_OpenBSD'#010+
   '3*2Tos2_OS/2 / eComStation'#010+
@@ -891,7 +891,7 @@ const msgtxt : array[0..000162,1..240] of char=(
   '3*2Twdosx_WDOSX DOS extender'#010+
   '3*2Twin32_Windows 32 Bit'#010+
   '4*2Tlinux_Linux'#010+
-  '6*2Tami','ga_Commodore Amiga'#010+
+  '6*2Tam','iga_Commodore Amiga'#010+
   '6*2Tatari_Atari ST/STe/TT'#010+
   '6*2Tlinux_Linux-68k'#010+
   '6*2Tmacos_Macintosh m68k (not supported)'#010+
@@ -899,7 +899,7 @@ const msgtxt : array[0..000162,1..240] of char=(
   'A*2Tlinux_Linux'#010+
   'P*2Tdarwin_Darwin and MacOS X on PowerPC'#010+
   'P*2Tlinux_Linux on PowerPC'#010+
-  'P*2Tmacos_MacOS (classic) on Pow','erPC'#010+
+  'P*2Tmacos_MacOS (classic) on Po','werPC'#010+
   'P*2Tmorphos_MorphOS'#010+
   'S*2Tlinux_Linux'#010+
   '**1u<x>_undefines the symbol <x>'#010+
@@ -907,42 +907,42 @@ const msgtxt : array[0..000162,1..240] of char=(
   '**2Un_don'#039't check the unit name'#010+
   '**2Ur_generate release unit files'#010+
   '**2Us_compile a system unit'#010+
-  '**1v<x>_Be verbose. <x> is a combination of the follo','wing letters:'#010+
+  '**1v<x>_Be verbose. <x> is a combination of the foll','owing letters:'#010+
   '**2*_e : Show errors (default)       0 : Show nothing (except errors)'#010+
   '**2*_w : Show warnings               u : Show unit info'#010+
   '**2*_n : Show notes                  t : Show tried/used files'#010+
-  '**2*_h : Show hints                  ','c : Show conditionals'#010+
+  '**2*_h : Show hints                 ',' c : Show conditionals'#010+
   '**2*_i : Show general info           d : Show debug info'#010+
   '**2*_l : Show linenumbers            r : Rhide/GCC compatibility mode'#010+
   '**2*_a : Show everything             x : Executable info (Win32 only)'#010+
-  '**2*_b : Write file n','ames messages with full path'#010+
+  '**2*_b : Write file ','names messages with full path'#010+
   '**2*_v : write fpcdebug.txt with     p : Write tree.log with parse tre'+
   'e'#010+
   '**2*_    lots of debugging info'#010+
   '3*1W<x>_Win32-like target options'#010+
   '3*2WB_Create a relocatable image'#010+
-  '3*2WB<x>_Set Image base to Hexadecimal <','x> value'#010+
+  '3*2WB<x>_Set Image base to Hexadecimal ','<x> value'#010+
   '3*2WC_Specify console type application'#010+
   '3*2WD_Use DEFFILE to export functions of DLL or EXE'#010+
   '3*2WF_Specify full-screen type application (OS/2 only)'#010+
   '3*2WG_Specify graphic type application'#010+
-  '3*2WN_Do not generate relocation code (necessa','ry for debugging)'#010+
+  '3*2WN_Do not generate relocation code (necess','ary for debugging)'#010+
   '3*2WR_Generate relocation code'#010+
   'P*2WC_Specify console type application (MacOS only)'#010+
   'P*2WG_Specify graphic type application (MacOS only)'#010+
   'P*2WT_Specify tool type application (MPW tool, MacOS only)'#010+
   '**1X_executable options:'#010+
-  '**2','Xc_pass --shared to the linker (Unix only)'#010+
+  '**','2Xc_pass --shared to the linker (Unix only)'#010+
   '**2Xd_don'#039't use standard library search path (needed for cross com'+
   'pile)'#010+
   '**2XD_try to link units dynamic          (defines FPC_LINK_DYNAMIC)'#010+
   '**2Xm_generate link map'#010+
-  '**2XM<x>_set the name of the '#039'mai','n'#039' program routine (default'+
+  '**2XM<x>_set the name of the '#039'ma','in'#039' program routine (default'+
   ' is '#039'main'#039')'#010+
   '**2XP<x>_prepend the binutils names with the prefix <x>'#010+
   '**2Xr<x>_set library search path to <x> (needed for cross compile)'#010+
   '**2Xs_strip all symbols from executable'#010+
-  '**2XS_try to link units static (defaul','t) (defines FPC_LINK_STATIC)'#010+
+  '**2XS_try to link units static (defau','lt) (defines FPC_LINK_STATIC)'#010+
   '**2Xt_link with static libraries (-static is passed to linker)'#010+
   '**2XX_try to link units smart            (defines FPC_LINK_SMART)'#010+
   '**1*_'#010+

+ 2 - 1
fcl/db/bufdataset.inc

@@ -322,7 +322,8 @@ function TBufDataset.GetFieldSize(FieldDef : TFieldDef) : longint;
 
 begin
   case FieldDef.DataType of
-    ftString     : result := FieldDef.Size + 1;
+    ftString,
+      ftFixedChar: result := FieldDef.Size + 1;
     ftSmallint,
       ftInteger,
       ftword     : result := sizeof(longint);

+ 0 - 7
fcl/db/database.inc

@@ -88,13 +88,6 @@ begin
     end;
 end;
 
-
-procedure TDatabase.Notification(AComponent: TComponent; Operation: TOperation);
-
-begin
-  //!! To be implemented.
-end;
-
 constructor TDatabase.Create(AOwner: TComponent);
 
 begin

+ 7 - 1
fcl/db/db.pp

@@ -32,6 +32,7 @@ const
   // whether it's true or false.
   YesNoChars : Array[Boolean] of char = ('Y','N');
 
+  SQLDelimiterCharacters = [';',',',' ','(',')',#13,#10,#9];
 
 type
 
@@ -1401,7 +1402,6 @@ type
     Procedure CheckDisConnected;
     procedure InternalHandleException; virtual;
     procedure Loaded; override;
-    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
     Procedure DoInternalConnect; Virtual;Abstract;
     Procedure DoInternalDisConnect; Virtual;Abstract;
   public
@@ -1547,9 +1547,13 @@ type
   { TParam }
 
   TBlobData = string;
+  
+  TParamBinding = array of integer;
 
   TParamType = (ptUnknown, ptInput, ptOutput, ptInputOutput, ptResult);
   TParamTypes = set of TParamType;
+  
+  TParamStyle = (psInterbase,psPostgreSQL);
 
   TParams = class;
 
@@ -1661,6 +1665,8 @@ type
     Function  IsEqual(Value: TParams): Boolean;
     Function  ParamByName(const Value: string): TParam;
     Function  ParseSQL(SQL: String; DoCreate: Boolean): String;
+    Function  ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle): String; overload;
+    Function  ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding): String; overload;
     Procedure RemoveParam(Value: TParam);
     Property Dataset : TDataset Read GetDataset;
     Property Items[Index: Integer] : TParam read GetItem write SetItem; default;

+ 194 - 0
fcl/db/dsparams.inc

@@ -151,10 +151,204 @@ begin
 end;
 
 Function TParams.ParseSQL(SQL: String; DoCreate: Boolean): String;
+
+var pb : TParamBinding;
+
+begin
+  Result := ParseSQL(SQL,DoCreate,psInterbase, pb);
+end;
+
+Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle): String;
+
+var pb : TParamBinding;
+
 begin
+  Result := ParseSQL(SQL,DoCreate,ParameterStyle,pb);
+end;
+
+
+Function TParams.ParseSQL(SQL: String; DoCreate: Boolean; ParameterStyle : TParamStyle; var ParamBinding: TParambinding): String;
+
+type
+  // used for ParamPart
+  TStringPart = record
+    Start,Stop:integer;
+  end;
+  
+const
+  ParamAllocStepSize = 8;
+  
+var
+  p,ParamNameStart,BufStart:PChar;
+  ParamName:string;
+  QuestionMarkParamCount,ParameterIndex,NewLength:integer;
+  ParamCount:integer; // actual number of parameters encountered so far;
+                      // always <= Length(ParamPart) = Length(Parambinding)
+                      // Parambinding will have length ParamCount in the end
+  ParamPart:array of TStringPart; // describe which parts of buf are parameters
+  NewQueryLength:integer;
+  NewQuery:string;
+  NewQueryIndex,BufIndex,CopyLen,i:integer;                     // Parambinding will have length ParamCount in the end
+
+begin
+  if DoCreate then Clear;
+  // Parse the SQL and build ParamBinding
+  ParamCount:=0;
+  NewQueryLength:=Length(SQL);
+  SetLength(ParamPart,ParamAllocStepSize);
+  SetLength(Parambinding,ParamAllocStepSize);
+  QuestionMarkParamCount:=0; // number of ? params found in query so far
+
+  p:=PChar(SQL);
+  BufStart:=p; // used to calculate ParamPart.Start values
+  repeat
+    case p^ of
+      '''': // single quote delimited string
+        begin
+          Inc(p);
+          while not (p^ in [#0, '''']) do
+          begin
+            if p^='\' then Inc(p,2) // make sure we handle \' and \\ correct
+            else Inc(p);
+          end;
+          if p^='''' then Inc(p); // skip final '
+        end;
+      '"':  // double quote delimited string
+        begin
+          Inc(p);
+          while not (p^ in [#0, '"']) do
+          begin
+            if p^='\'  then Inc(p,2) // make sure we handle \" and \\ correct
+            else Inc(p);
+          end;
+          if p^='"' then Inc(p); // skip final "
+        end;
+      '-': // possible start of -- comment
+        begin
+          Inc(p);
+          if p='-' then // -- comment
+          begin
+            repeat // skip until at end of line
+              Inc(p);
+            until p^ in [#10, #0];
+          end
+        end;
+      '/': // possible start of /* */ comment
+        begin
+          Inc(p);
+          if p^='*' then // /* */ comment
+          begin
+            repeat
+              Inc(p);
+              if p^='*' then // possible end of comment
+              begin
+                Inc(p);
+                if p^='/' then Break; // end of comment
+              end;
+            until p^=#0;
+            if p^='/' then Inc(p); // skip final /
+          end;
+        end;
+      ':','?': // parameter
+        begin
+          Inc(ParamCount);
+          if ParamCount>Length(ParamPart) then
+          begin
+            NewLength:=Length(ParamPart)+ParamAllocStepSize;
+            SetLength(ParamPart,NewLength);
+            SetLength(ParamBinding,NewLength);
+          end;
+
+          if p^=':' then
+          begin // find parameter name
+            Inc(p);
+            ParamNameStart:=p;
+            while not (p^ in (SQLDelimiterCharacters+[#0])) do
+              Inc(p);
+            ParamName:=Copy(ParamNameStart,1,p-ParamNameStart);
+          end
+          else
+          begin
+            Inc(p);
+            ParamNameStart:=p;
+            ParamName:='';
+          end;
+
+          // create Parameter and assign ParameterIndex
+          if DoCreate then
+            ParameterIndex := CreateParam(ftUnknown, ParamName, ptInput).Index
+          // else find ParameterIndex
+          else
+            begin
+              if ParamName<>'' then
+                ParameterIndex:=ParamByName(ParamName).Index
+              else
+              begin
+                ParameterIndex:=QuestionMarkParamCount;
+                Inc(QuestionMarkParamCount);
+              end;
+            end;
+
+          // store ParameterIndex in FParamIndex, ParamPart data
+          ParamBinding[ParamCount-1]:=ParameterIndex;
+          ParamPart[ParamCount-1].Start:=ParamNameStart-BufStart;
+          ParamPart[ParamCount-1].Stop:=p-BufStart+1;
+
+          // update NewQueryLength
+          Dec(NewQueryLength,p-ParamNameStart);
+        end;
+      #0:Break;
+    else
+      Inc(p);
+    end;
+  until false;
+  
+  SetLength(ParamPart,ParamCount);
+  SetLength(ParamBinding,ParamCount);
 
+  if ParamCount>0 then
+  begin
+    // replace :ParamName by ? (using ParamPart array and NewQueryLength)
+    if ParameterStyle = psPostgreSQL then
+      if paramcount < 10 then
+        inc(NewQueryLength,paramcount)
+      else
+        inc(NewQueryLength,(paramcount-9)*2+9);
+
+    SetLength(NewQuery,NewQueryLength);
+    NewQueryIndex:=1;
+    BufIndex:=1;
+    for i:=0 to High(ParamPart) do
+    begin
+      CopyLen:=ParamPart[i].Start-BufIndex;
+      Move(SQL[BufIndex],NewQuery[NewQueryIndex],CopyLen);
+      Inc(NewQueryIndex,CopyLen);
+      case ParameterStyle of
+        psInterbase : NewQuery[NewQueryIndex]:='?';
+        psPostgreSQL: begin
+                        ParamName := IntToStr(i+1);
+                        NewQuery[NewQueryIndex]:='$';
+                        Inc(NewQueryIndex);
+                        NewQuery[NewQueryIndex]:= paramname[1];
+                        if i>10 then
+                          begin
+                          Inc(NewQueryIndex);
+                          NewQuery[NewQueryIndex]:= paramname[2]
+                          end;
+                      end;
+      end;
+      Inc(NewQueryIndex);
+      BufIndex:=ParamPart[i].Stop;
+    end;
+    CopyLen:=Length(SQL)+1-BufIndex;
+    Move(SQL[BufIndex],NewQuery[NewQueryIndex],CopyLen);
+  end
+  else
+    NewQuery:=SQL;
+  Result := NewQuery;
 end;
 
+
 Procedure TParams.RemoveParam(Value: TParam);
 begin
    Value.Collection:=Nil;

+ 1 - 21
fcl/db/sqldb/interbase/ibconnection.pp

@@ -438,27 +438,7 @@ begin
     tr := aTransaction.Handle;
     
     if assigned(AParams) and (AParams.count > 0) then
-      begin
-      SetLength(ParamBinding,0);
-
-      i := posex(':',buf);
-      while i > 0 do
-        begin
-        inc(i);
-        p := @buf[i];
-        repeat
-        inc(p);
-        until (p^ in SQLDelimiterCharacters);
-
-        SetLength(ParamBinding,length(ParamBinding)+1);
-        parambinding[high(parambinding)] := AParams.ParamByName(copy(buf,i,p-@buf[i])).Index;
-
-        i := posex(':',buf,i);
-        end;
-
-        for x := 0 to AParams.count-1 do
-          buf := stringreplace(buf,':'+AParams[x].Name,'?',[rfReplaceAll,rfIgnoreCase]);
-      end;
+      buf := AParams.ParseSQL(buf,false,psInterbase,paramBinding);
 
     if isc_dsql_prepare(@Status, @tr, @Statement, 0, @Buf[1], Dialect, nil) <> 0 then
       CheckError('PrepareStatement', Status);

+ 6 - 0
fcl/db/sqldb/mysql/mysql4conn.pas

@@ -55,6 +55,7 @@ Type
     Function AllocateTransactionHandle : TSQLHandle; override;
 
     procedure PrepareStatement(cursor: TSQLCursor;ATransaction : TSQLTransaction;buf : string; AParams : TParams); override;
+    procedure UnPrepareStatement(cursor:TSQLCursor); override;
     procedure FreeFldBuffers(cursor : TSQLCursor); override;
     procedure Execute(cursor: TSQLCursor;atransaction:tSQLtransaction;AParams : TParams); override;
     procedure AddFieldDefs(cursor: TSQLCursor; FieldDefs : TfieldDefs); override;
@@ -233,6 +234,11 @@ begin
     end
 end;
 
+procedure TMySQLConnection.UnPrepareStatement(cursor: TSQLCursor);
+begin
+  // not necessary
+end;
+
 procedure TMySQLConnection.FreeFldBuffers(cursor: TSQLCursor);
 
 Var

+ 131 - 240
fcl/db/sqldb/odbc/odbcconn.pas

@@ -29,7 +29,7 @@ type
   protected
     FSTMTHandle:SQLHSTMT; // ODBC Statement Handle
     FQuery:string;        // last prepared query, with :ParamName converted to ?
-    FParamIndex:array of integer; // maps the i-th parameter in the query to the TParams passed to PrepareStatement
+    FParamIndex:TParamBinding; // maps the i-th parameter in the query to the TParams passed to PrepareStatement
     FParamBuf:array of pointer; // buffers that can be used to bind the i-th parameter in the query
   public
     constructor Create(Connection:TODBCConnection);
@@ -154,7 +154,7 @@ begin
   end;
 end;
 
-procedure ODBCCheckResult(HandleType:SQLSMALLINT; AHandle: SQLHANDLE; ErrorMsg: string);
+procedure ODBCCheckResult(LastReturnCode:SQLRETURN; HandleType:SQLSMALLINT; AHandle: SQLHANDLE; ErrorMsg: string);
 
   // check return value from SQLGetDiagField/Rec function itself
   procedure CheckSQLGetDiagResult(const Res:SQLRETURN);
@@ -172,13 +172,11 @@ procedure ODBCCheckResult(HandleType:SQLSMALLINT; AHandle: SQLHANDLE; ErrorMsg:
 var
   NativeError:SQLINTEGER;
   TextLength:SQLSMALLINT;
-  Res,LastReturnCode:SQLRETURN;
+  Res:SQLRETURN;
   SqlState,MessageText,TotalMessage:string;
   RecNumber:SQLSMALLINT;
 begin
   // check result
-  Res:=SQLGetDiagField(HandleType,AHandle,0,SQL_DIAG_RETURNCODE,@LastReturnCode,SQL_IS_SMALLINT,TextLength);
-  CheckSQLGetDiagResult(Res);
   if ODBCSucces(LastReturnCode) then
     Exit; // no error; all is ok
 
@@ -253,7 +251,7 @@ begin
     else if EqualSignPos=1 then
       raise EODBCException.CreateFmt('Invalid parameter in Params[%d]; no identifier before the ''='' in ''%s''',[i, Param])
     else
-      Result:=Result + EscapeParamValue(Copy(Param,1,EqualSignPos-1))+'='+EscapeParamValue(Copy(Param,EqualSignPos+1,MaxInt));
+      Result:=Result + EscapeParamValue(Copy(Param,1,EqualSignPos-1))+'='+EscapeParamValue(Copy(Param,EqualSignPos+1,MaxInt))+';';
   end;
 end;
 
@@ -286,17 +284,19 @@ begin
           IntVal:=AParams[ParamIndex].AsInteger;
           Move(IntVal,Buf^,4);
           ODBCCursor.FParamBuf[i]:=Buf;
-          SQLBindParameter(ODBCCursor.FSTMTHandle, // StatementHandle
-                           i+1,                    // ParameterNumber
-                           SQL_PARAM_INPUT,        // InputOutputType
-                           SQL_C_LONG,             // ValueType
-                           SQL_INTEGER,            // ParameterType
-                           10,                     // ColumnSize
-                           0,                      // DecimalDigits
-                           Buf,                    // ParameterValuePtr
-                           0,                      // BufferLength
-                           nil);                   // StrLen_or_IndPtr
-          ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not bind parameter %d',[i]));
+          ODBCCheckResult(
+            SQLBindParameter(ODBCCursor.FSTMTHandle, // StatementHandle
+                             i+1,                    // ParameterNumber
+                             SQL_PARAM_INPUT,        // InputOutputType
+                             SQL_C_LONG,             // ValueType
+                             SQL_INTEGER,            // ParameterType
+                             10,                     // ColumnSize
+                             0,                      // DecimalDigits
+                             Buf,                    // ParameterValuePtr
+                             0,                      // BufferLength
+                             nil),                   // StrLen_or_IndPtr
+            SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not bind parameter %d',[i])
+          );
         end;
       ftString:
         begin
@@ -306,17 +306,19 @@ begin
           Move(StrLen,    buf^,                    SizeOf(SQLINTEGER));
           Move(StrVal[1],(buf+SizeOf(SQLINTEGER))^,StrLen);
           ODBCCursor.FParamBuf[i]:=Buf;
-          SQLBindParameter(ODBCCursor.FSTMTHandle, // StatementHandle
-                           i+1,                    // ParameterNumber
-                           SQL_PARAM_INPUT,        // InputOutputType
-                           SQL_C_CHAR,             // ValueType
-                           SQL_CHAR,               // ParameterType
-                           StrLen,                 // ColumnSize
-                           0,                      // DecimalDigits
-                           buf+SizeOf(SQLINTEGER), // ParameterValuePtr
-                           StrLen,                 // BufferLength
-                           Buf);                   // StrLen_or_IndPtr
-          ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not bind parameter %d',[i]));
+          ODBCCheckResult(
+            SQLBindParameter(ODBCCursor.FSTMTHandle, // StatementHandle
+                             i+1,                    // ParameterNumber
+                             SQL_PARAM_INPUT,        // InputOutputType
+                             SQL_C_CHAR,             // ValueType
+                             SQL_CHAR,               // ParameterType
+                             StrLen,                 // ColumnSize
+                             0,                      // DecimalDigits
+                             buf+SizeOf(SQLINTEGER), // ParameterValuePtr
+                             StrLen,                 // BufferLength
+                             Buf),                   // StrLen_or_IndPtr
+            SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not bind parameter %d',[i])
+          );
         end;
     else
       raise EDataBaseError.CreateFmt('Parameter %d is of type %s, which not supported yet',[ParamIndex, Fieldtypenames[AParams[ParamIndex].DataType]]);
@@ -359,38 +361,49 @@ begin
   end;
 
   // allocate connection handle
-  SQLAllocHandle(SQL_HANDLE_DBC,Environment.FENVHandle,FDBCHandle);
-  ODBCCheckResult(SQL_HANDLE_ENV,Environment.FENVHandle,'Could not allocate ODBC Connection handle.');
+  ODBCCheckResult(
+    SQLAllocHandle(SQL_HANDLE_DBC,Environment.FENVHandle,FDBCHandle),
+    SQL_HANDLE_ENV,Environment.FENVHandle,'Could not allocate ODBC Connection handle.'
+  );
 
   // connect
   ConnectionString:=CreateConnectionString;
   SetLength(OutConnectionString,BufferLength-1); // allocate completed connection string buffer (using the ansistring #0 trick)
-  SQLDriverConnect(FDBCHandle,               // the ODBC connection handle
-                   nil,                      // no parent window (would be required for prompts)
-                   PChar(ConnectionString),  // the connection string
-                   Length(ConnectionString), // connection string length
-                   @(OutConnectionString[1]),// buffer for storing the completed connection string
-                   BufferLength,             // length of the buffer
-                   ActualLength,             // the actual length of the completed connection string
-                   SQL_DRIVER_NOPROMPT);     // don't prompt for password etc.
-  ODBCCheckResult(SQL_HANDLE_DBC,FDBCHandle,Format('Could not connect with connection string "%s".',[ConnectionString]));
-  if ActualLength<BufferLength-1 then
-    SetLength(OutConnectionString,ActualLength); // fix completed connection string length
+  ODBCCheckResult(
+    SQLDriverConnect(FDBCHandle,               // the ODBC connection handle
+                     nil,                      // no parent window (would be required for prompts)
+                     PChar(ConnectionString),  // the connection string
+                     Length(ConnectionString), // connection string length
+                     @(OutConnectionString[1]),// buffer for storing the completed connection string
+                     BufferLength,             // length of the buffer
+                     ActualLength,             // the actual length of the completed connection string
+                     SQL_DRIVER_NOPROMPT),     // don't prompt for password etc.
+    SQL_HANDLE_DBC,FDBCHandle,Format('Could not connect with connection string "%s".',[ConnectionString])
+  );
+
+// commented out as the OutConenctionString is not used further at the moment
+//  if ActualLength<BufferLength-1 then
+//    SetLength(OutConnectionString,ActualLength); // fix completed connection string length
 
   // set connection attributes (none yet)
 end;
 
 procedure TODBCConnection.DoInternalDisconnect;
+var
+  Res:SQLRETURN;
 begin
   inherited DoInternalDisconnect;
 
   // disconnect
-  SQLDisconnect(FDBCHandle);
-  ODBCCheckResult(SQL_HANDLE_DBC,FDBCHandle,'Could not disconnect.');
+  ODBCCheckResult(
+    SQLDisconnect(FDBCHandle),
+    SQL_HANDLE_DBC,FDBCHandle,'Could not disconnect.'
+  );
 
   // deallocate connection handle
-  if SQLFreeHandle(SQL_HANDLE_DBC, FDBCHandle)=SQL_ERROR then
-    ODBCCheckResult(SQL_HANDLE_DBC,FDBCHandle,'Could not free connection handle.');
+  Res:=SQLFreeHandle(SQL_HANDLE_DBC, FDBCHandle);
+  if Res=SQL_ERROR then
+    ODBCCheckResult(Res,SQL_HANDLE_DBC,FDBCHandle,'Could not free connection handle.');
 end;
 
 function TODBCConnection.AllocateCursorHandle: TSQLCursor;
@@ -413,25 +426,8 @@ begin
 end;
 
 procedure TODBCConnection.PrepareStatement(cursor: TSQLCursor; ATransaction: TSQLTransaction; buf: string; AParams: TParams);
-type
-  // used for ParamPart
-  TStringPart = record
-    Start,Stop:integer;
-  end;
-const
-  ParamAllocStepSize = 8;
 var
   ODBCCursor:TODBCCursor;
-  p,ParamNameStart,BufStart:PChar;
-  ParamName:string;
-  QuestionMarkParamCount,ParameterIndex,NewLength:integer;
-  ParamCount:integer; // actual number of parameters encountered so far;
-                      // always <= Length(ParamPart) = Length(ODBCCursor.FParamIndex)
-                      // ODBCCursor.FParamIndex will have length ParamCount in the end
-  ParamPart:array of TStringPart; // describe which parts of buf are parameters
-  NewQueryLength:integer;
-  NewQuery:string;
-  NewQueryIndex,BufIndex,CopyLen,i:integer;
 begin
   ODBCCursor:=cursor as TODBCCursor;
 
@@ -440,142 +436,16 @@ begin
   //       ODBCCursor.FParamIndex will map th i-th ? token in the (modified) query to an index for AParams
 
   // Parse the SQL and build FParamIndex
-  ParamCount:=0;
-  NewQueryLength:=Length(buf);
-  SetLength(ParamPart,ParamAllocStepSize);
-  SetLength(ODBCCursor.FParamIndex,ParamAllocStepSize);
-  QuestionMarkParamCount:=0; // number of ? params found in query so far
-  p:=PChar(buf);
-  BufStart:=p; // used to calculate ParamPart.Start values
-  repeat
-    case p^ of
-      '''': // single quote delimited string (not obligatory in ODBC, but let's handle it anyway)
-        begin
-          Inc(p);
-          while not (p^ in [#0, '''']) do
-          begin
-            if p^='\' then Inc(p,2) // make sure we handle \' and \\ correct
-            else Inc(p);
-          end;
-          if p^='''' then Inc(p); // skip final '
-        end;
-      '"':  // double quote delimited string
-        begin
-          Inc(p);
-          while not (p^ in [#0, '"']) do
-          begin
-            if p^='\'  then Inc(p,2) // make sure we handle \" and \\ correct
-            else Inc(p);
-          end;
-          if p^='"' then Inc(p); // skip final "
-        end;
-      '-': // possible start of -- comment
-        begin
-          Inc(p);
-          if p='-' then // -- comment
-          begin
-            repeat // skip until at end of line
-              Inc(p);
-            until p^ in [#10, #0];
-          end
-        end;
-      '/': // possible start of /* */ comment
-        begin
-          Inc(p);
-          if p^='*' then // /* */ comment
-          begin
-            repeat
-              Inc(p);
-              if p^='*' then // possible end of comment
-              begin
-                Inc(p);
-                if p^='/' then Break; // end of comment
-              end;
-            until p^=#0;
-            if p^='/' then Inc(p); // skip final /
-          end;
-        end;
-      ':','?': // parameter
-        begin
-          Inc(ParamCount);
-          if ParamCount>Length(ParamPart) then
-          begin
-            NewLength:=Length(ParamPart)+ParamAllocStepSize;
-            SetLength(ParamPart,NewLength);
-            SetLength(ODBCCursor.FParamIndex,NewLength);
-          end;
-
-          if p^=':' then
-          begin // find parameter name
-            Inc(p);
-            ParamNameStart:=p;
-            while not (p^ in (SQLDelimiterCharacters+[#0])) do
-              Inc(p);
-            ParamName:=Copy(ParamNameStart,1,p-ParamNameStart);
-          end
-          else
-          begin
-            Inc(p);
-            ParamNameStart:=p;
-            ParamName:='';
-          end;
-
-          // find ParameterIndex
-          if ParamName<>'' then
-          begin
-            if AParams=nil then
-              raise EDataBaseError.CreateFmt('Found parameter marker with name %s in the query, but no actual parameters are given at all',[ParamName]);
-            ParameterIndex:=AParams.ParamByName(ParamName).Index // lookup parameter in AParams
-          end
-          else
-          begin
-            ParameterIndex:=QuestionMarkParamCount;
-            Inc(QuestionMarkParamCount);
-          end;
-
-          // store ParameterIndex in FParamIndex, ParamPart data
-          ODBCCursor.FParamIndex[ParamCount-1]:=ParameterIndex;
-          ParamPart[ParamCount-1].Start:=ParamNameStart-BufStart;
-          ParamPart[ParamCount-1].Stop:=p-BufStart+1;
-
-          // update NewQueryLength
-          Dec(NewQueryLength,p-ParamNameStart);
-        end;
-      #0:Break;
-    else
-      Inc(p);
-    end;
-  until false;
-
-  SetLength(ParamPart,ParamCount);
-  SetLength(ODBCCursor.FParamIndex,ParamCount);
-
-  if ParamCount>0 then
-  begin
-    // replace :ParamName by ? (using ParamPart array and NewQueryLength)
-    SetLength(NewQuery,NewQueryLength);
-    NewQueryIndex:=1;
-    BufIndex:=1;
-    for i:=0 to High(ParamPart) do
-    begin
-      CopyLen:=ParamPart[i].Start-BufIndex;
-      Move(buf[BufIndex],NewQuery[NewQueryIndex],CopyLen);
-      Inc(NewQueryIndex,CopyLen);
-      NewQuery[NewQueryIndex]:='?';
-      Inc(NewQueryIndex);
-      BufIndex:=ParamPart[i].Stop;
-    end;
-    CopyLen:=Length(Buf)+1-BufIndex;
-    Move(buf[BufIndex],NewQuery[NewQueryIndex],CopyLen);
-  end
-  else
-    NewQuery:=buf;
+  if assigned(AParams) and (AParams.count > 0) then
+    buf := AParams.ParseSQL(buf,false,psInterbase,ODBCCursor.FParamIndex);
 
   // prepare statement
-  SQLPrepare(ODBCCursor.FSTMTHandle, PChar(NewQuery), Length(NewQuery));
-  ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not prepare statement.');
+  ODBCCheckResult(
+    SQLPrepare(ODBCCursor.FSTMTHandle, PChar(buf), Length(buf)),
+    SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not prepare statement.'
+  );
 
-  ODBCCursor.FQuery:=NewQuery;
+  ODBCCursor.FQuery:=Buf;
 end;
 
 procedure TODBCConnection.UnPrepareStatement(cursor: TSQLCursor);
@@ -623,8 +493,10 @@ begin
   SetParameters(ODBCCursor, AParams);
 
   // execute the statement
-  SQLExecute(ODBCCursor.FSTMTHandle);
-  ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not execute statement.');
+  ODBCCheckResult(
+    SQLExecute(ODBCCursor.FSTMTHandle),
+    SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not execute statement.'
+  );
 
   // free parameter buffers
   FreeParamBuffers(ODBCCursor);
@@ -640,7 +512,7 @@ begin
   // fetch new row
   Res:=SQLFetch(ODBCCursor.FSTMTHandle);
   if Res<>SQL_NO_DATA then
-    ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not fetch new row from result set');
+    ODBCCheckResult(Res,SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not fetch new row from result set');
 
   // result is true iff a new row was available
   Result:=Res<>SQL_NO_DATA;
@@ -654,6 +526,7 @@ var
   ODBCTimeStruct:SQL_TIME_STRUCT;
   ODBCTimeStampStruct:SQL_TIMESTAMP_STRUCT;
   DateTime:TDateTime;
+  Res:SQLRETURN;
 begin
   ODBCCursor:=cursor as TODBCCursor;
 
@@ -662,44 +535,44 @@ begin
   // TODO: finish this
   case FieldDef.DataType of
     ftFixedChar,ftString: // are both mapped to TStringField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_CHAR, buffer, FieldDef.Size, @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_CHAR, buffer, FieldDef.Size, @StrLenOrInd);
     ftSmallint:           // mapped to TSmallintField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_SSHORT, buffer, SizeOf(Smallint), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_SSHORT, buffer, SizeOf(Smallint), @StrLenOrInd);
     ftInteger,ftWord:     // mapped to TLongintField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_SLONG, buffer, SizeOf(Longint), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_SLONG, buffer, SizeOf(Longint), @StrLenOrInd);
     ftLargeint:           // mapped to TLargeintField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_SBIGINT, buffer, SizeOf(Largeint), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_SBIGINT, buffer, SizeOf(Largeint), @StrLenOrInd);
     ftFloat:              // mapped to TFloatField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_DOUBLE, buffer, SizeOf(Double), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_DOUBLE, buffer, SizeOf(Double), @StrLenOrInd);
     ftTime:               // mapped to TTimeField
     begin
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_TYPE_TIME, @ODBCTimeStruct, SizeOf(SQL_TIME_STRUCT), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_TYPE_TIME, @ODBCTimeStruct, SizeOf(SQL_TIME_STRUCT), @StrLenOrInd);
       DateTime:=TimeStructToDateTime(@ODBCTimeStruct);
       Move(DateTime, buffer^, SizeOf(TDateTime));
     end;
     ftDate:               // mapped to TDateField
     begin
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_TYPE_DATE, @ODBCDateStruct, SizeOf(SQL_DATE_STRUCT), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_TYPE_DATE, @ODBCDateStruct, SizeOf(SQL_DATE_STRUCT), @StrLenOrInd);
       DateTime:=DateStructToDateTime(@ODBCDateStruct);
       Move(DateTime, buffer^, SizeOf(TDateTime));
     end;
     ftDateTime:           // mapped to TDateTimeField
     begin
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_TYPE_TIMESTAMP, @ODBCTimeStampStruct, SizeOf(SQL_TIMESTAMP_STRUCT), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_TYPE_TIMESTAMP, @ODBCTimeStampStruct, SizeOf(SQL_TIMESTAMP_STRUCT), @StrLenOrInd);
       DateTime:=TimeStampStructToDateTime(@ODBCTimeStampStruct);
       Move(DateTime, buffer^, SizeOf(TDateTime));
     end;
     ftBoolean:            // mapped to TBooleanField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_BIT, buffer, SizeOf(Wordbool), @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_BIT, buffer, SizeOf(Wordbool), @StrLenOrInd);
     ftBytes:              // mapped to TBytesField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_BINARY, buffer, FieldDef.Size, @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_BINARY, buffer, FieldDef.Size, @StrLenOrInd);
     ftVarBytes:           // mapped to TVarBytesField
-      SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_BINARY, buffer, FieldDef.Size, @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_BINARY, buffer, FieldDef.Size, @StrLenOrInd);
     // TODO: Loading of other field types
   else
     raise EODBCException.CreateFmt('Tried to load field of unsupported field type %s',[Fieldtypenames[FieldDef.DataType]]);
   end;
-  ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get field data for field ''%s'' (index %d).',[FieldDef.Name, FieldDef.Index+1]));
+  ODBCCheckResult(Res,SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get field data for field ''%s'' (index %d).',[FieldDef.Name, FieldDef.Index+1]));
   Result:=StrLenOrInd<>SQL_NULL_DATA; // Result indicates whether the value is non-null
 
 //  writeln(Format('Field.Size: %d; StrLenOrInd: %d',[FieldDef.Size, StrLenOrInd]));
@@ -717,8 +590,10 @@ var
 begin
   ODBCCursor:=cursor as TODBCCursor;
 
-  SQLFreeStmt(ODBCCursor.FSTMTHandle, SQL_CLOSE);
-  ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not close ODBC statement cursor.');
+  ODBCCheckResult(
+    SQLFreeStmt(ODBCCursor.FSTMTHandle, SQL_CLOSE),
+    SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not close ODBC statement cursor.'
+  );
 end;
 
 procedure TODBCConnection.AddFieldDefs(cursor: TSQLCursor; FieldDefs: TFieldDefs);
@@ -737,24 +612,28 @@ begin
   ODBCCursor:=cursor as TODBCCursor;
 
   // get number of columns in result set
-  SQLNumResultCols(ODBCCursor.FSTMTHandle, ColumnCount);
-  ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not determine number of columns in result set.');
+  ODBCCheckResult(
+    SQLNumResultCols(ODBCCursor.FSTMTHandle, ColumnCount),
+    SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not determine number of columns in result set.'
+  );
 
   for i:=1 to ColumnCount do
   begin
     SetLength(ColName,ColNameDefaultLength); // also garantuees uniqueness
 
     // call with default column name buffer
-    SQLDescribeCol(ODBCCursor.FSTMTHandle, // statement handle
-                   i,                      // column number, is 1-based (Note: column 0 is the bookmark column in ODBC)
-                   @(ColName[1]),          // default buffer
-                   ColNameDefaultLength+1, // and its length; we include the #0 terminating any ansistring of Length > 0 in the buffer
-                   ColNameLength,          // actual column name length
-                   DataType,               // the SQL datatype for the column
-                   ColumnSize,             // column size
-                   DecimalDigits,          // number of decimal digits
-                   Nullable);              // SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
-    ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get column properties for column %d.',[i]));
+    ODBCCheckResult(
+      SQLDescribeCol(ODBCCursor.FSTMTHandle, // statement handle
+                     i,                      // column number, is 1-based (Note: column 0 is the bookmark column in ODBC)
+                     @(ColName[1]),          // default buffer
+                     ColNameDefaultLength+1, // and its length; we include the #0 terminating any ansistring of Length > 0 in the buffer
+                     ColNameLength,          // actual column name length
+                     DataType,               // the SQL datatype for the column
+                     ColumnSize,             // column size
+                     DecimalDigits,          // number of decimal digits
+                     Nullable),              // SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
+      SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get column properties for column %d.',[i])
+    );
 
     // truncate buffer or make buffer long enough for entire column name (note: the call is the same for both cases!)
     SetLength(ColName,ColNameLength);
@@ -762,14 +641,16 @@ begin
     if ColNameLength>ColNameDefaultLength then
     begin
       // request column name with buffer that is long enough
-      SQLColAttribute(ODBCCursor.FSTMTHandle, // statement handle
-                      i,                      // column number
-                      SQL_DESC_NAME,          // the column name or alias
-                      @(ColName[1]),          // buffer
-                      ColNameLength+1,        // buffer size
-                      @ColNameLength,         // actual length
-                      nil);                   // no numerical output
-      ODBCCheckResult(SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get column name for column %d.',[i]));
+      ODBCCheckResult(
+        SQLColAttribute(ODBCCursor.FSTMTHandle, // statement handle
+                        i,                      // column number
+                        SQL_DESC_NAME,          // the column name or alias
+                        @(ColName[1]),          // buffer
+                        ColNameLength+1,        // buffer size
+                        @ColNameLength,         // actual length
+                        nil),                   // no numerical output
+        SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, Format('Could not get column name for column %d.',[i])
+      );
     end;
 
     // convert type
@@ -847,15 +728,20 @@ begin
     raise EODBCException.Create('Could not allocate ODBC Environment handle'); // we can't retrieve any more information, because we don't have a handle for the SQLGetDiag* functions
 
   // set odbc version
-  SQLSetEnvAttr(FENVHandle, SQL_ATTR_ODBC_VERSION, SQLPOINTER(SQL_OV_ODBC3), 0);
-  ODBCCheckResult(SQL_HANDLE_ENV, FENVHandle,'Could not set ODBC version to 3.');
+  ODBCCheckResult(
+    SQLSetEnvAttr(FENVHandle, SQL_ATTR_ODBC_VERSION, SQLPOINTER(SQL_OV_ODBC3), 0),
+    SQL_HANDLE_ENV, FENVHandle,'Could not set ODBC version to 3.'
+  );
 end;
 
 destructor TODBCEnvironment.Destroy;
+var
+  Res:SQLRETURN;
 begin
   // free environment handle
-  if SQLFreeHandle(SQL_HANDLE_ENV, FENVHandle)=SQL_ERROR then
-    ODBCCheckResult(SQL_HANDLE_ENV, FENVHandle, 'Could not free ODBC Environment handle.');
+  Res:=SQLFreeHandle(SQL_HANDLE_ENV, FENVHandle);
+  if Res=SQL_ERROR then
+    ODBCCheckResult(Res,SQL_HANDLE_ENV, FENVHandle, 'Could not free ODBC Environment handle.');
 
   // free odbc if not used by any TODBCEnvironment object anymore
   Dec(ODBCLoadCount);
@@ -867,19 +753,24 @@ end;
 constructor TODBCCursor.Create(Connection:TODBCConnection);
 begin
   // allocate statement handle
-  SQLAllocHandle(SQL_HANDLE_STMT, Connection.FDBCHandle, FSTMTHandle);
-  ODBCCheckResult(SQL_HANDLE_DBC, Connection.FDBCHandle, 'Could not allocate ODBC Statement handle.');
+  ODBCCheckResult(
+    SQLAllocHandle(SQL_HANDLE_STMT, Connection.FDBCHandle, FSTMTHandle),
+    SQL_HANDLE_DBC, Connection.FDBCHandle, 'Could not allocate ODBC Statement handle.'
+  );
 end;
 
 destructor TODBCCursor.Destroy;
+var
+  Res:SQLRETURN;
 begin
   inherited Destroy;
 
   if FSTMTHandle<>SQL_INVALID_HANDLE then
   begin
     // deallocate statement handle
-    if SQLFreeHandle(SQL_HANDLE_STMT, FSTMTHandle)=SQL_ERROR then
-      ODBCCheckResult(SQL_HANDLE_STMT, FSTMTHandle, 'Could not free ODBC Statement handle.');
+    Res:=SQLFreeHandle(SQL_HANDLE_STMT, FSTMTHandle);
+    if Res=SQL_ERROR then
+      ODBCCheckResult(Res,SQL_HANDLE_STMT, FSTMTHandle, 'Could not free ODBC Statement handle.');
   end;
 end;
 

+ 3 - 5
fcl/db/sqldb/postgres/pqconnection.pp

@@ -26,7 +26,6 @@ type
     tr        : Pointer;
     nFields   : integer;
     res       : PPGresult;
-    BaseRes   : PPGresult;
     Nr        : string;
   end;
 
@@ -408,11 +407,9 @@ begin
         begin
         s := s + '(';
         for i := 0 to AParams.count-1 do
-          begin
           s := s + TypeStrings[AParams[i].DataType] + ',';
-          buf := stringreplace(buf,':'+AParams[i].Name,'$'+inttostr(i+1),[rfReplaceAll,rfIgnoreCase]);
-          end;
         s[length(s)] := ')';
+        buf := AParams.ParseSQL(buf,false,psPostgreSQL);
         end;
       s := s + ' as ' + buf;
       res := pqexec(tr,pchar(s));
@@ -459,7 +456,6 @@ begin
         DatabaseError(SErrClearSelection + ' (PostgreSQL: ' + PQerrorMessage(tr) + ')',self)
         end
       end;
-    pqclear(baseres);
     pqclear(res);
     end;
 end;
@@ -525,6 +521,7 @@ var
   size      : integer;
   st        : string;
   fieldtype : tfieldtype;
+  BaseRes   : PPGresult;
 
 begin
   with cursor as TPQCursor do
@@ -553,6 +550,7 @@ begin
 
       TFieldDef.Create(FieldDefs, PQfname(BaseRes, i), fieldtype,size, False, (i + 1));
       end;
+    pqclear(baseres);
     end;
 end;
 

+ 3 - 18
fcl/db/sqldb/sqldb.pp

@@ -52,7 +52,6 @@ const
                   'create', 'get', 'put', 'execute',
                   'start','commit','rollback', '?'
                  );
- SQLDelimiterCharacters = [';',',',' ','(',')',#13,#10,#9];
 
 
 { TSQLConnection }
@@ -517,23 +516,9 @@ begin
   UnPrepare;
   if (FSQL <> nil) then
     begin
-    if assigned(FParams) then FParams.Clear;
-    s := FSQL.Text;
-    i := posex(':',s);
-    while i > 0 do
-      begin
-      inc(i);
-      p := @s[i];
-      repeat
-      inc(p);
-      until (p^ in SQLDelimiterCharacters);
-      if not assigned(FParams) then FParams := TParams.create(self);
-      ParamName := copy(s,i,p-@s[i]);
-      if FParams.FindParam(ParamName) = nil then
-        FParams.CreateParam(ftUnknown, ParamName, ptInput);
-      i := posex(':',s,i);
-      end;
-    end
+    if not assigned(FParams) then FParams := TParams.create(self);
+    FParams.ParseSQL(FSQL.Text,True);
+    end;
 end;
 
 Procedure TSQLQuery.SetTransaction(Value : TDBTransaction);