在服务器端为了防止注入,不仅需要客户端传过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 默认情况下是将数据保存到内存,如果想直接保存到数据库是需要其他属性来配合的,不建议这么操作。
插一句不相关的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
以上都需要灵活使用不限制一成不变。