欢迎来到千学网!
您现在的位置:首页 > 实用文 > 其他范文

编写安全的SQL Server扩展存储过程数据库教程

时间:2022-08-12 08:21:15 其他范文 收藏本文 下载本文

这次小编给大家整理了编写安全的SQL Server扩展存储过程数据库教程,本文共10篇,供大家阅读参考,也相信能帮助到您。

编写安全的SQL Server扩展存储过程数据库教程

篇1:编写安全的SQL Server扩展存储过程数据库教程

server|安全|存储过程

SQL Server 的扩展存储过程,其实就是一个普通的 Windows DLL,只不过按照某种规则实现了某些函数而已,

编写安全的SQL Server扩展存储过程数据库教程

近日在写一个扩展存储过程时,发现再写这类动态库时,还是有一些需要特别注意的地方。之所以会特别注意,是因为DLL运行于SQL Server的地址空间,而SQL Server到底是怎么进行线程调度的,却不是我们能了解的,即便了解也无法控制。

我们写动态库一般是自己用,即便给别人用,也很少像SQL Server这样,一个动态库很有可能加载多次,并且都是加载到一个进程的地址空间中。我们知道,当一个动态库加载到进程的地址空间时,DLL所有全局与局部变量初始化且仅初始化一次,以后再次调用 LoadLibrary函数时,仅仅增加其引用计数而已,那么很显然,假如有一全局 int ,初始化为0,调用一个函数另其自加,此时其值为1,然后再调用LoadLibray,并利用返回的句柄调用输出函数输出该值,虽然调用者觉得自己加载后立即输出,然后该值确实1而不是0。windows是进程独立的,而在线程方面,假如不注意,上面的情况很可能会程序员带来麻烦。

介绍一下我的扩展存储过程,该动态库导出了三个函数: Init,work,Final,Init读文件,存储信息于内存,work简单的只是向该内存检索信息,Final回收内存。如上所说,假如不考虑同一进程空间多次加载问题,两次调用Init将造成无谓的浪费,因为我第一次已经读进了内存,要是通过堆分配内存,还会造成内存泄露。

我使用的引用计数解决的该问题,代码很短,直接贴上来:

#include “stdafx.h”

#include

using namespace std;

extern “C” {

RETCODE __declspec(dllexport) xp_part_init(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_process(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_finalize(SRV_PROC *srvproc);

}

#define XP_NOERROR 0

#define XP_ERROR 1

HINSTANCE hInst = NULL;

int nRef = 0;

void printError (SRV_PROC *pSrvProc, CHAR* szErrorMsg);

ULONG __GetXpVersion{ return ODS_VERSION;}

SRVRETCODE xp_part_init(SRV_PROC* pSrvProc){

typedef bool (*Func)();

if(nRef == 0){

hInst = ::LoadLibrary(“part.dll”);

if(hInst == NULL){

printError(pSrvProc,“不能加载part.dll”);

return XP_ERROR;

}

Func theFunc = (Func)::GetProcAddress(hInst,“Init”);

if(!theFunc()){

::FreeLibrary(hInst);

printError(pSrvProc,“不能获得分类号与专辑的对应表”);

return XP_ERROR;

}

}

++ nRef;

return (XP_NOERROR);

}

SRVRETCODE xp_part_process(SRV_PROC* pSrvProc){

typedef bool (*Func)(char*);

if(nRef == 0){

printError(pSrvProc,“函数尚未初始化,请首先调用xp_part_init”);

return XP_ERROR;

}

Func theFunc = (Func)::GetProcAddress(hInst,“Get”);

BYTE bType;

ULONG cbMaxLen,cbActualLen;

BOOL fNull;

char szInput[256] = {0};

if (srv_paraminfo(pSrvProc, 1, &bType, (ULONG*)&cbMaxLen, (ULONG*)&cbActualLen, (BYTE*)szInput, &fNull) == FAIL){

printError(pSrvProc,“srv_paraminfo 返回 FAIL”);

return XP_ERROR;

}

szInput[cbActualLen] = 0;

string strInput = szInput;

string strOutput = “;”;

int cur,old = 0;

while(string::npos != (cur = strInput.find(’;’,old)) ){

strncpy(szInput,strInput.c_str() + old,cur - old);

szInput[cur - old] = 0;

ld = cur + 1;

theFunc(szInput);

if(string::npos ==strOutput.find((string)“;” + szInput))

strOutput += szInput;

}

strcpy(szInput,strOutput.c_str());

if (FAIL == srv_paramsetoutput(pSrvProc, 1, (BYTE*)(szInput + 1), strlen(szInput) - 1,FALSE)){

printError (pSrvProc, “srv_paramsetoutput 调用失败”);

return XP_ERROR;

}

srv_senddone(pSrvProc, (SRV_DONE_COUNT | SRV_DONE_MORE), 0, 0);

return XP_NOERROR;

}

SRVRETCODE xp_part_finalize(SRV_PROC* pSrvProc){

typedef void (*Func)();

if(nRef == 0)

return XP_NOERROR;

Func theFunc = (Func)::GetProcAddress(hInst,“Fin”);

if((--nRef) == 0){

theFunc();

::FreeLibrary(hInst);

hInst = NULL;

}

return (XP_NOERROR);

}

我想虽然看上去不是很高明,然而问题应该是解决了的,

还有一点说明,为什么不使用Tls,老实说,我考虑过使用的,因为其实代码是有一点问题的,假如一个用户调用xp_part_init,然后另一个用户也调用xp_part_init,注意我们的存储过程可是服务器端的,然后第一个用户调用xp_part_finalize,那么会怎样,他仍然可以正常使用xp_part_process,这倒无所谓,然而第一个用户调用两次xp_part_finalize,就能够影响第二个用户了,他的xp_part_process将返回错误。

使用Tls 似乎可以解决这问题,例如再添加一个tls_index变量,调用 TlsSetValue保存用户私人数据,TlsGetValue检索私人数据,当xp_part_init时,假如该私人数据为0,执行正常的初始化过程,(即上面的xp_part_init)执行成功后存储私人数据为1,假如是1,直接返回,xp_part_finalize时,假如私人数据为1,则执行正常的xp_part_finalize,然后设私人数据为0,假如是0,直接返回。

好像想法还是不错的,这样隔离了多个用户,安全性似乎提高了不少,然而事实是不可行的。因为Tls保存的并不是私人数据,而是线程本地变量,我们不能保证一个用户的多次操作都是用同一个线程执行的,这个由SQL Server自己控制,事实上我在查询分析器里多次执行的结果显示,SQL Server内部似乎使用了一个线程池。既然如此,那这种想法也只能作罢。

篇2:通用存储过程的编写数据库教程

存储过程

通用存储过程的编写

对数据库的操作基本上就四种:Insert、Update、Delete和Select,而Update和Insert两种操作又可以作简单的合并,这样下来,基本上一个数据表对应三个存储过程便可以完成绝大多数的数据库操作,存储过程命名规则:Operate_TableName。比如表Order_Info对应三个存储过程:AddEdit_Order_Info、Delete_Order_Info、Search_Order_Info,下面先列出相关代码,然后作总体分析。

一、AddEdit_Order_Info

/*************************************************************

** Name     :   AddEdit_Order_Info

** Creater       :   PPCoder Designed By PPCode Studio(PPTech.Net)

** Create Date   :   -9-6 8:30:17

** Modifer       :  Rexsp

** Modify Date   :   2004-9-6 8:30:17

** Description : AddEdit information for Order_Info

**************************************************************/

ALTER PROCEDURE dbo.AddEdit_Order_Info

(

@OrderStateID Int = -1,

@OrderStateID_Min Int = -1,

@OrderStateID_Max Int = -1,

@OrderUserID Int = -1,

@OrderUserID_Min Int = -1,

@OrderUserID_Max Int = -1,

@OrderID Int = -1,

@OrderID_Min Int = -1,

@OrderID_Max Int = -1,

@ProductID Int = -1,

@ProductID_Min Int = -1,

@ProductID_Max Int = -1,

@CustomizeID Int = -1,

@CustomizeID_Min Int = -1,

@CustomizeID_Max Int = -1,

@OutID INT = 0 OUTPUT

)

AS

IF @OrderID=-1

BEGIN

INSERT INTO [Order_Info] (

[OrderStateID],

[OrderUserID],

[ProductID],

[CustomizeID]

)

VALUES(

@OrderStateID,

@OrderUserID,

@ProductID,

@CustomizeID

)

Set @OutID = @@IDENTITY

END

ELSE

BEGIN

DECLARE @strSQL NVARCHAR(1000)

SET @strSQL = 'UPDATE [Order_Info] SET @tmpOrderID = @tmpOrderID'

IF @OrderStateID -1

BEGIN

SET @strSQL = @strSQL + ', [OrderStateID] = @tmpOrderStateID'

END

IF @OrderUserID -1

BEGIN

SET @strSQL = @strSQL + ', [OrderUserID] = @tmpOrderUserID'

END

IF @ProductID -1

BEGIN

SET @strSQL = @strSQL + ', [ProductID] = @tmpProductID'

END

IF @CustomizeID -1

BEGIN

SET @strSQL = @strSQL + ', [CustomizeID] = @tmpCustomizeID'

END

SET @strSQL = @strSQL + ' WHERE [OrderID] = @tmpOrderID'

BEGIN TRAN

EXECUTE sp_executesql @strSQL, N'

@tmpOrderStateID INT,

@tmpOrderUserID INT,

@tmpOrderID INT,

@tmpProductID INT,

@tmpCustomizeID INT',

@tmpOrderStateID=@OrderStateID,

@tmpOrderUserID=@OrderUserID,

@tmpOrderID=@OrderID,

@tmpProductID=@ProductID,

@tmpCustomizeID=@CustomizeID

Set @OutID = @OrderID

IF @@error!=0

BEGIN

ROLLBACK

END

ELSE

BEGIN

COMMIT

END

END

RETURN

二、Delete_Order_Info

/*************************************************************

** Name     :   Delete_Order_Info

** Creater       :   PPCoder Designed By PPCode Studio(PPTech.Net)

** Create Date   :   2004-9-6 8:30:17

** Modifer       :  Rexsp

** Modify Date   :   2004-9-6 8:30:17

** Description : Delete information for Order_Info

**************************************************************/

ALTER PROCEDURE dbo.Delete_Order_Info

(

@OrderStateID Int = -1,

@OrderStateID_Min Int = -1,

@OrderStateID_Max Int = -1,

@OrderUserID Int = -1,

@OrderUserID_Min Int = -1,

@OrderUserID_Max Int = -1,

@OrderID Int = -1,

@OrderID_Min Int = -1,

@OrderID_Max Int = -1,

@ProductID Int = -1,

@ProductID_Min Int = -1,

@ProductID_Max Int = -1,

@CustomizeID Int = -1,

@CustomizeID_Min Int = -1,

@CustomizeID_Max Int = -1,

@OutID INT = 0 OUTPUT

)

AS

DECLARE @strSQL NVARCHAR(1000)

SET @strSQL = 'DELETE FROM [Order_Info] WHERE @tmpOrderID = @tmpOrderID '

IF @OrderStateID-1

BEGIN

SET @strSQL = @strSQL + ' AND rderStateID = @tmpOrderStateID'

END

IF @OrderStateID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderStateID_Min = @tmpOrderStateID_Min'

END

IF @OrderStateID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderStateID_Max = @tmpOrderStateID_Max'

END

IF @OrderUserID-1

BEGIN

SET @strSQL = @strSQL + ' AND rderUserID = @tmpOrderUserID'

END

IF @OrderUserID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderUserID_Min = @tmpOrderUserID_Min'

END

IF @OrderUserID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderUserID_Max = @tmpOrderUserID_Max'

END

IF @OrderID-1

BEGIN

SET @strSQL = @strSQL + ' AND rderID = @tmpOrderID'

END

IF @OrderID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderID_Min = @tmpOrderID_Min'

END

IF @OrderID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderID_Max = @tmpOrderID_Max'

END

IF @ProductID-1

BEGIN

SET @strSQL = @strSQL + ' AND ProductID = @tmpProductID'

END

IF @ProductID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND ProductID_Min = @tmpProductID_Min'

END

IF @ProductID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND ProductID_Max = @tmpProductID_Max'

END

IF @CustomizeID-1

BEGIN

SET @strSQL = @strSQL + ' AND CustomizeID = @tmpCustomizeID'

END

IF @CustomizeID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND CustomizeID_Min = @tmpCustomizeID_Min'

END

IF @CustomizeID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND CustomizeID_Max = @tmpCustomizeID_Max'

END

BEGIN TRAN

EXECUTE sp_executesql @strSQL, N'

@tmpOrderStateID INT,

@tmpOrderUserID INT,

@tmpOrderID INT,

@tmpProductID INT,

@tmpCustomizeID INT',

@tmpOrderStateID=@OrderStateID,

@tmpOrderUserID=@OrderUserID,

@tmpOrderID=@OrderID,

@tmpProductID=@ProductID,

@tmpCustomizeID=@CustomizeID

Set @OutID = @OrderID

IF @@error!=0

BEGIN

ROLLBACK

END

ELSE

BEGIN

COMMIT

END

RETURN

三、 Search_Order_Info

/*************************************************************

** Name     :   Search_Order_Info

** Creater       :   PPCoder Designed By PPCode Studio(PPTech.Net)

** Create Date   :   2004-9-6 8:30:17

** Modifer       :  Rexsp

** Modify Date   :   2004-9-6 8:30:17

** Description : Search information for Order_Info

**************************************************************/

ALTER PROCEDURE dbo.Search_Order_Info

(

@OrderStateID Int = -1,

@OrderStateID_Min Int = -1,

@OrderStateID_Max Int = -1,

@OrderUserID Int = -1,

@OrderUserID_Min Int = -1,

@OrderUserID_Max Int = -1,

@OrderID Int = -1,

@OrderID_Min Int = -1,

@OrderID_Max Int = -1,

@ProductID Int = -1,

@ProductID_Min Int = -1,

@ProductID_Max Int = -1,

@CustomizeID Int = -1,

@CustomizeID_Min Int = -1,

@CustomizeID_Max Int = -1,

@ReturnCount INT=-1,

@OutID INT = 0 OUTPUT

)

AS

DECLARE @strSQL NVARCHAR(1000)

IF @ReturnCount-1

BEGIN

SET @strSQL = 'SELECT TOP '+@ReturnCount+' * FROM [Order_Info] WHERE @tmpOrderID = @tmpOrderID '

END

ELSE

BEGIN

SET @strSQL = 'SELECT * FROM [Order_Info] WHERE @tmpOrderID = @tmpOrderID '

END

IF @OrderStateID-1

BEGIN

SET @strSQL = @strSQL + ' AND rderStateID = @tmpOrderStateID'

END

IF @OrderStateID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderStateID_Min = @tmpOrderStateID_Min'

END

IF @OrderStateID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderStateID_Max = @tmpOrderStateID_Max'

END

IF @OrderUserID-1

BEGIN

SET @strSQL = @strSQL + ' AND rderUserID = @tmpOrderUserID'

END

IF @OrderUserID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderUserID_Min = @tmpOrderUserID_Min'

END

IF @OrderUserID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderUserID_Max = @tmpOrderUserID_Max'

END

IF @OrderID-1

BEGIN

SET @strSQL = @strSQL + ' AND rderID = @tmpOrderID'

END

IF @OrderID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderID_Min = @tmpOrderID_Min'

END

IF @OrderID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND OrderID_Max = @tmpOrderID_Max'

END

IF @ProductID-1

BEGIN

SET @strSQL = @strSQL + ' AND ProductID = @tmpProductID'

END

IF @ProductID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND ProductID_Min = @tmpProductID_Min'

END

IF @ProductID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND ProductID_Max = @tmpProductID_Max'

END

IF @CustomizeID-1

BEGIN

SET @strSQL = @strSQL + ' AND CustomizeID = @tmpCustomizeID'

END

IF @CustomizeID_Min-1

BEGIN

SET @strSQL = @strSQL + ' AND CustomizeID_Min = @tmpCustomizeID_Min'

END

IF @CustomizeID_Max-1

BEGIN

SET @strSQL = @strSQL + ' AND CustomizeID_Max = @tmpCustomizeID_Max'

END

BEGIN TRAN

EXECUTE sp_executesql @strSQL, N'

@tmpOrderStateID INT,

@tmpOrderUserID INT,

@tmpOrderID INT,

@tmpProductID INT,

@tmpCustomizeID INT',

@tmpOrderStateID=@OrderStateID,

@tmpOrderUserID=@OrderUserID,

@tmpOrderID=@OrderID,

@tmpProductID=@ProductID,

@tmpCustomizeID=@CustomizeID

Set @OutID = @OrderID

IF @@error!=0

BEGIN

ROLLBACK

END

ELSE

BEGIN

COMMIT

END

分析:

1、             三个存储过程的入参基本上相同,只有Search_Order_Info多了一个@ReturnCount用来控制搜索信息的条数的,

入参很有特点:与数据表字段的扩展对应。扩展方式有三种:数字型和日期型扩展出“极小”和“极大”两个属性,例如数字型的OrderStateID对应的参数有三个@OrderStateID、@OrderStateID_Min 、@OrderStateID_Max ,时间型的AddTime对应@AddTime、@AddTime_Rof、@AddTime_Eof ;如果是字符型的,则会扩展出一个用来进行模糊搜索的属性,例如Title对应@Title、@Title_Like。之所以这样设计,是为了组合出更具适应性的条件语句。三个存储过程都有一个出参,就是表的唯一标识ID。这个主要在“添加和更新”操作中使用。当然搜索的时候也可以当唯一键返回。这个唯一标识ID也是来判断是Insert或Update的标识。

2、             入参都有赋初值,然后动态构建Sql语句的时候,会判断各入参是否等于初值,如果不等于表示是外面传进来的传,便参与Sql语句的构建。这种灵活性是程序适应性的保证。这样,我们就可以在程序员通过控制是否给入参传值来判断是否要进行某一栏位进行更新或是否要把某一栏位的信息参与条件语句的构成。

3、             用系统存储过程sp_executesql来执行Sql语句,完全数据库操作。用系统存储过程来执行sql语句有一个好处,就是可以实现特殊字符的自动转义。

4、             三个存储过程都有统一的构建规律,所以可以使用自动化工具依据表结构直接生成。

篇3:编写安全的扩展存储过程存储过程

近日在写一个扩展存储过程时,发现再写这类动态库时,还是有一些需要特别注意的地方,之所以会特别注意,是因为DLL运行于SQL Server的地址空间,而SQL Server到底是怎么进行线程调度的,却不是我们能了解的,即便了解也无法控制。

我们写动态库一般是自己用,即便给别人用,也很少像SQL Server这样,一个动态库很有可能加载多次,并且都是加载到一个进程的地址空间中。我们知道,当一个动态库加载到进程的地址空间时,DLL所有全局与局部变量初始化且仅初始化一次,以后再次调用 LoadLibrary函数时,仅仅增加其引用计数而已,那么很显然,假如有一全局 int,初始化为0,调用一个函数另其自加,此时其值为1,然后再调用LoadLibray,并利用返回的句柄调用输出函数输出该值,虽然调用者觉得自己加载后立即输出,然后该值确实1而不是0。windows是进程独立的,而在线程方面,假如不注意,上面的情况很可能会程序员带来麻烦。

介绍一下我的扩展存储过程,该动态库导出了三个函数:Init,work,Final,Init读文件,存储信息于内存,work简单的只是向该内存检索信息,Final回收内存。如上所说,假如不考虑同一进程空间多次加载问题,两次调用Init将造成无谓的浪费,因为我第一次已经读进了内存,要是通过堆分配内存,还会造成内存泄露。

我使用的引用计数解决的该问题,代码很短,直接贴上来:

#include “stdafx.h”

#include

using namespace std;

extern “C” {

RETCODE __declspec(dllexport) xp_part_init(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_process(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_finalize(SRV_PROC *srvproc);

}

#define XP_NOERROR 0

#define XP_ERROR 1

HINSTANCE hInst = NULL;

int nRef = 0;

void printError (SRV_PROC *pSrvProc, CHAR* szErrorMsg);

ULONG __GetXpVersion{ return ODS_VERSION;}

SRVRETCODE xp_part_init(SRV_PROC* pSrvProc){

typedef bool (*Func)();

if(nRef == 0){

hInst = ::LoadLibrary(“part.dll”);

if(hInst == NULL){

printError(pSrvProc,“不能加载part.dll”);

return XP_ERROR;

}

Func theFunc = (Func)::GetProcAddress(hInst,“Init”);

if(!theFunc()){

::FreeLibrary(hInst);

printError(pSrvProc,“不能获得分类号与专辑的对应表”);

return XP_ERROR;

}

}

++ nRef;

return (XP_NOERROR);

}

SRVRETCODE xp_part_process(SRV_PROC* pSrvProc){

typedef bool (*Func)(char*);

if(nRef == 0){

printError(pSrvProc,“函数尚未初始化,请首先调用xp_part_init”);

return XP_ERROR;

}

Func theFunc = (Func)::GetProcAddress(hInst,“Get”);

BYTE bType;

关 键 字:MYSQL

篇4:编写安全的扩展存储过程

近日在写一个扩展存储过程时,发现再写这类动态库时,还是有一些需要特别注意的地方,之所以会特别注意,是因为DLL运行于SQL Server的地址空间,而SQL Server到底是怎么进行线程调度的,却不是我们能了解的,即便了解也无法控制。

我们写动态库一般是自己用,即便给别人用,也很少像SQL Server这样,一个动态库很有可能加载多次,并且都是加载到一个进程的地址空间中。我们知道,当一个动态库加载到进程的地址空间时,DLL所有全局与局部变量初始化且仅初始化一次,以后再次调用 LoadLibrary函数时,仅仅增加其引用计数而已,那么很显然,假如有一全局 int,初始化为0,调用一个函数另其自加,此时其值为1,然后再调用LoadLibray,并利用返回的句柄调用输出函数输出该值,虽然调用者觉得自己加载后立即输出,然后该值确实1而不是0。windows是进程独立的,而在线程方面,假如不注意,上面的情况很可能会程序员带来麻烦。

介绍一下我的扩展存储过程,该动态库导出了三个函数:Init,work,Final,Init读文件,存储信息于内存,work简单的只是向该内存检索信息,Final回收内存。如上所说,假如不考虑同一进程空间多次加载问题,两次调用Init将造成无谓的浪费,因为我第一次已经读进了内存,要是通过堆分配内存,还会造成内存泄露。

我使用的引用计数解决的该问题,代码很短,直接贴上来:

#include “stdafx.h”

#include

using namespace std;

extern “C” {

RETCODE __declspec(dllexport) xp_part_init(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_process(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_finalize(SRV_PROC *srvproc);

}

#define XP_NOERROR 0

#define XP_ERROR 1

HINSTANCE hInst = NULL;

int nRef = 0;

void printError (SRV_PROC *pSrvProc, CHAR* szErrorMsg);

ULONG __GetXpVersion(){ return ODS_VERSION;}

SRVRETCODE xp_part_init(SRV_PROC* pSrvProc){

typedef bool (*Func)();

if(nRef == 0){

hInst = ::LoadLibrary(“part.dll”);

if(hInst == NULL){

printError(pSrvProc,“不能加载part.dll”);

return XP_ERROR;

}

Func theFunc = (Func)::GetProcAddress(hInst,“Init”);

if(!theFunc()){

::FreeLibrary(hInst);

printError(pSrvProc,“不能获得分类号与专辑的对应表”);

return XP_ERROR;

}

}

++ nRef;

return (XP_NOERROR);

}

SRVRETCODE xp_part_process(SRV_PROC* pSrvProc){

typedef bool (*Func)(char*);

if(nRef == 0){

printError(pSrvProc,“函数尚未初始化,请首先调用xp_part_init”);

return XP_ERROR;

}

Func theFunc = (Func)::GetProcAddress(hInst,“Get”);

BYTE bType;

ULONG cbMaxLen,cbActualLen;

BOOL fNull;

char szInput[256] = {0};

if (srv_paraminfo(pSrvProc, 1, &bType, (ULONG*)&cbMaxLen, (ULONG*)&cbActualLen,

(BYTE*)szInput, &fNull) == FAIL){

printError(pSrvProc,“srv_paraminfo 返回 FAIL”);

return XP_ERROR;

}

szInput[cbActualLen] = 0;

string strInput = szInput;

string strOutput = “;”;

int cur,old = 0;

while(string::npos != (cur = strInput.find(’;’,old)) ){

strncpy(szInput,strInput.c_str() + old,cur - old);

szInput[cur - old] = 0;

old = cur + 1;

theFunc(szInput);

if(string::npos ==strOutput.find((string)“;” + szInput))

strOutput += szInput;

}

strcpy(szInput,strOutput.c_str());

if (FAIL == srv_paramsetoutput(pSrvProc, 1, (BYTE*)(szInput + 1),

strlen(szInput) - 1,FALSE)){

printError (pSrvProc, “srv_paramsetoutput 调用失败”);

return XP_ERROR;

}

srv_senddone(pSrvProc, (SRV_DONE_COUNT | SRV_DONE_MORE), 0, 0);

return XP_NOERROR;

}

SRVRETCODE xp_part_finalize(SRV_PROC* pSrvProc){

typedef void (*Func)();

if(nRef == 0)

return XP_NOERROR;

Func theFunc = (Func)::GetProcAddress(hInst,“Fin”);

if((--nRef) == 0){

theFunc();

::FreeLibrary(hInst);

hInst = NULL;

}

return (XP_NOERROR);

}

我想虽然看上去不是很高明,然而问题应该是解决了的,

还有一点说明,为什么不使用Tls,老实说,我考虑过使用的,因为其实代码是有一点问题的,假如一个用户调用xp_part_init,然后另一个用户也调用xp_part_init,注意我们的存储过程可是服务器端的,然后第一个用户调用xp_part_finalize,那么会怎样,他仍然可以正常使用xp_part_process,这倒无所谓,然而第一个用户调用两次xp_part_finalize,就能够影响第二个用户了,他的xp_part_process将返回错误。

使用Tls 似乎可以解决这问题,例如再添加一个tls_index变量,调用 TlsSetValue保存用户私人数据,TlsGetValue检索私人数据,当xp_part_init时,假如该私人数据为0,执行正常的初始化过程,(即上面的xp_part_init)执行成功后存储私人数据为1,假如是1,直接返回,xp_part_finalize时,假如私人数据为1,则执行正常的xp_part_finalize,然后设私人数据为0,假如是0,直接返回。

好像想法还是不错的,这样隔离了多个用户,安全性似乎提高了不少,然而事实是不可行的。因为Tls保存的并不是私人数据,而是线程本地变量,我们不能保证一个用户的多次操作都是用同一个线程执行的,这个由SQL Server自己控制,事实上我在查询分析器里多次执行的结果显示,SQL Server内部似乎使用了一个线程池。既然如此,那这种想法也只能作罢。

篇5:管理存储过程数据库教程

12.3.1 查看存储过程

存储过程被创建以后,它的名字存储在系统表sysobjects 中;它的源代码存放在系统表syscomments 中,可以通过MS SQL Server 提供的系统存储过程来查看关于用户创建的存储过程信息。

1通过Enterprise Manager 管理工具同样可以查看存储过程的源代码

其操作如下:

(1) 启动Enterprise Manager, 登录到要使用的服务器。

(2) 选择要创建存储过程的数据库,在左窗格中单击Stored Procedure 文件夹,此时在右窗格中显示该数据库的所有存储过程。

(3) 在右窗格中,右击要查看源代码的存储过程,在弹出的菜单中选择Properties选项,此时便可看到存储过程的源代码。如图12-4 所示:

2 使用sp_helptext 存储过程查看存储过程的源代码

其语法格式如下:

sp_helptext 存储过程名称

例如要查看数据库pubs 是否是存储过程reptq1 的源代码,则执行sp_helptext reptq1。

注意:如果在创建存储过程时使用了WITH ENCRYPTION选项,那么无论是使用Enterprise Manager还是系统存储过程sp_helptext都无法查看到存储过程的源代码。

12.3.2 重新命名存储过程

修改存储过程的名字使用系统存储过程sp_rename。 其命令格式为:

sp_rename 原存储过程名, 新存储过程名

例12-4 :将存储过程reptq1 修改为newproc 其语句为:

sp_rename reptq1, newproc

另外,通过Enterprise Manager 也可修改存储过程的名字,其操作过程与WINDOWS 下修改文件名字的操作类似。即首先选中需修改名字的存储过程,然后右击鼠标,在弹出菜单中选取rename 选项,最后输入新存储过程的名字。

12.3.3 删除存储过程

删除存储过程使用drop 命令,drop 命令可将一个或多个存储过程或者存储过程组从

当前数据库中删除。其语法规则为:

DROP PROCEDURE {procedure}} [,…n]

例12-5: 如将存储过程reptq1 从数据库中删除,则执行:

drop procedure reptq1

12.3.4 执行存储过程

执行已创建的存储过程使用EXECUTE 命令,其语法如下:

[EXECUTE]

{[@return_statur=]

{procedure_name[;number] | @procedure_name_var}

[[@parameter=] {value | @variable [OUTPUT] | [DEFAULT] [,…n]

[WITH RECOMPILE]

各参数的含义如下:

@return_status

是可选的整型变量,用来存储存储过程向调用者返回的值,

@procedure_name_var

是一变量名用来代表存储过程的名字。

其它参数据和保留字的含义与CREATE PROCEDURE 中介绍的一样。

例12-6: 该存储过程被用来将两个字符串连接成一个字符串,并将结果返回。创建存储过程:

12.3.5 修改存储过程

修改以前用CREATE PROCEDURE 命令创建的存储过程,并且不改变权限的授予情况以及不影响任何其它的独立的存储过程或触发器常使用ALTER PROCEDURE 命令。其语法规则是:

ALTER PROC[EDURE] procedure_name [;number]

[ {@parameter data_type } [VARYING] [= default] [OUTPUT]] [,...n]

[WITH

{RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION}]

[FOR REPLICATION]

AS

sql_statement [...n]

其中各参数和保留字的具体含义请参看CREATE PROCEDURE 命令。

下面将举一个例子使读者对该命令有更为深刻的理解。

将该段代码输入到SQL Server Query Analyzer 窗口中运行后结果为:

注意:由于在ALERT PROCEDURE中使用了WITH ENCTYPTION保留字,所以在查看修改后的存储过程源代码时看到是一些代码。

篇6:SQLSERVER Pager store procedure分页存储过程数据库教程

复制代码代码如下:

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE PROCEDURE [dbo].[Pagination]

@Page int = 1, -- 当前页码

@PageSize int = 10, -- 每页记录条数(页面大小)

@Table nvarchar(500), -- 表名或视图名,甚至可以是嵌套SQL:(Select * From Tab Where ID>1000) Tab

@Field nvarchar(800) = '*', -- 返回记录集字段名,“,”隔开,默认是“*”

@OrderBy nvarchar(100) = 'ID ASC', -- 排序规则

@Filter nvarchar(500), -- 过滤条件

@MaxPage smallint output, -- 执行结果 -1 error, 0 false, maxpage true

@TotalRow int output, -- 记录总数 /* -07-12 22:11:00 update */

@Descript. varchar(100) output -- 结果描述

AS

BEGIN

-- =============================================

-- Author: Jimmy.Yu

-- Create date: 2007-5-11

-- Description: SQL 以上版本 通用分页存储过程

-- =============================================

Set ROWCOUNT @PageSize;

Set @Descript. = 'successful';

-------------------参数检测----------------

IF LEN(RTRIM(LTRIM(@Table))) !> 0

Begin

Set @MaxPage = 0;

Set @Descript. = 'table name is empty';

Return;

End

IF LEN(RTRIM(LTRIM(@OrderBy))) !> 0

Begin

Set @MaxPage = 0;

Set @Descript. = 'order is empty';

Return;

End

IF ISNULL(@PageSize,0) <= 0

Begin

Set @MaxPage = 0;

Set @Descript. = 'page size error';

Return;

End

IF ISNULL(@Page,0) <= 0

Begin

Set @MaxPage = 0;

Set @Descript. = 'page error';

Return;

End

-------------------检测结束----------------

Begin Try

-- 整合SQL

Declare @SQL nvarchar(4000), @Portion nvarchar(4000);

Set @Portion = ' ROW_NUMBER OVER (ORDER BY ' + @OrderBy + ') AS ROWNUM FROM ' + @Table;

Set @Portion = @Portion + (CASE WHEN LEN(@Filter) >= 1 THEN (' Where ' + @Filter + ') AS tab') ELSE (') AS tab') END);

Set @SQL = 'Select TOP(' + CAST(@PageSize AS nvarchar(8)) + ') ' + @Field + ' FROM (Select ' + @Field + ',' + @Portion;

Set @SQL = @SQL + ' Where tab.ROWNUM > ' + CAST((@Page-1)*@PageSize AS nvarchar(8));

-- 执行SQL, 取当前页记录集

Execute(@SQL);

--------------------------------------------------------------------

-- 整合SQL

Set @SQL = 'Set @Rows = (Select MAX(ROWNUM) FROM (Select' + @Portion + ')';

-- 执行SQL, 取最大页码

Execute sp_executesql @SQL, N'@Rows int output', @TotalRow output;

Set @MaxPage = (CASE WHEN (@TotalRow % @PageSize)0 THEN (@TotalRow / @PageSize + 1) ELSE (@TotalRow / @PageSize) END);

End Try

Begin Catch

-- 捕捉错误

Set @MaxPage = -1;

Set @Descript. = 'error line: ' + CAST(ERROR_LINE() AS varchar(8)) + ', error number: ' + CAST(ERROR_NUMBER() AS varchar(8)) + ', error message: ' + ERROR_MESSAGE();

Return;

End Catch;

-- 执行成功

Return;

END

相对应的页面逻辑中写的对应调用该存储过程的方法(C#)

篇7:编写安全的SQL Server扩展存储过程存储过程

SQL Server 的扩展存储过程,其实就是一个普通的 Windows DLL,只不过按照某种规则实现了某些函数而已,

近日在写一个扩展存储过程时,发现再写这类动态库时,还是有一些需要特别注意的地方。之所以会特别注意,是因为DLL运行于SQL Server的地址空间,而SQL Server到底是怎么进行线程调度的,却不是我们能了解的,即便了解也无法控制。

我们写动态库一般是自己用,即便给别人用,也很少像SQL Server这样,一个动态库很有可能加载多次,并且都是加载到一个进程的地址空间中。我们知道,当一个动态库加载到进程的地址空间时,DLL所有全局与局部变量初始化且仅初始化一次,以后再次调用 LoadLibrary函数时,仅仅增加其引用计数而已,那么很显然,假如有一全局 int ,初始化为0,调用一个函数另其自加,此时其值为1,然后再调用LoadLibray,并利用返回的句柄调用输出函数输出该值,虽然调用者觉得自己加载后立即输出,然后该值确实1而不是0。windows是进程独立的,而在线程方面,假如不注意,上面的情况很可能会程序员带来麻烦。

介绍一下我的扩展存储过程,该动态库导出了三个函数: Init,work,Final,Init读文件,存储信息于内存,work简单的只是向该内存检索信息,Final回收内存。如上所说,假如不考虑同一进程空间多次加载问题,两次调用Init将造成无谓的浪费,因为我第一次已经读进了内存,要是通过堆分配内存,还会造成内存泄露。

我使用的引用计数解决的该问题,代码很短,直接贴上来:

#include “stdafx.h”

#include

using namespace std;

extern “C” {

RETCODE __declspec(dllexport) xp_part_init(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_process(SRV_PROC *srvproc);

RETCODE __declspec(dllexport) xp_part_finalize(SRV_PROC *srvproc);

}

#define XP_NOERROR 0

#define XP_ERROR 1

HINSTANCE hInst = NULL;

int nRef = 0;

void printError (SRV_PROC *pSrvProc, CHAR* szErrorMsg);

ULONG __GetXpVersion(){ return ODS_VERSION;}

SRVRETCODE xp_part_init(SRV_PROC* pSrvProc){

typedef bool (*Func)();

if(nRef == 0){

hInst = ::LoadLibrary(“part.dll”);

if(hInst == NULL){

printError(pSrvProc,“不能加载part.dll”);

return XP_ERROR;

}

Func theFunc = (Func)::GetProcAddress(hInst,“Init”);

if(!theFunc()){

::FreeLibrary(hInst);

printError(pSrvProc,“不能获得分类号与专辑的对应表”);

return XP_ERROR;

}

}

++ nRef;

return (XP_NOERROR);

}

SRVRETCODE xp_part_process(SRV_PROC* pSrvProc){

typedef bool (*Func)(char*);

if(nRef == 0){

printError(pSrvProc,“函数尚未初始化,请首先调用xp_part_init”);

return XP_ERROR;

}

Func the

关 键 字:MYSQL

篇8:教你SQLSERVER扩展存储过程XPCMDSHELL的简单应用

XP_CMDSHELL存储过程是执行本机的CMD命令,要求系统登陆有SA权限,也就是说如果获得SQLSERVER的SA命令,那就可以在目标机为所欲为了,知名软件“流光”使用的应该也是这个存储过程来实现在目标机上的操作,下面是我写的一个简单的应用页面(ASP),代码如下。

XP_CMDSHELL存储过程是执行本机的CMD命令,要求系统登陆有SA权限,也就是说如果获得SQLSERVER的SA命令,那就可以在目标机为所欲为了,知名软件“流光”使用的应该也是这个存储过程来实现在目标机上的操作。

下面是我写的一个简单的应用页面(ASP),代码如下。

CMD.ASP

<%@LANGUAGE=“VBSCRIPT” CODEPAGE=“936”%>

SQLSERVER_XP_CMDSHELL实例_魔术师·刘

<%if request(“cmd”)“” then%>

XP_CMDSHELL请求结果

<%dim connstr,conn,rs,i

ConnStr=“Provider=sqloledb.1;persist security info=false;server=”&request(“server”)&“;uid=sa;pwd=”&request(“pwd”)&“;database=master”

'ConnStr=“Provider=sqloledb.1;persist security info=false;server=(local);uid=sa;pwd=www.zhi.net;database=master”

set conn=Server.CreateObject(“ADODB.Connection”)

conn.open Connstr

set rs=server.CreateObject(“ADODB.Recordset”)

set rs=conn.execute(“xp_cmdshell '”&replace(replace(request(“cmd”),“'”,“''”),chr(34),“''”)&“'”)

i=0

while not rs.eof

if not isnull(rs(0)) then

if i mod 2 =0 then

response.Write “”&rs(0)&“”

else

response.Write “”&rs(0)&“”

end if

i=i 1

end if

rs.movenext

wend

rs.close

set rs=nothing

conn.close

set conn=nothing

%>

<%end if%>

XP_CMDSHELL实例

服务器

<%=request(“Server”)%>“>

SA密码

<%=request(”PWD“)%>”>

CMD命令

<%=request(“CMD”)%>“>

篇9:一问一答:存储过程经典问题数据库教程

存储过程|问题

只涉及到一个表:xkb_treeNode

表结构是这样:

node_id         int     //节点id

parentNode_id   int     //父节点id

node_text       varchar //节点内容

isModule        bit     //是否叶子节点

现在保存的数据有:

node_id parentNode_id node_text       isModule

1       -1         语言与文学          0

2       -1           数学              0

3       -1           技术              0

4        1           语文              0

5        1           外语              0

6        5           英语              0

7        6         初中英语            0

8        7          特斯塔             1

9        4          测定是2            1

10       2           测试3             1

现在问题是:

能否通过做一个存储过程,

根据表中的isModule字段的取值(取值为1的表示最终叶子结点),

比如“特斯塔”为叶子节点,层层向上递进找到”特斯塔“的祖先节点:

特斯塔-〉初中英语-〉英语-〉外语-〉语言与文学

即通过”特斯塔“找到”语言与文学“来

最终返回的形态为:

叶子节点id 父节点id     节点名称     祖先节点名称 祖先节点id

8          7          特斯塔       语言与文学      1

9          4          测定是2      语言与文学      1

10         2          测试3          数学          2

/////////////////////////////////////////////////////////////////////////

正确答案:

--生成测试数据

create table xkb_treeNode(

node_id       int,

parentNode_id  int,

node_textvarchar(10),

isModulebit)

insert into xkb_treeNode select 1 ,-1,'语言与文学',0

insert into xkb_treeNode select 2 ,-1,'数学',0

insert into xkb_treeNode select 3 ,-1,'技术',0

insert into xkb_treeNode select 4 , 1,'语文',0

insert into xkb_treeNode select 5 , 1,'外语',0

insert into xkb_treeNode select 6 , 5,'英语',0

insert into xkb_treeNode select 7 , 6,'初中英语',0

insert into xkb_treeNode select 8 , 7,'特斯塔'       ,1

insert into xkb_treeNode select 9 , 4,'测定是2',1

insert into xkb_treeNode select 10 , 2,'测试3',1

--创建存储过程

create procedure sp_test

as

begin

select

a.node_id,

a.parentNode_id,

a.node_text,

b.node_id  as ancestor_id ,

b.node_text as ancestor_text

into

#t

from

xkb_treeNode a,xkb_treeNode b

where

a.parentNode_id = b.node_id and a.isModule = 1

while(exists(select 1 from xkb_treeNode a,#t b where a.node_id=ancestor_id and a.parentNode_id != -1))

begin

update #t

set

ancestor_id  = b.p_id,

ancestor_text = b.p_text

from

#t a,

(select

c.node_id,

d.node_id as p_id,

d.node_text as p_text

from

xkb_treeNode c,xkb_treeNode d

where

c.parentNode_id = d.node_id) b

where

a.ancestor_id = b.node_id

end

select * from #t order by node_id

end

--执行存储过程,结果楼主自己看

exec sp_test

篇10:Delphi与SQLServer存储过程编程详解数据库

前言 经常有很多初学者问到在delphi中如何调用 SQL Server的存储过程 ?问题其实很好解决,但问得多了,也就不愿答了,下面我将用实例进行说明,从在SQL Server中创建存储过程到调用的完整实例。 首先,打开 sql server管理器,在pubs 数据库 中建一个 测试 表

前言

经常有很多初学者问到在delphi中如何调用SQLServer的存储过程?问题其实很好解决,但问得多了,也就不愿答了。下面我将用实例进行说明,从在SQL Server中创建存储过程到调用的完整实例。

首先,打开sqlserver管理器,在pubs数据库中建一个测试表,表名为test,字段有id,name,和desc,全部为字符型,如果你不知道建表,那么打开sql查询分析器,贴上以下的代码,然后按执行,就会自动生成test表.

use pubs

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[test]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)

drop table [dbo].[test]

GO

CREATE TABLE [dbo].[test] (

[id] [char] (10) COLLATE Chinese_PRC_CI_AS NOT NULL ,

[name] [char] (12) COLLATE Chinese_PRC_CI_AS NULL ,

[descrip] [char] (30) COLLATE Chinese_PRC_CI_AS NULL

) ON [PRIMARY]

GO

然后,我们来创建一个存储过程,其功能为在test中插入一条新记录.创建存储过程的代码如下,同样的,你也可以复制到查询分析器里直接执行就可以:

CREATE PROCEDURE myInsert

@id char(10) ,

@name varchar(12),

@descrip varchar(30)

AS

begin

insert into test (id,name,descrip) values (@id,@name,@descrip)

if @@rowcount=0

begin

raiserror('error',16,1)

rollback transaction

end

end

GO

接下来,新建一个工程文件,在form1上放置如下控件,并设置属性(括号内):

一个ADOConnection1: TADOConnection;

(LoginPrompt:=false;

connectionstring:=Provider=SQLOLEDB.1;Persist Security Info=True;User ID=sa;Initial Catalog=pubs;Data Source=(local);)

一个ADOStoredProc1: TADOStoredProc;属性为:

(connection:=adoconnection1;

procedurename:=myinsert;//上面我们创建的那个)

一个ADOTable1: TADOTable;属性为:

(connection:=adoconnection1;

tablename:=test; //上面我们创建的那个)

一个Datasource1,属性为:

(dataset:=Tadotable;)

一个 DBGrid1: TDBGrid;属性为

(datasource:=datasource1;)

一个Button1,在其Onclick中写到:

with adoStoredproc1 do

begin

Parameters.ParamByName('@id').Value := '2';

parameters.ParamByName('@name').Value := 'myname';

parameters.ParamByName('@descrip').Value :='nosubject';

ExecProc;

end;

Adotable1.Close;

adotable1.Open;

原文转自:www.ltesting.net

自动生成对表进行插入和更新的存储过程的存储过程数据库教程

DELPHI 调用 Oracle 存储过程并返回数据集的例子.数据库教程

读书笔记MySQL存储过程存储函数

浅谈触发器和存储过程

SMTP服务扩展服务器教程

什么是数据仓库数据库教程

自定义链接后端数据库数据库教程

Building a TSQL Loop数据库教程

存储过程同函数的区别

怎样去宣传数据仓库?数据库教程

《编写安全的SQL Server扩展存储过程数据库教程(精选10篇).doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式

点击下载本文文档