Linux下使用C/C++访问数据库——SQL Server篇

  • Post author:
  • Post category:linux


在Linux下连接MSSQL是一件很痛苦的事,因为微软同志没有提供任何接口给开发人员,大约他们认为要用MSSQL的,只可能是windows的操作系统。还好,MSSQL是从Sybase衍生出来的,有一些哥们做了一些Sybase的Linux下的连接库,这些连接库同时也能支持MSSQL,FreeTDS就是这样的一个东东。
这篇文章的受用读者,我想是那些希望在Linux或Unix下编写C或C++程序来连接MSSQL2000的兄弟们,因为我就是这样的。同时,那些写PHP的哥们也可以参考一下,当然如果你是用PHP的,你恐怕还要知道APACHE以及PHP脚本的安装,或者关于PHP自定义Module的开发,可以参考我以前写的一篇Blog(

PHP5自定义Module开发

)。
下面开始我们的探索之旅:
一、相关软件
首先我们需要FreeTDS的安装包,可以在这个地址下载到:

http://ibiblio.org/pub/Linux/ALPHA/freetds/stable/freetds-stable.tgz
现在的最新版是0.82
其次就是大家需要自己搭建C++的开发环境了。
二、软件安装、配置
# tar zxvf freetds-stable.tgz(解压)
# ./configure –prefix=/usr/local/freetds \(指定FreeTDS安装路径)

–with-tdsver=8.0 –enable-msdblib (设置TDS版本,支持SQL Server 2000)
# make
# make install
将freetds的库文件所在路径配置到LD_LIBRARY_PATH参数中:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/freetds/lib/:

这么作的目的是为了避免加载FreeTds库文件加载不上的情况。
三、程序开发
不多说了,还是直接上代码:
/*

* SyBaseManager.h

*

*    Created .: Feb 18, 2009

*            Author: Steven Wee

*/

#ifndef SYBASEMANAGER_H_

#define SYBASEMANAGER_H_

#include “../Common/CheckStringTools.h”

#include <string>

#include <vector>

#include <iostream>

#include <assert.h>

#include <errno.h>

#include <stdlib.h>

#include <string.h>

#include <sybfront.h>

#include <sybdb.h>

using namespace std;

class SybaseManager

{

public:

SybaseManager(std::string hosts, std::string userName, std::string password, std::string dbName, unsigned int port);

~SybaseManager();

/*

* Init SQL Server

* @param hosts:         Host IP address

* @param userName:        Login UserName

* @param password:        Login Password

* @param dbName:        Database Name

* @param port:                Host listen port number

*/

void initConnection();

/*

* Making query from database

* @param mysql:        MySQL Object

* @param sql:                Running SQL command

*/

bool runSQLCommand(std::string sql);

/**

* Destroy MySQL object

* @param mysql                MySQL object

*/

void destroyConnection();

bool getConnectionStatus();

vector<vector<string> > getResult();

protected:

void setUserName(std::string userName);

void setHosts(std::string hosts);

void setPassword(std::string password);

void setDBName(std::string dbName);

void setPort(unsigned int port);

private:

bool IsConnected;

DBPROCESS *dbProcess;

vector< vector<string> > resultList;

unsigned int DEFAULTPORT;

char * HOSTS;

char * USERNAME;

char * PASSWORD;

char * DBNAME;

};

#endif /* SYBASEMANAGER_H_ */

/*

* SyBaseManager.cpp

*

*    Created .: Feb 18, 2009

*            Author: Steven Wee

*/

#include “SybaseManager.h”

SybaseManager::SybaseManager(std::string hosts, std::string userName, std::string password, std::string dbName, unsigned int port)

{

IsConnected = false;

this ->setHosts(hosts);

this ->setUserName(userName);

this ->setPassword(password);

this ->setDBName(dbName);

this ->setPort(port);

}

SybaseManager::~SybaseManager()

{

destroyConnection();

}

void SybaseManager::setDBName(string dbName)

{

if ( dbName.empty() )

{

std::cout << “DBName is null! Used default value: master” << std::endl;

this ->DBNAME = new char[5];

strcpy(this ->DBNAME, “master”);

}

else

{

this ->DBNAME = new char[dbName.length()];

strcpy(this ->DBNAME, dbName.c_str());

}

}

void SybaseManager::setHosts(string hosts)

{

if ( hosts.empty() )

{

std::cout << “Hosts is null! Used default value: localhost” << std::endl;

this ->HOSTS = new char[9];

strcpy(this ->HOSTS, “localhost”);

}

else

{

this ->HOSTS = new char[hosts.length()];

strcpy(this ->HOSTS, hosts.c_str());

}

}

void SybaseManager::setPassword(string password)

{

if ( password.empty() )

{

std::cout << “Password is null! Used default value: ” << std::endl;

this ->PASSWORD = new char[1];

strcpy(this ->PASSWORD, “”);

}

else

{

this ->PASSWORD = new char[password.length()];

strcpy(this ->PASSWORD, password.c_str());

}

}

void SybaseManager::setPort(unsigned int port)

{

if ( port )

{

std::cout << “Port number is null! Used default value: 0” << std::endl;

this ->DEFAULTPORT = 0;

}

else

{

this ->DEFAULTPORT = port;

}

}

void SybaseManager::setUserName(string userName)

{

if ( userName.empty() )

{

std::cout << “UserName is null! Used default value: sa” << std::endl;

this ->USERNAME = new char[4];

strcpy(this ->USERNAME, “sa”);

}

else

{

this ->USERNAME = new char[userName.length()];

strcpy(this ->USERNAME, userName.c_str());

}

}

void SybaseManager::initConnection()

{

string Charset = “UTF-8”;

dbinit();

LOGINREC *loginREC = dblogin();

DBSETLUSER(loginREC, this ->USERNAME);

DBSETLPWD(loginREC, this ->PASSWORD);

DBSETLCHARSET(loginREC, Charset.c_str());

dbProcess = dbopen(loginREC, this ->HOSTS);

if ( dbProcess == FAIL )

{

std::cout << “Connect to SQL Server failed!” << std::endl;

}

if ( dbuse( dbProcess, this ->DBNAME ) == FAIL )

{

std::cout << “Use table failed!” << std::endl;

}

}

bool SybaseManager::runSQLCommand( string sql )

{

dbcmd(dbProcess, sql.c_str());

if ( dbsqlexec(dbProcess) == FAIL )

{

std::cout << “Query from database failed!” << std::endl;

}

DBINT result_code;

vector<string> objectValue;

StringTools stringTools;

sql = stringTools.filterString(sql);

while ( (result_code = dbresults(dbProcess)) != NO_MORE_RESULTS )

{

struct Column

{

char* colName;

char* colBuffer;

int colType, colSize, colStatus;

} *columns, *pCol;

int nColumns;

int rowNo;

if ( result_code == SUCCEED )

{

nColumns = dbnumcols(dbProcess);

if ( (columns = (Column*)calloc(nColumns, sizeof(struct Column))) == NULL )

{

std::cout << “Error at bind data” << std::endl;

return false;

}

for ( pCol = columns; pCol – columns < nColumns; pCol++ )

{

int colNo = pCol – columns + 1;

pCol ->colName = dbcolname(dbProcess, colNo);

pCol ->colType = dbcoltype(dbProcess, colNo);

pCol ->colSize = dbcollen(dbProcess, colNo);

if ( SYBCHAR != pCol ->colType )

{

pCol ->colSize = dbwillconvert(pCol ->colType, SYBCHAR);

}

if ( (pCol ->colBuffer = (char*)calloc(1, pCol ->colSize + 1)) == NULL )

{

std::cout << “Check column buffer error!” << std::endl;

return false;

}

if ( dbbind(dbProcess, colNo, STRINGBIND, pCol ->colSize + 1, (BYTE*)pCol ->colBuffer) == FAIL )

{

std::cout << “Running dbbind() error!” << std::endl;

return false;

}

if ( dbnullbind(dbProcess, colNo, &pCol ->colStatus) == FAIL )

{

std::cout << “Running dbnullbind() error!” << std::endl;

return false;

}

}

while ( (rowNo = dbnextrow(dbProcess)) != NO_MORE_ROWS )

{

objectValue.clear();

switch ( rowNo )

{

case REG_ROW:

for ( pCol = columns; pCol – columns < nColumns; pCol++ )

{

const char* columnBuffer = pCol ->colStatus == -1 ? “NULL” : pCol ->colBuffer;

objectValue.push_back(stringTools.Trim(columnBuffer));        //        std::cout << columnBuffer << std::endl;

}

break;

case BUF_FULL:

assert( rowNo != BUF_FULL );

break;

case FAIL:

std::cout << “Get result error!” << std::endl;

break;

default:

std::cout << “Get result ignore, row number:” << rowNo << std::endl;

}

this ->resultList.push_back(objectValue);

}

for ( pCol = columns; pCol – columns < nColumns; pCol++ )

{

free( pCol ->colBuffer );

}

free( columns );

/*

if ( DBCOUNT(dbProcess) > -1 )

{

std::cout << “Affected rows:” << DBCOUNT(dbProcess) << std::endl;

}

*/

if ( dbhasretstat(dbProcess) == TRUE )

{

std::cout << “Procedure returned ” << dbhasretstat(dbProcess) << std::endl;

}

}

}

return true;

}

void SybaseManager::destroyConnection()

{

dbclose(dbProcess);

}

bool SybaseManager::getConnectionStatus()

{

return IsConnected;

}

vector< vector<string> > SybaseManager::getResult()

{

return this ->resultList;

}

四、修改建议
本人在以后的完善中,打算把runSQLCommand(char * sql)函数分解成两个或者三个函数,分别执行select和insert等语句。
在程序中,我并没有强制要求参数必须为const,可能会出现一些安全问题。
本文仅起抛砖引玉的作用,希望有高手可以指点我程序中的问题。
敬请期待下一篇文章:Linux下使用C/C++访问数据库——Oracle之OCI篇

转载于:https://blog.51cto.com/commandos/136829


关闭菜单