修改虫虫的三层数据库类中的一个单元文件untRmoDbClient.pas

虫子的三层数据库类很好用,使用中发现一个小问题,就是当数据库字段较多时,如果DBGEID中只显示其中几列的话,在修改数据后移动到下一行进,会出错,无法更新数据,检查是生成的UPDATE语句有问题,没有在DBGRID中显示的列也生成进去了,并给了个‘’空值,刚好数据库设置了字段不能为空,就报错了,根据自己实用,修改如下

{******************************************************* 万剑修改                585行 20120420

        单元名称:untRmoDbClient.pas         创建日期:2008-09-16 17:25:52         创建者   马敏钊         功能:     远程数据库客户端         当前版本: v2.0.2

更新历史 v1.0  单元实现 v1.1  解决不支持自增长字段的问题 v1.2  解决id号必须是第1个字段的问题 v1.3  为增加速度,做缓冲不用每次生成语句 ,改变自动更新时导致filter属性暂用的方式 v1.4  在sabason 兄的热心帮助下,解决了流试传输存在的问题,大大提高了传输效率 20100413 v1.5  全面修改为支持高效率的UniDAC数据库驱动套件 和ClientDataset (原来是ADO方式)支持所有主流数据库,大幅提高传输效率,且使用方法没有改变 v1.6  解决流传输存在的BUG  ,修正最后一个字段为blob字段导致语句生成错误的BUG v1.7  增加服务端sys.ini文件配置客户端登陆权限,增加批量执行SQL语句接口 v1.8  增加服务端提供自动升级功能,可以升级多个文件或者目录,可选择强制升级或者客户端可选升级 v2.0  增加asio高性能 C++ 完成端口稳定库的封装支持 v2.1  增加存储过程调用的支持(参考静水流深的修改版本,在此表示感谢) v2.0.2 2011-04-20                   统一和服务端的版本号 ,从v2.1修改为v2.0.2                   由于MAX()方式获取数据记录当数据表内存在大量记录时会很慢,而且可能导致ID冲突,                   所以特,增加快速获取自增长ID的方式,客户端可配置是否使用这种方式 *******************************************************}

unit untRmoDbClient;

interface

uses   Classes, UntsocketDxBaseClient, IdComponent, Controls, ExtCtrls, db, dbclient, midaslib;

type   TConnthread = class;   TSelectitems = class   public     Sql: string;   end;   TRmoClient = class(TAsioClient)   private     gLmemStream: TMemoryStream;     FCachSQllst, FsqlLst: TStrings; //用来记录已经打开了的数据集 以及对于的语句     FSqlPart1, FSqlPart2: string;

    Fsn: Cardinal;     FQryForID: TClientDataSet;     FIsDisConn: boolean; //是否是自己手动断开连接的     Ftimer: TTimer; //连接保活器     FisConning: Boolean; //是否连接成功     //定时检查是否需要重连 或者连接断开     procedure OnCheck(Sender: TObject);      //检查是否连接存活     procedure checkLive;

    procedure OnBeginPost(DataSet: TDataSet);     procedure OnBeforeDelete(DataSet: TDataSet);     function GetSvrmaxID(Iidname, itablename: string): integer;   public     IsSpeedGetID: Boolean; //是否使用高速方式获取自增长ID     IsInserIDfield: boolean; //是否插入语句 支持ID字段 自增长不允许插入该字段默认是false     FLastInsertID: Integer; //insert语句时返回插入记录的自增字段的值

    //连接服务端     function ConnToSvr(ISvrIP: ansistring; ISvrPort: Integer = 9988; Iacc: ansistring = '';       iPsd: ansistring = ''): boolean;     //断开连接     procedure DisConn;     //重新连接新的IP     function ReConn(ISvrIP: ansistring; IPort: Integer = -1; Iacc: ansistring = '';       iPsd: ansistring = ''): boolean;

    //将post模式变更为 更新语句到远端执行     procedure ReadySqls(IAdoquery: TClientDataSet);

    //执行一条语句     function ExeSQl(ISql: ansistring): Integer;     //打开一个过数据集     function OpenAndataSet(ISql: ansistring; IADoquery: TClientDataSet): Boolean;     //批量提交语句  立即执行所传入的语句列表     function BathExecSqls(IsqlList: TStrings): Integer;     //执行一个存储过程     //参数 执行语句  是否需要返回数据集     function ExecProc(iSQL: ansistring; IsBackData: boolean; cds: TClientDataSet =       nil): Boolean;

    //检查升级     procedure CheckUpdate;

    procedure OnCreate; override;     procedure OnDestory; override;   end;

TConnthread = class(TThread)   public     Client: TRmoClient;     procedure execute; override;   end;

var   //远程连接控制对象   Gob_RmoCtler: TRmoClient;   GCurrVer: integer = 1; //当前程序升级版本号

implementation

uses untfunctions, sysUtils, UntBaseProctol, IniFiles, ADOInt, Variants,   Windows, untASIOSvr;

function TRmoClient.BathExecSqls(IsqlList: TStrings): Integer; var   IErr: ansistring;   llen, i: Integer;   ls: TMemoryStream; begin   //批量执行SQL语句   ls := TMemoryStream.Create;   IsqlList.SaveToStream(ls);   EnCompressStream(ls);   llen := 4 + 4 + ls.Size;   SendAsioHead(llen);   WriteInteger(110);   SendZIpStream(ls, Self, true);   llen := ReadInteger();   if llen = -1 then begin     llen := ReadInteger();     IErr := ReadStr(llen); //    IsqlList.SaveToFile('D:\2.txt');     raise Exception.Create(IErr);   end   else begin     Result := llen;   end; end;

procedure TRmoClient.checkLive; begin   try     if IsConnected then begin       SendAsioHead(4);       if WriteInteger(4) <> 4 then begin         if FIsDisConn = False then           FisConning := False;       end;     end     else begin       if FIsDisConn = False then         FisConning := False;     end;

  except     if FIsDisConn = False then       FisConning := False;   end; end;

procedure TRmoClient.CheckUpdate; var   i, lstrlrn, illen: Integer;   li, lr, lm: integer;   ls, lspath, lflst: ansistring;   lspit: TStringList; begin   SendAsioHead(4);   WriteInteger(9998);   lr := ReadInteger;   if lr > 0 then begin     lspit := TStringList.Create;     lr := ReadInteger; //ver     lm := ReadInteger;     lstrlrn := Readinteger;     lspath := ReadStr(lstrlrn);     lstrlrn := Readinteger;     ls := ReadStr(lstrlrn);     li := ReadInteger;     lflst := ReadStr(li);     if lr > GCurrVer then begin       lspit.Add(IntToStr(lm));       lspit.Add(ls);       //后台下载下来       GetEveryWord(lflst, '|');       lspit.Add(IntToStr(GlGetEveryWord.Count));

      for i := 0 to GlGetEveryWord.Count - 1 do begin // Iterate         ls := GlGetEveryWord[i];         illen := Length(ls);         SendAsioHead(8 + illen);         SendHead(9997);         Writeinteger(illen);         Write(ls);         li := ReadInteger;         if li = 1 then begin           ls := StringReplace(GlGetEveryWord[i], lspath, '', []);           ls := GetCurrPath + ls;           ForceDirectories(ExtractFilePath(ls));           GetZipFile(ls);           lspit.Add(ls);         end;       end; // for       lspit.SaveToFile('up.cfg');       lspit.Free;       WinExec(pansichar('up.exe  ' + ExtractFileName(ParamStr(0))), SW_SHOW);     end;   end; end;

function TRmoClient.ConnToSvr(ISvrIP: ansistring; ISvrPort: Integer = 9988;   Iacc: ansistring = ''; iPsd: ansistring = ''): boolean; var   i: Integer;   ls: ansistring; begin   Result := True;   if (IsConnected = false) or (FHost <> ISvrIP) or (FPort <> ISvrPort) then begin     if IsConnected then       DisConn;     FHost := ISvrIP;     FPort := ISvrPort;     Facc := Iacc;     Fpsd := iPsd;     FIsDisConn := False;     try       Result := Connto(FHost, FPort);     except       Result := False;       FIsDisConn := False;     end;     if Result = True then begin //        SendHead(CTSLogin); //        WriteInteger(CClientID); //        if ReadInteger <> STCLogined then //          Result := False;       ls := format('%s|%s', [Iacc, Str_Encry(iPsd, 'rmo')]);       Writeinteger(Length(ls));       Write(ls);       if ReadInteger <> STCLogined then begin         Result := False;         DisConn;         FisConning := false;         Exit;       end;       FisConning := True;       FIsDisConn := False;       Ftimer.Enabled := True;     end;   end; end;

procedure TRmoClient.DisConn; begin   try     CloseConn; //    if IsConnected then //      DisConn;   except   end;   FIsDisConn := True; end;

{ TConnthread }

procedure TConnthread.execute; begin   try     if Client.ConnToSvr(Client.FHost, Client.FPort, Client.Facc, Client.Fpsd) then begin       Client.FisConning := True;     end;   finally     Client.Ftimer.Tag := 0;   end; end;

function StreamToVarArray(const S: TStream): Variant; var P: Pointer;   C: Integer;   L: Integer; begin   S.Position := 0;   C := S.Size;   Result := VarArrayCreate([1, C], varByte);   L := Length(Result);   if L <> 0 then     ;   P := VarArrayLock(Result);   try     S.Read(P^, C);   finally     VarArrayUnlock(Result);   end; end;

procedure VarArrayToStream(const V: Variant; S: TStream); var P: Pointer;   C: Integer; begin   if not VarIsArray(V) then     raise Exception.Create('Var is not array');   if VarType(V[1]) <> varByte then     raise Exception.Create('Var array is not blob array');   C := VarArrayHighBound(V, 1) - VarArrayLowBound(V, 1) + 1;   if not (C > 0) then     Exit;

  P := VarArrayLock(V);   try     S.Write(P^, C * SizeOf(Byte));     S.Position := 0;   finally     VarArrayUnLock(V);   end; end;

function DatasetFromStream(Idataset: TClientDataSet; Stream:   TMemoryStream): boolean; var   RS: Variant; begin   Result := false;   if Stream.Size < 1 then     Exit;   try     Idataset.Data := StreamToVarArray(Stream);     Result := true;   finally;   end;

end;

function TRmoClient.ExecProc(iSQL: ansistring; IsBackData: boolean; cds:   TClientDataSet = nil): Boolean; var   nReturn, i: Integer;   sErr: ansistring;   stmp: ansistring; begin   //在firebird中   //执行存储过程   //sql = 'Execute procedure ' + ProcName + '(' + Format(ParamsValues, args) + ')';   //执行返回数据集的存储过程   //sql = 'select * from ' + ProcName + '(' + Format(ParamsValues, args) + ')';   Result := True;   FLastInsertID := -1;   if not IsBackData then begin //执行存储过程     SendAsioHead(4 + 4 + length(iSQL));     WriteInteger(1010);     WriteInteger(Length(iSQL));     Write(iSQL);     nReturn := ReadInteger();     if nReturn = -1 then begin       nReturn := ReadInteger();       sErr := ReadStr(nReturn);       Result := False;       raise Exception.Create(Format('错误: %s', [sErr]));     end else begin       //{ TODO -owshx -c :  2010-11-10 下午 02:26:32 }       //stmp := ReadStr(ReadInteger());   //返回output参数值     end;   end else begin //有返回数据集     SendAsioHead(4 + 4 + length(iSQL));     WriteInteger(1011); //从存储过程返回数据集     WriteInteger(Length(iSQL));     Write(iSQL);     nReturn := ReadInteger();     if nReturn = -1 then begin       nReturn := ReadInteger();       sErr := ReadStr(nReturn);       raise Exception.Create(Format('执行语句<%s>时发生错误。', [sErr]));     end else begin       if glmemStream = nil then         glmemStream := TMemoryStream.Create       else         glmemStream.clear;       GetZipStream(glmemStream, self);       if Assigned(cds) then         DatasetFromStream(cds, glmemStream)       else begin         raise Exception.Create('返回的数据集没有指定载体。');         Result := False;       end;     end;   end;

end;

function TRmoClient.ExeSQl(ISql: ansistring): Integer; var   llen, i: Integer; begin   llen := Length(ISql);   SendAsioHead(8 + llen);   WriteInteger(1);   WriteInteger(llen);   Write(ISql);   llen := ReadInteger();   if llen = -1 then begin     llen := ReadInteger();     ISql := ReadStr(llen);     raise Exception.Create(ISql);   end   else begin     Result := llen;   end; end;

//------------------------------------------------------------------------------ // 数据post时自动更新服务端 2009-05-22 马敏钊 // 要求表必须有id号而且必须是第一个字段 //------------------------------------------------------------------------------

var   lglst: Tstrings;

function TRmoClient.GetSvrmaxID(Iidname, itablename: string): integer; var   llen, i: Integer;   ISql: string; begin   if IsSpeedGetID then begin     ISql := Format('%s|%s', [Iidname, itablename]);     llen := Length(ISql);     SendAsioHead(8 + llen);     WriteInteger(7);     WriteInteger(llen);     Write(ISql);     llen := ReadInteger();     Result := ReadInteger;     if llen = -1 then begin       llen := ReadInteger();       ISql := ReadStr(llen);       raise Exception.Create(ISql);     end;   end   else begin                   //如果需要ID字段 自动获取     if FQryForID = nil then       FQryForID := TClientDataSet.Create(nil); //    获取ID     OpenAndataSet(Format('select max(%s) as myid from %s', [Iidname, itablename]), FQryForID); //     Result := FQryForID.FieldByName('myid').AsInteger + 1;   end; end;

procedure TRmoClient.OnBeforeDelete(DataSet: TDataSet); var   I: Integer;   lsql: string;   Result, ltablename: string;   Lkey, lvalue: string;   Lindex: integer; begin

  //获取表名   i := FsqlLst.IndexOf(IntToStr(integer(DataSet)));   if i > -1 then     lsql := LowerCase(TSelectitems(FsqlLst.Objects[i]).Sql); //  LowerCase(DataSet.Filter);   if Pos('select', lsql) > 0 then begin     if lglst = nil then       lglst := TStringList.Create;     GetEveryWord(lsql, lglst, ' ');     for i := 0 to lglst.Count - 1 do       if lglst.Strings[i] = 'from' then begin         Lindex := i;         Break;       end;     if Lindex < 2 then       ExceptTip('SQL语句错误!');     ltablename := '';     for i := Lindex + 1 to lglst.Count - 1 do       if lglst.Strings[i] <> '' then begin         ltablename := lglst.Strings[i];         Break;       end;     if ltablename = '' then       ExceptTip('SQL语句错误!');   end   else     ExceptTip('无法自动提交,请先执行select');   //获取方法   with DataSet.Fields do begin     Result := 'delete from ' + ltablename + Format(' where %s=%d', [Fields[0].FieldName, Fields[0].AsInteger]);     ExeSQl(Result);   end; end;

procedure TRmoClient.OnBeginPost(DataSet: TDataSet); var   I, n: Integer;   lsql, lBobName: string;   Result, FtableName: string;   Lkey, lvalue: string;   Lindex: integer;   LblobStream: TStream; begin   //获取表名   i := FsqlLst.IndexOf(IntToStr(integer(DataSet)));   if i > -1 then     lsql := LowerCase(TSelectitems(FsqlLst.Objects[i]).Sql); //  LowerCase(DataSet.Filter);   if Pos('select', lsql) > 0 then begin     if lglst = nil then       lglst := TStringList.Create;     GetEveryWord(lsql, lglst, ' ');     for i := 0 to lglst.Count - 1 do       if lglst.Strings[i] = 'from' then begin         Lindex := i;         Break;       end;     if Lindex < 2 then       ExceptTip('SQL语句错误!');     FtableName := '';     for i := Lindex + 1 to lglst.Count - 1 do       if lglst.Strings[i] <> '' then begin         FtableName := lglst.Strings[i];         Break;       end;     if FtableName = '' then       ExceptTip('SQL语句错误!');   end   else     ExceptTip('无法自动提交,请先执行select');

  //获取方法   case TClientDataSet(DataSet).State of //     dsinsert: begin         with DataSet.Fields do begin         //如果第一个字段为只读,说明是自增长ID字段 改掉它           if Fields[0].ReadOnly = true then begin            // IsInserIDfield := True;  // 要你插入自增长字段干机八             Fields[0].ReadOnly := False;           end;           if DataSet.State = dsInsert then begin //------------------------------------------------------------------------------ // 更换为通过服务端获取ID  2011-4-20 10:46:02   马敏钊 //------------------------------------------------------------------------------             DataSet.Fields[0].AsInteger := GetSvrmaxID(DataSet.Fields[0].FieldName, FtableName);           end;          //IsInserIDfield:=True;//万剑修改 2012 04 20 //是否插入语句 支持ID字段 自增长不允许插入该字段默认是false           if IsInserIDfield then begin             n := 0;  //           end           else             n := 1;           FSqlPart1 := 'insert into ' + FtableName + '(';           FSqlPart2 := '';           for i := n to count - 1 do begin             if (fields[i].IsNull) or (trim(fields[i].AsString) = '') then               continue;   //终止循环             //如果有blob字段则跳过             if Fields[i].DataType in [ftBlob] then begin               LblobStream := TMemoryStream.Create;               TBlobField(Fields[i]).SaveToStream(LblobStream);               EnCompressStream(TMemoryStream(LblobStream));               lBobName := Fields[i].FieldName; //------------------------------------------------------------------------------ // 如果是最后一个字段则跳过之前去掉上次生成的,号  2010-04-21 马敏钊 //------------------------------------------------------------------------------               if i = count - 1 then begin                 if FSqlPart1[length(FSqlPart1) - 1] = ',' then begin                   FSqlPart1 := copy(FSqlPart1, 1, length(FSqlPart1) - 1);                   FSqlPart2 := copy(FSqlPart2, 1, length(FSqlPart2) - 1);                 end;               end;               Continue;             end;

            FSqlPart1 := FSqlPart1 + ifthen(i = n, '', ',') + Fields[i].FieldName;             case Fields[i].DataType of               ftCurrency, ftBCD, ftWord, ftFloat, ftBytes: FSqlPart2 := FSqlPart2 + ifthen(i = n, '', ',') + ifthen(Fields[i].AsString = '', '0', Fields[i].AsString);               ftBoolean, ftSmallint, ftInteger: FSqlPart2 := FSqlPart2 + ifthen(i = n, '', ',') + IntToStr(Fields[i].AsInteger);               ftDate, ftDateTime: if Fields[i].AsString = '' then FSqlPart2 := FSqlPart2 + ifthen(i = n, '', ',') + 'null' else                   FSqlPart2 := FSqlPart2 + ifthen(i = n, '', ',') + '''' + Fields[i].AsString + '''' // Modified by qnaqbgss 2010/9/11 17:56:49               else FSqlPart2 := FSqlPart2 + ifthen(i = n, '', ',') + '''' + Fields[i].AsString + '''';             end;           end;           Result := FSqlPart1 + ') values (' + FSqlPart2 + ')';         end;       end;     dsEdit: begin         with DataSet.Fields do begin           Result := 'Update ' + FtableName + ' Set ';           for I := 0 to count - 1 do begin // Iterate             if I = 0 then begin               Lkey := Fields[i].FieldName;               lvalue := Fields[i].AsString;               Continue;             end;             if (fields[i].IsNull) or (trim(fields[i].AsString) = '') then              begin               if i = count - 1 then begin     // 这里是我加进去的 万剑 20120420                 Result := copy(Result, 1, length(Result) - 1);               end;               continue;              end; 

             //如果有blob字段则跳过             if Fields[i].DataType in [ftBlob] then begin               LblobStream := TMemoryStream.Create;               TBlobField(Fields[i]).SaveToStream(LblobStream);               EnCompressStream(TMemoryStream(LblobStream));               lBobName := Fields[i].FieldName; //------------------------------------------------------------------------------ // 如果是最后一个字段则跳过之前去掉上次生成的,号  2010-04-21 马敏钊 //------------------------------------------------------------------------------               if i = count - 1 then begin                 Result := copy(Result, 1, length(Result) - 1);               end;               Continue;             end;

            Result := Result + Fields[i].FieldName + '=';             case Fields[i].DataType of //               ftCurrency, ftBCD, ftWord: Result := Result + Fields[i].AsString;               ftFloat: Result := Result + Fields[i].AsString;               ftBytes, ftSmallint, ftInteger: Result := Result + IntToStr(Fields[i].AsInteger);               ftBoolean:Result := Result +Booltostr(fields[i].AsBoolean,true);               ftDate, ftDateTime: if Fields[i].AsString = '' then result := Result + 'null' else                   result := Result + '''' + Fields[i].AsString + '''' // Modified by qnaqbgss 2010/9/11 17:57:14               else                 Result := Result + '''' + Fields[i].AsString + '''';             end; // case             if i <> Count - 1 then               Result := Result + ',';           end; // for           Result := Result + Format(' where %s=%s', [Lkey, lvalue]);         end; // with       end;   end; // case   ExeSQl(Result);   //如果有blob字段则 追加写入   if LblobStream <> nil then begin     lsql := format('update %s set %s=:%s where %s=%d', [FtableName, lBobName, 'Pbob'       , DataSet.Fields[0].FieldName, DataSet.Fields[0].AsInteger]);     SendAsioHead(8 + length(lsql) + 4 + LblobStream.Size);     WriteInteger(6);     WriteInteger(length(lsql));     Write(lsql);     WriteStream(LblobStream);   end; end;

procedure TRmoClient.OnCheck(Sender: TObject); begin   if TTimer(sender).tag = 0 then begin     if ((IsConnected = false) or (FisConning = false)) and (FIsDisConn = false) then begin       TTimer(sender).tag := 1;       with TConnthread.Create(True) do begin         FreeOnTerminate := True;         Client := Self;         Resume;       end;     end     else begin       checkLive;     end;   end; end;

procedure TRmoClient.OnCreate; begin   inherited;   IsSpeedGetID := True;   FCachSQllst := THashedStringList.Create;   Ftimer := TTimer.Create(nil);   Ftimer.OnTimer := OnCheck;   Ftimer.Interval := 3000;   Ftimer.Enabled := False;   Ftimer.Tag := 0;   FisConning := false;   FIsDisConn := False;   FsqlLst := THashedStringList.Create;   gLmemStream := TMemoryStream.Create; end;

procedure TRmoClient.OnDestory; begin   inherited;   FCachSQllst.Free;   if FQryForID <> nil then     FQryForID.Free;   Ftimer.Free;   FsqlLst.Free;   gLmemStream.Free; end;

function TRmoClient.OpenAndataSet(ISql: ansistring;   IADoquery: TClientDataSet): Boolean; var   llen, i: Integer;   ls: ansistring;   Lend: integer;   Litem: TSelectitems; begin   inc(Fsn);   Lend := 0;   ls := ISql;   llen := length(isql);   SendAsioHead(8 + llen);   WriteInteger(2);   WriteInteger(llen);   Write(ISql);   llen := ReadInteger();   if llen = -1 then begin     llen := ReadInteger();     ISql := ReadStr(llen);     raise Exception.Create(ISql);   end   else begin     //记录着 是否可以自动保存     i := FsqlLst.IndexOf(IntToStr(integer(IADoquery)));     if i = -1 then begin       Litem := TSelectitems.Create;       FsqlLst.AddObject(IntToStr(integer(IADoquery)), Litem);     end     else       Litem := TSelectitems(FsqlLst.Objects[i]);     Litem.Sql := ISql;      //记录一下     ReadySqls(IADoquery);     if llen = 1 then begin       if gLmemStream = nil then         gLmemStream := TMemoryStream.Create;       GetZipStream(gLmemStream, self);       DatasetFromStream(IADoquery, gLmemStream);     end     else begin       ISql := GetCurrPath + GetDocDate + GetDocTime + IntToStr(Fsn);       GetZipFile(ISql);       IADoquery.LoadFromFile(ISql);       DeleteFile(pchar(ISql));     end;     Result := True;   end; end;

procedure TRmoClient.ReadySqls(IAdoquery: TClientDataSet); begin   IAdoquery.BeforePost := OnBeginPost;   IAdoquery.BeforeDelete := OnBeforeDelete; end;

function TRmoClient.ReConn(ISvrIP: ansistring; IPort: Integer = -1; Iacc: ansistring = '';   iPsd: ansistring = ''): boolean; begin   Result := False;   if IsLegalIP(ISvrIP) then begin     Result := ConnToSvr(ISvrIP, IfThen(IPort = -1, FPort, IPort), iacc, ipsd);   end; end;

end.

posted on 2012-12-25 23:41  万剑  阅读(713)  评论(0编辑  收藏  举报