国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數據庫 > SQL Server > 正文

編寫安全的SQL Server擴展存儲過程

2024-08-31 00:47:47
字體:
來源:轉載
供稿:網友
    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 <string>
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內部似乎使用了一個線程池。既然如此,那這種想法也只能作罷。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 六盘水市| 蕉岭县| 淅川县| 青田县| 东至县| 泰州市| 措勤县| 酉阳| 宜丰县| 苏州市| 乌海市| 承德县| 溧水县| 大石桥市| 巴中市| 汉沽区| 石河子市| 铜川市| 茌平县| 汉沽区| 沈丘县| 大方县| 扶余县| 雅安市| 汾阳市| 安阳县| 德钦县| 镇原县| 航空| 合水县| 姚安县| 揭阳市| 汝城县| 东方市| 佛山市| 延边| 瑞金市| 九寨沟县| 宁城县| 肇东市| 永丰县|