服务器端自定义OpenSQL、ExecSQL

本文介绍了在服务器端实现SQL参数化的方法,包括定义参数结构体、处理不同类型的参数值及参数检查等内容,旨在提高SQL操作的安全性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在服务器端为了防止注入,不仅需要客户端传过SQL还需要传入参数,

一、参数定义可以使用结构体数组:

type

   TParamlocal =record
      ParamName:PChar;
      ParamType:PChar;
      ParamValue:PChar;
   end;

   TParamArrlocal = record
        strArr:array of TParamlocal;
   end;

即定义一个结构体来保存参数的参数名称、参数类型、参数值,都采用字符串类型。方便统一传输。

在此结构体基础上定义一个变长数组,这样不限定传入参数的个数。

二、定义服务器方法

至少需要传入两个参数:一个是带参数的SQL语句,一个是参数数组;其他的譬如,是否检查参数,返回值等可依据实际情况考虑。在设计时建议增加,方便以后扩展。

上源码如下:

function TNS_Ver2Ex.ExecClientSQL(const sSQL: AnsiString; const ParamsServer: TParamArray; const ParamCheck:Boolean; const Des1: AnsiString; out Des2: AnsiString): Integer;
var
  QryTDB: TADOQuery;
  QryConn: TADOConnection;
  i:Integer;
begin
  result := 0;
  ServerForm.LogMessage(#9'=====================================================');
  ServerForm.LogMessage(#9' Start Exec SQL:'+sSql+' at '+DateTimeToStr(Now));
  try
    CoInitialize(nil);
    try
      QryTDB := TADOQuery.Create(nil);
      QryTDB.Close;
      QryTDB.SQL.Clear;
      QryConn:=ServerForm.ADOPool.GetCon(ServerForm.ADOConfig);
      QryTDB.Connection := QryConn;
      QryTDB.SQL.Add(sSql);
      QryTDB.CommandTimeout:=ServerForm.ADOConfig.ConnectionTimeout;
      QryTDB.LockType:=ltReadOnly;
      for i:=0 to ParamsServer.count - 1 do
      begin
        if Assigned(QryTDB.Parameters.FindParam(ParamsServer.Items[i].ParamName)) then
          if ((ParamsServer.Items[i].ParamType = 'ANSISTRING') or (ParamsServer.Items[i].ParamType = 'PCHAR') or (ParamsServer.Items[i].ParamType = 'STRING')) then
          begin
              QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftString;
              QryTDB.Parameters.parambyname(ParamsServer.Items[i].ParamName).value := ParamsServer.Items[i].ParamValue;
          end
          else if ParamsServer.Items[i].ParamType = 'BIGINT' then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftLargeint;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=StrToInt64(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else if (ParamsServer.Items[i].ParamType = 'INTEGER') or (ParamsServer.Items[i].ParamType = 'INT') then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftInteger;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=StrToInt(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else if (ParamsServer.Items[i].ParamType = 'REAL') or (ParamsServer.Items[i].ParamType = 'FLOAT') then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftFloat;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=StrToFloat(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else if (ParamsServer.Items[i].ParamType = 'TDATETIME') or (ParamsServer.Items[i].ParamType = 'DATETIME') then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftDateTime;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=ServerForm._StrtoDatetime(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else
          begin
             QryTDB.Parameters[i].DataType:=ftUnknown;
             QryTDB.Parameters.parambyname(ParamsServer.Items[i].ParamName).value := ParamsServer.Items[i].ParamValue;
          end;
      end;
      QryTDB.ParamCheck:=ParamCheck;
      QryTDB.Prepared:=True;
      QryTDB.ExecSQL;
      result := 1;
      if Assigned(QryTDB.Parameters.FindParam('ret')) then
      begin
        result:= QryTDB.Parameters.ParambyName('ret').Value;
      end;
    except
      on e: exception do
      begin
        ServerForm.ADOPool.FindAndFreeCon(QryTDB.Connection);
        ServerForm.LogMessage(pchar(#9' Exec sql '''+sSql+''':'+e.Message)+' result:'+IntToStr(Result));
      end;
    end;
  finally
    ServerForm.ADOPool.PutCon(QryTDB.Connection);
    QryTDB.Free;
    CoUninitialize;
    ServerForm.LogMessage(#9' End Exec SQL at '+DateTimeToStr(Now)+' result:'+IntToStr(Result));
    ServerForm.LogMessage(#9'----------------------------------------------------');
  end;
end;

function TNS_Ver2Ex.OpenClientSQL(const sSql: AnsiString; const ParamsServer: TParamArray; const ParamCheck: Boolean; const Des1: AnsiString; out Des2: AnsiString): Integer;
var
  QryTDB: TADOQuery;
  QryConn: TADOConnection;
  i:Integer;
begin
  result := 0;
  ServerForm.LogMessage(#9'=====================================================');
  ServerForm.LogMessage(#9' Start Exec SQL:'+sSql+' at '+DateTimeToStr(Now));
  try
    CoInitialize(nil);
    try
      QryTDB := TADOQuery.Create(nil);
      QryTDB.Close;
      QryTDB.SQL.Clear;
      QryConn:=ServerForm.ADOPool.GetCon(ServerForm.ADOConfig);
      QryTDB.Connection := QryConn;
      QryTDB.SQL.Text := sSql;
      QryTDB.CommandTimeout:=ServerForm.ADOConfig.ConnectionTimeout;
      QryTDB.LockType:=ltReadOnly;
      QryTDB.Prepared:=False;
//      QryTDB.Parameters.Clear;
//      for i:=0 to ParamsServer.count - 1 do
//      begin
//        ServerForm.LogMessage(#9+ParamsServer.Items[i].ParamName+':'+ParamsServer.Items[i].ParamValue);
//        QryTDB.Parameters.AddParameter;
//        QryTDB.Parameters[i].Name:=ParamsServer.Items[i].ParamName;
//        if ((ParamsServer.Items[i].ParamType = 'ANSISTRING') or (ParamsServer.Items[i].ParamType = 'PCHAR') or (ParamsServer.Items[i].ParamType = 'STRING')) then
//            QryTDB.Parameters[i].DataType:=ftString
//        else if ParamsServer.Items[i].ParamType = 'BIGINT' then
//        begin
//           QryTDB.Parameters[i].DataType:=ftLargeint;
//           QryTDB.Parameters[i].Value:=StrToInt64(ParamsServer.Items[i].ParamValue);
//           Continue;
//        end
//        else if (ParamsServer.Items[i].ParamType = 'INTEGER') or (ParamsServer.Items[i].ParamType = 'INT') then
//        begin
//           QryTDB.Parameters[i].DataType:=ftInteger;
//           QryTDB.Parameters[i].Value:=StrToInt(ParamsServer.Items[i].ParamValue);
//           Continue;
//        end
//        else if (ParamsServer.Items[i].ParamType = 'REAL') or (ParamsServer.Items[i].ParamType = 'FLOAT') then
//        begin
//           QryTDB.Parameters[i].DataType:=ftFloat;
//           QryTDB.Parameters[i].Value:=StrToFloat(ParamsServer.Items[i].ParamValue);
//           Continue;
//        end
//        else if (ParamsServer.Items[i].ParamType = 'TDATETIME') or (ParamsServer.Items[i].ParamType = 'DATETIME') then
//        begin
//           QryTDB.Parameters[i].DataType:=ftDateTime;
//           QryTDB.Parameters[i].Value:=ServerForm._StrtoDatetime(ParamsServer.Items[i].ParamValue);
//           Continue;
//        end
//        else QryTDB.Parameters[i].DataType:=ftUnknown;
//        QryTDB.Parameters[i].Value:=ParamsServer.Items[i].ParamValue;
//      end;
      for i:=0 to ParamsServer.count - 1 do
      begin
        if Assigned(QryTDB.Parameters.FindParam(ParamsServer.Items[i].ParamName)) then
          if ((ParamsServer.Items[i].ParamType = 'ANSISTRING') or (ParamsServer.Items[i].ParamType = 'PCHAR') or (ParamsServer.Items[i].ParamType = 'STRING')) then
          begin
              QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftString;
              QryTDB.Parameters.parambyname(ParamsServer.Items[i].ParamName).value := ParamsServer.Items[i].ParamValue;
          end
          else if ParamsServer.Items[i].ParamType = 'BIGINT' then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftLargeint;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=StrToInt64(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else if (ParamsServer.Items[i].ParamType = 'INTEGER') or (ParamsServer.Items[i].ParamType = 'INT') then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftInteger;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=StrToInt(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else if (ParamsServer.Items[i].ParamType = 'REAL') or (ParamsServer.Items[i].ParamType = 'FLOAT') then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftFloat;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=StrToFloat(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else if (ParamsServer.Items[i].ParamType = 'TDATETIME') or (ParamsServer.Items[i].ParamType = 'DATETIME') then
          begin
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).DataType:=ftDateTime;
             QryTDB.Parameters.ParamByName(ParamsServer.Items[i].ParamName).Value:=ServerForm._StrtoDatetime(ParamsServer.Items[i].ParamValue);
             Continue;
          end
          else
          begin
             QryTDB.Parameters[i].DataType:=ftUnknown;
             QryTDB.Parameters.parambyname(ParamsServer.Items[i].ParamName).value := ParamsServer.Items[i].ParamValue;
          end;
      end;
      QryTDB.ParamCheck:=False;
      QryTDB.Prepared:=True;
      QryTDB.Open;
      result := 1;
      if Assigned(QryTDB.FindField('ret')) then
      begin
        result:= QryTDB.FieldByName('ret').AsInteger;
      end;
    except
      on e: exception do
      begin
        ServerForm.ADOPool.FindAndFreeCon(QryTDB.Connection);
        ServerForm.LogMessage(pchar(#9' Exec sql '''+sSql+''':'+e.Message));
      end;
    end;
  finally
    ServerForm.ADOPool.PutCon(QryTDB.Connection);
    QryTDB.Free;
    CoUninitialize;
    ServerForm.LogMessage(#9' End Exec SQL at '+DateTimeToStr(Now)+' result:'+IntToStr(Result));
    ServerForm.LogMessage(#9'----------------------------------------------------');
  end;
end;

依据不同参数类型来修改传入参数的值类型(前面传入进来的时候统一用了字符串)。


另外一个,对于参数是否重新创建的问题。TADOQuery自身会替用户创建参数(依据语句中的“:XXX”)。如果有必要可自行删除再创建。

QryTDB.Parameters.Clear;//删除所有参数

QryTDB.Parameters.AddParameter;//追加新增一个参数

新建的参数需要注意设置参数的参数名称、类型、以及参数值


对于TADOQuery需要注意几个参数的设置:

1、在使用前ADOQuery.Close关闭连接

2、ADOQuery.SQL.Clear清空查询语句(在使用SQL.Add时尤其注意),防止以往查询语句干扰

3、ADOQuery.SQL.Text:=XXX 表示直接赋值查询语句,可不进行ADOQuery.SQL.Clear  但是如果用ADOQuery.SQL.Add则是以追加形式插入

4、ADOQuery.Connection设置的是数据库的连接即TADOConnection

5、ADOQuery.CommandTimeout 设置的是连接超时,即如果执行语句执行超过这个时长ADO主动断开连接,防止引起数据库死锁等情况。默认值为30s 如果像不受限制则设置为0s即可,最大值为30s超过30s,ADO默认为不断开连接。

6、ADOQuery.Prepared 设置前设置为False,打开(Open、ExecSQL)前设置为True有助于提示查询性能

7、ADOQuery.LockType 有几个选项:ltUnspecified, ltReadOnly(执行时数据库对应的数据其他访问者可以读,存在读脏数据的可能性), ltPessimistic(悲观锁即记录被编辑时其他用户不能用,数据安全,但是高并发时可能引起访问问题), ltOptimistic(能有效防止重复修改,防止同步修改,即如果A、B同时查询了某个表数据,A提交修改后B则需要重新查询出来才能进行保存有效防止了A所修改的内容在B提交后数据库没有了),  ltBatchOptimistic(批量的ltOptimistic在使用批量提交时需要设置为这个,UpdateBatch(arAll),ApplyUpdate,能将单张表中的数据进行一次性提交)

参考他人介绍如下:

ltUnspecified 未指定锁定级别
ltReadOnly Read-only    只读
ltPessimistic 记录级别(该记录被编辑时其它用户不能用)
ltOptimistic 独立模式(与原来结果集对照,如果在此期间其它用户修改了结果,则你的结果不能保存)
ltBatchOptimistic      批量模式(使用缓存,进行批量提交)

ltUnspecified          不特別指定
ltReadOnly          选出來的资料表只能读,无法写入
ltPessimistic         选出来的资料表可以写入,当改记录写入时会立刻写入并锁定
ltOptimistic          选出来的资料表可写入,当该记录表写入时不会立刻写入但会锁定,当执行updates时才正式写入改记录

ltBatchOptimistic          选出来的资料表可写入,当该记录写入时不会立刻写入但会锁定,当执行updates才正式整批写入该记录(可以修改多条整批update)}

8、ADOQuery.Post 默认情况下是将数据保存到内存,如果想直接保存到数据库是需要其他属性来配合的,不建议这么操作。
9、 ADOQuery.Insert ADOQuery.Edit ADOQuery.Append 等比较常用也好理解,不做解释。

      插一句不相关的TADOConnection 需要注意属性ADOConn.LoginPrompt:=False的设置,否则会检查连接,容易跳出对话框。

三、客户端的使用

在客户端调用ExecSQL和OpenSQL的场景不同,拼凑的SQL语句也会有点差异。

大概有几种写语句的方式:

1、

declare @ret int Exec @ret = do_U_MyProc :MyParam1,:MyParam2 

2、

Exec :ret = do_U_MyProc :MyParam1,:MyParam2
以上都需要灵活使用不限制一成不变。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值