freeeyes 发表于 2010-8-9 21:08:44

在Linux下使用freeTDS和SQLServer服务器链接

应一位朋友的要求,把Linux下FreeTDS链接SQLServer的代码放在这里。
我一年前折腾这个东西,也是费了我不少精力。不过,我现在已经基本实现了freetds和SQLServer之间的所有关系调用。
第一,不要太相信freetds。因为这个东西是一个开源的项目,其中有一些仿照Windows的API尚未完毕,我查看过他们的部分源代码,里面的函数是空的,注释尚未完成,这个和freetds的版本相关,所以建议你调用的时候最好自己好好看看freetds源码中的函数。另外,去一下他的官网。下载最新的说明函数文档,里面写的很详细。
下面说一下你调用存储过程的问题。
我使用如下方法,供参考(已经在实际环境上使用半年以上,没有重启过进程,很稳定,访问若干个DB的存储过程)。
首先确保自己的freetds和unixODBC安装是正确的,具体方法可以百度,有很多帖子相关于此。实在不行,我也可以给你一个测试文档。
测试自己的SQL语句是否可以直接链接执行。
有了以上的方法保证,再做下面的事情。
首先找一个otlv4.h加入到你的文件中并引用,相信你也知道这个东西。(网上有很多,里面有几万行代码,不用理它,它可以方便的和freetds整合)
我的代码如下:
bool CDBManager::ExecuteSQLProc(int nDBCommand, const char* szDBProcName, CDBProcParam& DBProcParam, bool bFlag, CRecordSet* pRecordSet)
{
//获得操连接
otl_connect* pOtlConnect = AppDBOption::instance()->GetConnect(nDBCommand);
ACE_DEBUG((LM_ERROR, "(%d)Get pOtlConnect(%d). \n", nDBCommand, pOtlConnect));

try
{

if(pOtlConnect == NULL)
{
ACE_DEBUG((LM_ERROR, "(%d)Get pOtlConnect fail. \n", nDBCommand));
return false;
}

bool bRecord = false;
if(pRecordSet != NULL)
{
//ACE_DEBUG((LM_ERROR, "(%d)pRecordSet->Execute. \n", nDBCommand));
bRecord = pRecordSet->ExecuteProc(nDBCommand, 1, szDBProcName, DBProcParam, pOtlConnect);
}

return bRecord;
}
catch(otl_exception& p)

{
ACE_DEBUG((LM_ERROR, "SQL fail(%s)!\n", p.msg));
//如果查询异样,归还这个连接
bool bState = AppDBOption::instance()->SetConnect(nDBCommand, pOtlConnect);
if(bState != true)
{
ACE_DEBUG((LM_ERROR, "(%d)Set pdbprocess fail. \n", nDBCommand));
}
return false;
}

return true;
}
因为我做了一系列通用的类给别的程序使用,可以关联许多数据库,并自动实现链接断开重新链接等操作,所以看上去有些复杂,其实关键代码是很简单的。CRecordSet是我自己仿照ADO接口写的一个简单类。CDBProcParam实际上是一个Vector,用来记录存储过程中需要的参数和数值。szDBProcName是存储过程的名字,nDBCommand么,是数据库链接的代号,方便链接不同数据库准备的,不用管它。
关键在于,首先,你必须建立一个链接对象pOtlConnect。
这里我要先解释一下otl_connect* pOtlConnect(otlv4.h)中的这个对象,他是数据库的链接对象句柄。初始化的代码如下。
//创建连接
bool Connect(const char* szIP, const char* szUser, const char* szPass, const char* szDBName)
{
try
{
Close();
char szConnectString = {'\0'};
sprintf(szConnectString, "UID=%s;PWD=%s;DSN=%s", szUser, szPass, szDBName);

ACE_DEBUG((LM_ERROR, "(%s)!\n", szConnectString));
m_otlConnector.rlogon(szConnectString);
   

m_bConnect = true;
return true;
}
catch(otl_exception& p)
{
ACE_DEBUG((LM_ERROR, "New otl_connect fail(%s)!\n", p.msg));
}
}
这里的关键在于"UID=%s;PWD=%s;DSN=%s",想必你也明白。这里有一点要说明,在Linux的freetds安装完后,有一个odbc.ini的文件,一定要在这里添加你的DSN,这里的DSN就是你链接远程数据库需要的DSN名字,freetds会根据这个名字去匹配odbc.ini中的DSN链接参数。具体方法网上百度一下,有很多。我这里用了一些ACE的代码,不用管它。意思明白就行了。
行了,链接成功了。继续说链接数据库,调用存储过程的代码。
bool CRecordSet::ExecuteProc(int nDBConnand, int nRowCount, const char* szProcName, CDBProcParam& DBProcParam, otl_connect* pOtlConnect)
{
char szProcData = {'\0'};
char szProcBuff = {'\0'};
int i = 0;
int nType = 1;
m_nDBConnand = nDBConnand;
m_pOtlConnect = pOtlConnect;

//如果有原来的连接,则关闭
Close();

//连接为空,则退出执行
if(pOtlConnect == NULL)
{
return false;
}

try
{
//拼装PROC请求
for(i = 0; i < DBProcParam.GetCount(); i++)
{
_DBProcPValue* pDBProcPValue = DBProcParam.GetParam(i);
nType = pDBProcPValue->GetType();
if(nType == DBPROC_TYPE_INT)
{
if(pDBProcPValue->GetParamFlag() == 0)
{
if(i == 0)
{
sprintf(szProcBuff, ":%s<int>", pDBProcPValue->GetName());
}
else
{
sprintf(szProcBuff, "%s, :%s<int>", szProcBuff, pDBProcPValue->GetName());
}
}
else
{
if(i == 0)
{
sprintf(szProcBuff, ":%s<int>", pDBProcPValue->GetName());
}
else
{
sprintf(szProcBuff, "%s, :%s<int>", szProcBuff, pDBProcPValue->GetName());
}
}
}
if(nType == DBPROC_TYPE_VARCHAR)
{
if(pDBProcPValue->GetParamFlag() == 0)
{
if(i == 0)
{
sprintf(szProcBuff, ":%s<char>", pDBProcPValue->GetName());
}
else
{
sprintf(szProcBuff, "%s, :%s<char>", szProcBuff, pDBProcPValue->GetName());
}
}
else
{
if(i == 0)
{
sprintf(szProcBuff, ":%s<char>", pDBProcPValue->GetName());
}
else
{
sprintf(szProcBuff, "%s, :%s<char>", szProcBuff, pDBProcPValue->GetName());
}
}
}
if(nType == DBPROC_TYPE_DATETIME)
{
if(pDBProcPValue->GetParamFlag() == 0)
{
if(i == 0)
{
sprintf(szProcBuff, ":%s<long>", pDBProcPValue->GetName());
}
else
{
sprintf(szProcBuff, "%s, :%s<long>", szProcBuff, pDBProcPValue->GetName());
}
}
else
{
if(i == 0)
{
sprintf(szProcBuff, ":%s<long>", pDBProcPValue->GetName());
}
else
{
sprintf(szProcBuff, "%s, :%s<long>", szProcBuff, pDBProcPValue->GetName());
}
}
}
}
sprintf(szProcData, "{call %s(%s) }", szProcName, szProcBuff);

//执行SQL存储过程语句
m_pOtlStream = new otl_stream(nRowCount, szProcData, *pOtlConnect, otl_implicit_select);
ACE_DEBUG((LM_ERROR, "Proc String(%s)!\n", szProcData));

//输入存储过程参数
for(i = 0; i < DBProcParam.GetCount(); i++)
{
_DBProcPValue* pDBProcPValue = DBProcParam.GetParam(i);
nType = pDBProcPValue->GetType();
if(nType == DBPROC_TYPE_INT)
{
ACE_DEBUG((LM_ERROR, "Proc Param int(%d)!\n", *pDBProcPValue->GetInt()));
(*m_pOtlStream) << (*pDBProcPValue->GetInt());
}
if(nType == DBPROC_TYPE_VARCHAR)
{
ACE_DEBUG((LM_ERROR, "Proc Param char(%s)!\n", pDBProcPValue->GetString()));
(*m_pOtlStream) << pDBProcPValue->GetString();
}
if(nType == DBPROC_TYPE_DATETIME)
{
ACE_DEBUG((LM_ERROR, "Proc Param long(%d)!\n", *pDBProcPValue->GetLong()));
(*m_pOtlStream) << (*pDBProcPValue->GetLong());
}
}

//ACE_DEBUG((LM_ERROR, "Proc OK!\n"));
return true;
}
catch(otl_exception& p)

{
ACE_DEBUG((LM_ERROR, "SQL fail(%s)!\n", p.msg));

//写入日志
char szLog = {'\0'};
ACE_OS::sprintf(szLog, "%s", p.msg);
AppLogFileManager::instance()->doLog(1001, szLog);
return false;
}
}
代码有点唬人,其实关键的地方只有几句,其他的代码都是我通用化做的冗余,不理他。
sprintf(szProcData, "{call %s(%s) }", szProcName, szProcBuff);
这是使用otlv4.h调用底层freetds接口的写法。
m_pOtlStream = new otl_stream(nRowCount, szProcData, *pOtlConnect, otl_implicit_select);
nRowCount一般固定参数是1。
szProcData是你拼装的存储过称的语句,
pOtlConnect是你的数据库链接句柄。
otl_implicit_select是一个调用过程参数。
回来的数据会放在m_pOtlStream这个对象里面。
这是一个流。你可以根据你的结果集的类型,将数据一点点的流出来。
比如,返回的数据结果集如下:
Name(varchar(20)) ID(int)
freeeyes 1029

流出的顺序是
string strName;
int nID;

while(m_pOtlStream->eof()== true)
{
m_pOtlStream >> strName;
m_pOtlStream >> nID;
}
如果结果集似乎多列,自己做一个循环吧。数据怎么放可以自己随意。

以上就是freetds调用的程序。


代码在Linux32位系统下测试通过。
页: [1]
查看完整版本: 在Linux下使用freeTDS和SQLServer服务器链接