Jelajahi Sumber

# revisions: 41432,41655,41801,41939

git-svn-id: branches/fixes_3_2@42000 -
marco 6 tahun lalu
induk
melakukan
e0314439fa

+ 3 - 0
.gitattributes

@@ -2031,6 +2031,8 @@ packages/fcl-db/examples/showcsv.pp svneol=native#text/plain
 packages/fcl-db/examples/sqlite3extdemo.pp svneol=native#text/plain
 packages/fcl-db/examples/sqlite3loadlib.lpr svneol=native#text/plain
 packages/fcl-db/examples/sqlparser.pp svneol=native#text/plain
+packages/fcl-db/examples/sqlshell.lpi svneol=native#text/plain
+packages/fcl-db/examples/sqlshell.pas svneol=native#text/plain
 packages/fcl-db/examples/tsamytable.pp svneol=native#text/plain
 packages/fcl-db/examples/typesafetable.sql svneol=native#text/plain
 packages/fcl-db/fpmake.pp svneol=native#text/plain
@@ -2229,6 +2231,7 @@ packages/fcl-db/src/sqldb/postgres/fpmake.pp svneol=native#text/plain
 packages/fcl-db/src/sqldb/postgres/pqconnection.pp svneol=native#text/plain
 packages/fcl-db/src/sqldb/postgres/pqeventmonitor.pp svneol=native#text/plain
 packages/fcl-db/src/sqldb/sqldb.pp svneol=native#text/plain
+packages/fcl-db/src/sqldb/sqldbini.pp svneol=native#text/plain
 packages/fcl-db/src/sqldb/sqldblib.pp svneol=native#text/plain
 packages/fcl-db/src/sqldb/sqlite/Makefile svneol=native#text/plain
 packages/fcl-db/src/sqldb/sqlite/Makefile.fpc svneol=native#text/plain

+ 58 - 0
packages/fcl-db/examples/sqlshell.lpi

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <ProjectOptions>
+    <Version Value="11"/>
+    <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+        <UseDefaultCompilerOptions Value="True"/>
+      </Flags>
+      <SessionStorage Value="InProjectDir"/>
+      <MainUnit Value="0"/>
+      <Title Value="sqlshell"/>
+      <UseAppBundle Value="False"/>
+      <ResourceType Value="res"/>
+    </General>
+    <BuildModes Count="1">
+      <Item1 Name="Default" Default="True"/>
+    </BuildModes>
+    <PublishOptions>
+      <Version Value="2"/>
+      <UseFileFilters Value="True"/>
+    </PublishOptions>
+    <RunParams>
+      <FormatVersion Value="2"/>
+      <Modes Count="0"/>
+    </RunParams>
+    <Units Count="1">
+      <Unit0>
+        <Filename Value="sqlshell.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit0>
+    </Units>
+  </ProjectOptions>
+  <CompilerOptions>
+    <Version Value="11"/>
+    <Target>
+      <Filename Value="sqlshell"/>
+    </Target>
+    <SearchPaths>
+      <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
+    </SearchPaths>
+  </CompilerOptions>
+  <Debugging>
+    <Exceptions Count="3">
+      <Item1>
+        <Name Value="EAbort"/>
+      </Item1>
+      <Item2>
+        <Name Value="ECodetoolError"/>
+      </Item2>
+      <Item3>
+        <Name Value="EFOpenError"/>
+      </Item3>
+    </Exceptions>
+  </Debugging>
+</CONFIG>

+ 296 - 0
packages/fcl-db/examples/sqlshell.pas

@@ -0,0 +1,296 @@
+{$mode objfpc}
+{$h+}
+uses
+  custapp, sysutils, strutils, classes, db, sqldb, bufdataset, XMLDatapacketReader,
+  sqlite3conn, pqconnection, ibconnection, mssqlconn, oracleconnection,mysql55conn,mysql40conn,mysql51conn,mysql50conn;
+
+Const
+  CmdSep = [' ',#9,#10,#13,#12];
+
+type
+
+  { TSQLShellApplication }
+
+  TSQLShellApplication = class(TCustomApplication)
+  Private
+    FConn : TSQLConnection;
+    FTR : TSQLTransaction;
+    FQuery : TSQLQuery;
+    FConnType : String;
+    FCharset : String;
+    FDatabaseName: String;
+    FHostName : string;
+    FUserName : String;
+    FPassword : String;
+    FPort : INteger;
+    FAutoCommit : Boolean;
+    procedure ConnectToDatabase;
+    procedure DisconnectFromDatabase;
+    procedure ExecuteCommand(const ASQL: UTF8String);
+    procedure ExecuteSystemCommand(const S : UTF8String);
+    procedure MaybeCommit;
+    procedure MaybeRollBack;
+    function ParseArgs: Boolean;
+    procedure RunCommandLoop;
+    procedure SaveLast(FN: String);
+    procedure Usage(const Err: String);
+    procedure WriteHelp;
+  Protected
+    procedure DoRun; override;
+    Property Conn : TSQLConnection Read FConn;
+    Property AutoCommit : Boolean Read FAutoCommit;
+  end;
+
+
+Procedure TSQLShellApplication.ConnectToDatabase;
+
+begin
+  FConn:=TSQLConnector.Create(Self);
+  TSQLConnector(FConn).ConnectorType:=FConnType;
+  FTR:=TSQLTransaction.Create(Self);
+  Conn.Transaction:=FTR;
+  Conn.DatabaseName:=FDatabaseName;
+  Conn.HostName:=FHostName;
+  Conn.UserName:=FUserName;
+  Conn.Password:=FPassword;
+  Conn.Connected:=True;
+  if FCharset<>'' then
+    Conn.CharSet:=FCharset;
+end;
+
+
+Procedure TSQLShellApplication.DisconnectFromDatabase;
+
+begin
+  FreeAndNil(FTr);
+  FreeAndNil(FConn);
+end;
+
+Procedure TSQLShellApplication.ExecuteCommand(Const ASQL : UTF8String);
+
+Var
+  Q : TSQLQuery;
+  F : TField;
+  
+begin
+  FreeAndNil(FQuery);
+  Q:=TSQLQuery.Create(Conn);
+  Q.Database:=Conn;
+  Q.Transaction:=FTr;
+  if not FTR.Active then
+    FTR.StartTransaction;
+  Q.SQL.Text:=aSQL;
+  Q.Prepare;
+  if Q.StatementType<>stSelect then
+    begin
+    Q.ExecSQL;
+    Writeln('Rows affected : ',Q.RowsAffected);
+    if AutoCommit then
+      (Q.Transaction as TSQLTransaction).Commit;
+    Q.Free;
+    end
+  else
+    begin
+    Q.Open;
+    Write('|');
+    For F in Q.Fields do
+      Write(' ',F.FieldName,' |');
+    Writeln;
+    While not Q.EOF do
+      begin
+      Write('|');
+      For F in Q.Fields do
+        Write(F.AsString,' |');
+      Writeln;
+      Q.Next;
+      end;
+    FQuery:=Q;
+    end;
+end;
+
+Procedure TSQLShellApplication.SaveLast(FN : String);
+
+begin
+  FN:=Trim(FN);
+  if FN='' then
+    begin
+    Write('Type filename to save data: ');
+    Readln(fn);
+    end;
+  if (FN<>'') then
+    FQuery.SaveToFile(FN,dfXML);
+end;
+
+Procedure TSQLShellApplication.MaybeCommit;
+begin
+  if FTR.Active then
+    FTR.Commit;
+end;
+
+Procedure TSQLShellApplication.MaybeRollBack;
+begin
+  if FTR.Active then
+    FTR.Commit;
+end;
+
+Procedure TSQLShellApplication.ExecuteSystemCommand(Const S : UTF8String);
+
+Var
+  Cmd,Args : String;
+
+begin
+  Cmd:=ExtractWord(1,S,CmdSep);
+  Args:=S;
+  Delete(Args,1,Length(Cmd)+Pos(Cmd,Args)-1);
+  While (Length(Args)>0) and (Args[1] in CmdSep) do
+    Delete(Args,1,1);
+  case Cmd of
+   'a','autocommit' :
+      FAutoCommit:=Not FAutoCommit;
+   'q','quit' :
+      begin
+      MaybeCommit;
+      Terminate;
+      end;
+   'x','exit' :
+      begin
+      MaybeRollBack;
+      Terminate;
+      end;
+   'c','commit' :
+      MaybeCommit;
+   'r','collback':
+      MaybeRollBack;
+   's',
+   'save' : SaveLast(Args);
+   '?','h','help' : WriteHelp;
+  end;
+end;
+
+Procedure TSQLShellApplication.WriteHelp;
+
+begin
+  Writeln('Commands : ');
+  Writeln('\a \autocommit  Toggle autocommit (Current autocommit :',FAutoCommit,')');
+  Writeln('\c \commit      commit');
+  Writeln('\h \help        this help');
+  Writeln('\q \quit        commit and quit');
+  Writeln('\r \rollback    commit');
+  Writeln('\x \exit        RollBack and quit');
+  Writeln('\s \save [FN]   Save result of last select to XML file');
+end;
+
+Procedure TSQLShellApplication.RunCommandLoop;
+
+Var
+  S : UTF8String;
+
+begin
+  Writeln('Enter commands, end with \q. \?, \h or \help for help.');
+  Repeat
+    Write('SQL > ');
+    Readln(S);
+    try
+      While (Length(S)>0) and (S[1] in CmdSep) do
+        Delete(S,1,1);
+      if Copy(S,1,1)='\' then
+        begin
+        Delete(S,1,1);
+        ExecuteSystemCommand(S)
+        end
+      else
+        ExecuteCommand(S)
+    except
+      On E : Exception do
+        Writeln(Format('Error %s executing command : %s',[E.ClassName,E.Message]));
+    end;
+  until Terminated;
+  Terminate;
+end;
+
+Procedure  TSQLShellApplication.Usage(Const Err : String);
+
+Var
+  L : TStrings;
+  S : String;
+
+begin
+  if (Err<>'') then
+    Writeln('Error : ',Err);
+  Writeln('Usage : ',ExtractFileName(Paramstr(0)),' [options]');
+  Writeln('Where options is one or more of:');
+  Writeln('-h --help           This help text.');
+  Writeln('-t --type=TYPE      Set connection type.');
+  Writeln('-d --database=DB    Set database name.');
+  Writeln('-H --hostname=DB    Set database hostname.');
+  Writeln('-u --username=NAME  Set database user name.');
+  Writeln('-p --password=PWD   Set database user password.');
+  Writeln('-c --charset=SET    Set database character set.');
+  Writeln('-P --port=N         Set database connection port.');
+  Writeln('Known connection types for this binary:');
+  L:=TStringList.Create;
+  try
+    GetConnectionList(L);
+    for S in L do
+      Writeln('  ',S);
+  finally
+    L.Free;
+  end;
+end;
+
+Function TSQLShellApplication.ParseArgs : Boolean;
+
+Var
+  S : String;
+
+begin
+  Result:=False;
+  S:=CheckOptions('hH:d:t:u:p:c:P:',['help','hostname:','database:','type:','username:','password:','c:charset','port']);
+  if (S<>'') or (HasOption('h','help')) then
+    begin
+    Usage(S);
+    exit;
+    end;
+  FConnType:=GetOptionValue('t','type');
+  FHostName:=GetOptionValue('H','hostname');
+  FDatabaseName:=GetOptionValue('d','database');
+  FUserName:=GetOptionValue('u','user');
+  FPassword:=GetOptionValue('p','password');
+  FCharset:=GetOptionValue('c','charset');
+  if HasOption('P','port') then
+    begin
+    FPort:=StrToIntDef(GetOptionValue('P','port'),-1);
+    if FPort=-1 then
+      Usage('Databasename not supplied');
+    exit;
+    end;
+  Result:=(FDatabaseName<>'');
+  if not Result then
+    Usage('Databasename not supplied');
+end;
+
+Procedure TSQLShellApplication.DoRun;
+
+begin
+  StopOnException:=True;
+  if Not ParseArgs then
+    begin
+    terminate;
+    exit;
+    end;
+  ConnectToDatabase;
+  RunCommandLoop;
+  DisconnectFromDatabase;
+end;
+
+begin
+  With TSQLShellApplication.Create(Nil) do
+    try
+      Initialize;
+      Run;
+    finally
+      Free;
+    end;
+end.
+
+

+ 3 - 0
packages/fcl-db/fpmake.pp

@@ -831,6 +831,9 @@ begin
     with T.Dependencies do
       AddUnit('fpjsondataset');
 
+    T:=P.Targets.AddUnit('sqldbini.pp');
+    with T.Dependencies do
+      AddUnit('sqldb');
 
     P.ExamplePath.Add('tests');
     T:=P.Targets.AddExampleProgram('dbftoolsunit.pas', DBaseOSes);

+ 25 - 20
packages/fcl-db/src/base/sqlscript.pp

@@ -641,28 +641,33 @@ begin
 end;
 
 procedure TCustomSQLScript.DefaultDirectives;
+
+  Procedure Add(S : String);
+  
+  begin
+    if FDirectives.IndexOf(S)=-1 then
+      FDirectives.Add(S);
+  end;
+
 begin
-  With FDirectives do
-    begin
-    // Insertion order matters as testing for directives will be done with StartsWith
-    if FUseSetTerm then
-      Add('SET TERM');
-    if FUseCommit then
+  // Insertion order matters as testing for directives will be done with StartsWith
+  if FUseSetTerm then
+    Add('SET TERM');
+  if FUseCommit then
+  begin
+    Add('COMMIT WORK'); {SQL Standard, equivalent to commit}
+    Add('COMMIT RETAIN'); {Firebird/Interbase; probably won't hurt on other dbs}
+    Add('COMMIT'); {Shorthand used in many dbs, e.g. Firebird}
+  end;
+  if FUseDefines then
     begin
-      Add('COMMIT WORK'); {SQL Standard, equivalent to commit}
-      Add('COMMIT RETAIN'); {Firebird/Interbase; probably won't hurt on other dbs}
-      Add('COMMIT'); {Shorthand used in many dbs, e.g. Firebird}
-    end;
-    if FUseDefines then
-      begin
-      Add('#IFDEF');
-      Add('#IFNDEF');
-      Add('#ELSE');
-      Add('#ENDIF');
-      Add('#DEFINE');
-      Add('#UNDEF');
-      Add('#UNDEFINE');
-      end;
+    Add('#IFDEF');
+    Add('#IFNDEF');
+    Add('#ELSE');
+    Add('#ENDIF');
+    Add('#DEFINE');
+    Add('#UNDEF');
+    Add('#UNDEFINE');
     end;
 end;
 

+ 6 - 9
packages/fcl-db/src/sqldb/odbc/odbcconn.pas

@@ -939,7 +939,7 @@ begin
                      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
+                     ColumnSize,             // column size (in characters)
                      DecimalDigits,          // number of decimal digits
                      Nullable),              // SQL_NO_NULLS, SQL_NULLABLE or SQL_NULLABLE_UNKNOWN
       SQL_HANDLE_STMT, ODBCCursor.FSTMTHandle, 'Could not get column properties for column %d.',[i]
@@ -969,8 +969,8 @@ begin
       SQL_CHAR:          begin FieldType:=ftFixedChar;  FieldSize:=ColumnSize; end;
       SQL_VARCHAR:       begin FieldType:=ftString;     FieldSize:=ColumnSize; end;
       SQL_LONGVARCHAR:   begin FieldType:=ftMemo;       FieldSize:=BLOB_BUF_SIZE; end; // is a blob
-      SQL_WCHAR:         begin FieldType:=ftFixedWideChar; FieldSize:=ColumnSize*sizeof(Widechar); end;
-      SQL_WVARCHAR:      begin FieldType:=ftWideString; FieldSize:=ColumnSize*sizeof(Widechar); end;
+      SQL_WCHAR:         begin FieldType:=ftFixedWideChar; FieldSize:=ColumnSize; end;
+      SQL_WVARCHAR:      begin FieldType:=ftWideString; FieldSize:=ColumnSize; end;
       SQL_SS_XML,
       SQL_WLONGVARCHAR:  begin FieldType:=ftWideMemo;   FieldSize:=BLOB_BUF_SIZE; end; // is a blob
       SQL_DECIMAL:       begin FieldType:=ftFloat;      FieldSize:=0; end;
@@ -1120,10 +1120,7 @@ begin
     end;
 
     // add FieldDef
-    with FieldDefs.Add(FieldDefs.MakeNameUnique(ColName), FieldType, FieldSize, (Nullable=SQL_NO_NULLS) and (AutoIncAttr=SQL_FALSE), i) do
-    begin
-      if Updatable = SQL_ATTR_READONLY then Attributes := Attributes + [faReadonly];
-    end;
+    AddFieldDef(FieldDefs, i, ColName, FieldType, FieldSize, -1, False, (Nullable=SQL_NO_NULLS) and (AutoIncAttr=SQL_FALSE), Updatable=SQL_ATTR_READONLY);
   end;
 end;
 
@@ -1166,9 +1163,9 @@ begin
   // TODO: finish this
   case FieldDef.DataType of
     ftWideString,ftFixedWideChar: // mapped to TWideStringField
-      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_WCHAR, buffer, FieldDef.Size+sizeof(WideChar), @StrLenOrInd); //buffer must contain space for the null-termination character
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_WCHAR, buffer, FieldDef.Size*FieldDef.CharSize+sizeof(WideChar), @StrLenOrInd); //buffer must contain space for the null-termination character
     ftGuid, ftFixedChar,ftString: // are mapped to a TStringField (including TGuidField)
-      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_CHAR, buffer, FieldDef.Size+1, @StrLenOrInd);
+      Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_CHAR, buffer, FieldDef.Size*FieldDef.CharSize+1, @StrLenOrInd);
     ftSmallint:           // mapped to TSmallintField
       Res:=SQLGetData(ODBCCursor.FSTMTHandle, FieldDef.Index+1, SQL_C_SSHORT, buffer, SizeOf(Smallint), @StrLenOrInd);
     ftInteger,ftAutoInc:  // mapped to TLongintField

+ 219 - 0
packages/fcl-db/src/sqldb/sqldbini.pp

@@ -0,0 +1,219 @@
+unit sqldbini;
+
+{$mode objfpc}{$H+}
+{$modeswitch typehelpers}
+
+interface
+
+uses
+  Classes, SysUtils, sqldb, inifiles, strutils;
+
+Type
+  TSQLDBIniOption = (sioClearOnRead,      // Clear values first
+                     sioSkipPassword,     // Do not save/load password
+                     sioSkipMaskPassword, // do not mask the password
+                     sioUserNameAsMask,   // use the username as mask for password
+                     sioSkipParams        // Do not read/write params.
+                     );
+  TSQLDBIniOptions = set of TSQLDBIniOption;
+
+  { TSQLDBIniHelper }
+
+  TSQLDBIniHelper = class helper for TSQLConnection
+  Private
+    Procedure ClearValues;
+  Public
+    Procedure LoadFromIni(Const aIni: TCustomIniFile; aOptions : TSQLDBIniOptions = []); overload;
+    Procedure LoadFromIni(Const aIni: TCustomIniFile; ASection : String; aOptions : TSQLDBIniOptions); overload;
+    Procedure LoadFromFile(Const aFileName : String; aOptions : TSQLDBIniOptions = []); overload;
+    Procedure LoadFromFile(Const aFileName : String; Const ASection : String; aOptions : TSQLDBIniOptions); overload;
+    Procedure SaveToFile(Const aFileName : String; aOptions : TSQLDBIniOptions = []);overload;
+    Procedure SaveToFile(Const aFileName : String; Const ASection : String; aOptions : TSQLDBIniOptions = []);overload;
+    Procedure SaveToIni(Const aIni: TCustomIniFile; aOptions : TSQLDBIniOptions = []); overload;
+    Procedure SaveToIni(Const aIni: TCustomIniFile; ASection : String; aOptions : TSQLDBIniOptions); overload;
+  end;
+
+Var
+  TrivialEncryptKey : String = 'SQLDB';
+  DefaultSection : String = 'Connection';
+
+implementation
+
+{ TSQLDBIniHelper }
+
+procedure TSQLDBIniHelper.ClearValues;
+begin
+  HostName:='';
+  DatabaseName:='';
+  UserName:='';
+  Password:='';
+  CharSet:='';
+  Params.Clear;
+  Port:=0;
+end;
+
+
+Const
+  KeyHost = 'Host';
+  KeyDatabaseName = 'DatabaseName';
+  KeyUserName = 'UserName';
+  KeyPassword = 'Password';
+  KeyPort = 'Port';
+  keyParams = 'Params';
+  KeyCharset = 'Charset';
+  KeyRole = 'Role';
+
+Const
+  ForbiddenParamKeys : Array[1..8] of unicodestring
+                     = (keyHost,KeyDatabaseName,KeyUserName,KeyPassword,KeyPort,keyParams,keyCharSet,keyRole);
+  ParamSeps = [',',';',' '];
+
+procedure TSQLDBIniHelper.LoadFromIni(const aIni: TCustomIniFile; ASection: String; aOptions: TSQLDBIniOptions);
+
+Var
+  M,N,P : String;
+  I : integer;
+
+begin
+  With aIni do
+    begin
+    if (sioClearOnRead in aOptions) then
+       ClearValues;
+    HostName:=ReadString(ASection,KeyHost,HostName);
+    DatabaseName:=ReadString(ASection,KeyDatabaseName,DatabaseName);
+    UserName:=ReadString(ASection,KeyUserName,UserName);
+    CharSet:=ReadString(ASection,KeyCharset,CharSet);
+    Role:=ReadString(ASection,KeyRole,Role);
+    Port:=ReadInteger(ASection,KeyPort,Port);
+    // optional parts
+    if not (sioSkipPassword in aOptions) then
+      begin
+      if sioSkipMaskPassword in aOptions then
+        P:=ReadString(ASection,KeyPassword,Password)
+      else
+        begin
+        P:=ReadString(ASection,KeyPassword,'');
+        if (P<>'') then
+          begin
+          if sioUserNameAsMask in aOptions then
+            M:=UserName
+          else
+            M:=TrivialEncryptKey;
+          P:=XorDecode(M,P);
+          end;
+        end;
+      Password:=P;
+      end;
+    if not (sioSkipParams in aOptions) then
+      begin
+      M:=ReadString(ASection,keyParams,'');
+      For I:=1 to WordCount(M,ParamSeps) do
+        begin
+        N:=ExtractWord(I,M,ParamSeps);
+        if IndexStr(Utf8Decode(N),ForbiddenParamKeys)=-1 then
+          begin
+          P:=ReadString(ASection,N,'');
+          Params.Values[N]:=P;
+          end;
+        end;
+      end;
+    end;
+end;
+
+procedure TSQLDBIniHelper.LoadFromIni(const aIni: TCustomIniFile; aOptions: TSQLDBIniOptions);
+begin
+  LoadFromIni(aIni,Defaultsection,aOptions);
+end;
+
+procedure TSQLDBIniHelper.LoadFromFile(const aFileName: String; aOptions: TSQLDBIniOptions);
+
+
+begin
+  Loadfromfile(aFileName,DefaultSection,aOptions);
+end;
+
+procedure TSQLDBIniHelper.LoadFromFile(const aFileName: String; const ASection: String; aOptions: TSQLDBIniOptions);
+
+Var
+  Ini : TCustomIniFile;
+
+begin
+  Ini:=TMeminiFile.Create(aFileName);
+  try
+    LoadFromIni(Ini,aSection,aOptions);
+  finally
+    Ini.Free;
+  end;
+end;
+
+procedure TSQLDBIniHelper.SaveToFile(const aFileName: String; aOptions: TSQLDBIniOptions);
+begin
+  SaveToFile(aFileName,DefaultSection,aOptions);
+end;
+
+procedure TSQLDBIniHelper.SaveToFile(const aFileName: String; const ASection: String; aOptions: TSQLDBIniOptions);
+Var
+  Ini : TCustomIniFile;
+
+begin
+  Ini:=TMeminiFile.Create(aFileName);
+  try
+    SaveToini(Ini,aSection,aOptions);
+  finally
+    Ini.Free;
+  end;
+end;
+
+procedure TSQLDBIniHelper.SaveToIni(const aIni: TCustomIniFile; aOptions: TSQLDBIniOptions);
+begin
+  SaveToIni(aIni,DefaultSection,aOptions);
+end;
+
+procedure TSQLDBIniHelper.SaveToIni(const aIni: TCustomIniFile; ASection: String; aOptions: TSQLDBIniOptions);
+Var
+  M,N,P : String;
+  I : integer;
+
+begin
+  With aIni do
+    begin
+    WriteString(ASection,KeyHost,HostName);
+    WriteString(ASection,KeyDatabaseName,DatabaseName);
+    WriteString(ASection,KeyUserName,UserName);
+    WriteString(ASection,KeyCharset,CharSet);
+    WriteString(ASection,KeyRole,Role);
+    WriteInteger(ASection,KeyPort,Port);
+    if not (sioSkipPassword in aOptions) then
+      begin
+      P:=Password;
+      if Not (sioSkipMaskPassword in aOptions) then
+        begin
+        if sioUserNameAsMask in aOptions then
+          M:=UserName
+        else
+          M:=TrivialEncryptKey;
+        P:=XorEncode(M,P);
+        end;
+      WriteString(ASection,KeyPassword,P);
+      end;
+    if not (sioSkipParams in aOptions) then
+      begin
+      M:='';
+      for I:=0 to Params.Count-1 do
+        begin
+        Params.GetNameValue(I,N,P);
+        if (N<>'') and (IndexStr(Utf8Decode(N),ForbiddenParamKeys)=-1) then
+          begin
+          WriteString(ASection,N,P);
+          if (M<>'') then
+            M:=M+',';
+          M:=M+N;
+          end;
+        end;
+      WriteString(ASection,KeyParams,M);
+      end;
+    end;
+end;
+
+end.
+