/* CVSNT Generic API Copyright (C) 2005 Tony Hoyle and March-Hare Software Ltd This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // MS Sql // Win32 specific #define WIN32_LEAN_AND_MEAN #include #include #include #include #include "../lib/api_system.h" #include "../cvs_string.h" #include "../FileAccess.h" #include "../ServerIO.h" #include #include #include "MssqlConnection.h" #include "MssqlRecordset.h" extern "C" CSqlConnection *Mssql_Alloc() { return new CMssqlConnection; } CMssqlConnection::CMssqlConnection() { m_hEnv=NULL; m_hDbc=NULL; m_lasterror=SQL_SUCCESS; } CMssqlConnection::~CMssqlConnection() { Close(); } bool CMssqlConnection::Create(const char *host, const char *database, const char *username, const char *password) { if(!Open(host,"master",username,password)) return false; Execute("create database %s",database); if(Error()) return false; Close(); return Open(host,database,username,password); } bool CMssqlConnection::Open(const char *host, const char *database, const char *username, const char *password) { m_lasterror = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&m_hEnv); if(!SQL_SUCCEEDED(m_lasterror)) return false; m_lasterror = SQLSetEnvAttr(m_hEnv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); if(!SQL_SUCCEEDED(m_lasterror)) return false; m_lasterror = SQLAllocHandle(SQL_HANDLE_DBC,m_hEnv,&m_hDbc); if(!SQL_SUCCEEDED(m_lasterror)) return false; cvs::wstring strConn; cvs::swprintf(strConn,80,L"DATABASE={%s};DRIVER={SQL Server};Pwd={%s};Server={%s};Uid={%s};", (const wchar_t*)cvs::wide(database),(const wchar_t*)cvs::wide(password),(const wchar_t*)cvs::wide(host),(const wchar_t*)cvs::wide(username)); m_lasterror = SQLDriverConnect(m_hDbc,NULL,(SQLWCHAR*)strConn.c_str(),(SQLSMALLINT)strConn.size(),NULL,0,NULL,SQL_DRIVER_NOPROMPT); if(!SQL_SUCCEEDED(m_lasterror)) return false; m_lasterror = SQLSetConnectAttr(m_hDbc,SQL_ATTR_AUTOCOMMIT,(SQLPOINTER)SQL_AUTOCOMMIT_ON,0); if(!SQL_SUCCEEDED(m_lasterror)) return false; return true; } bool CMssqlConnection::Close() { if(m_hEnv) { SQLDisconnect(m_hDbc); SQLFreeConnect(m_hDbc); SQLFreeEnv(m_hEnv); } m_hEnv = NULL; m_hDbc = NULL; m_lastrsError=L""; return true; } bool CMssqlConnection::IsOpen() { if(m_hEnv) return true; return false; } CSqlRecordsetPtr CMssqlConnection::Execute(const char *string, ...) { SQLRETURN ret; cvs::string str; va_list va; va_start(va,string); cvs::vsprintf(str,64,string,va); va_end(va); CMssqlRecordset *rs = new CMssqlRecordset(); CServerIo::trace(3,"%s",str.c_str()); bool inquote = false; for(size_t n=0; n::iterator i = m_bindVars.begin(); i!=m_bindVars.end(); ++i) { switch(i->second.type()) { case CSqlVariant::vtNull: m_sqli[i->first]=SQL_NULL_DATA; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,0,0,NULL,0,&m_sqli[i->first]); break; case CSqlVariant::vtChar: m_sqli[i->first]=0; m_sqlv[i->first].c=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,1,0,&m_sqlv[i->first].c,1,&m_sqli[i->first]); break; case CSqlVariant::vtUChar: m_sqli[i->first]=0; m_sqlv[i->first].c=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_CHAR,SQL_CHAR,1,0,&m_sqlv[i->first].c,1,&m_sqli[i->first]); break; case CSqlVariant::vtShort: m_sqli[i->first]=0; m_sqlv[i->first].s=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_SSHORT,SQL_INTEGER,0,0,&m_sqlv[i->first].s,0,&m_sqli[i->first]); break; case CSqlVariant::vtUShort: m_sqli[i->first]=0; m_sqlv[i->first].s=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_USHORT,SQL_INTEGER,0,0,&m_sqlv[i->first].s,0,&m_sqli[i->first]); break; case CSqlVariant::vtInt: case CSqlVariant::vtLong: m_sqli[i->first]=0; m_sqlv[i->first].l=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_SLONG,SQL_INTEGER,0,0,&m_sqlv[i->first].l,0,&m_sqli[i->first]); break; case CSqlVariant::vtUInt: case CSqlVariant::vtULong: m_sqli[i->first]=0; m_sqlv[i->first].l=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_ULONG,SQL_INTEGER,0,0,&m_sqlv[i->first].l,0,&m_sqli[i->first]); break; case CSqlVariant::vtLongLong: m_sqli[i->first]=0; m_sqlv[i->first].ll=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_SBIGINT,SQL_BIGINT,0,0,&m_sqlv[i->first].ll,0,&m_sqli[i->first]); break; case CSqlVariant::vtULongLong: m_sqli[i->first]=0; m_sqlv[i->first].ll=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_UBIGINT,SQL_BIGINT,0,0,&m_sqlv[i->first].ll,0,&m_sqli[i->first]); break; case CSqlVariant::vtString: m_sqli[i->first]=SQL_NTS; m_sqlv[i->first].ws=cvs::wide(i->second); ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_WCHAR,SQL_WVARCHAR,(SQLINTEGER)m_sqlv[i->first].ws.size()+1,0,(SQLPOINTER)m_sqlv[i->first].ws.c_str(),(SQLINTEGER)m_sqlv[i->first].ws.size()+1,&m_sqli[i->first]); break; case CSqlVariant::vtWString: m_sqli[i->first]=SQL_NTS; m_sqlv[i->first].ws=i->second; ret = SQLBindParameter(hStmt,i->first+1,SQL_PARAM_INPUT,SQL_C_WCHAR,SQL_WVARCHAR,(SQLINTEGER)m_sqlv[i->first].ws.size()+1,0,(SQLPOINTER)m_sqlv[i->first].ws.c_str(),(SQLINTEGER)m_sqlv[i->first].ws.size()+1,&m_sqli[i->first]); break; } } CServerIo::trace(3,"%s",str.c_str()); rs->Init(this,hStmt,str.c_str()); // Ignore return... it's handled by the error routines m_bindVars.clear(); return rs; } bool CMssqlConnection::Error() const { if(SQL_SUCCEEDED(m_lasterror)) return false; return true; } const char *CMssqlConnection::ErrorString() { SQLWCHAR state[6]; SQLINTEGER error; SQLSMALLINT size = 512,len; cvs::wstring err; err.resize((int)size); SQLWCHAR *pmsg = (SQLWCHAR*)err.data(); if(m_lastrsError.size()) { wcscpy((wchar_t *)pmsg,m_lastrsError.c_str()); pmsg+=m_lastrsError.size(); size-=(SQLSMALLINT)m_lastrsError.size(); m_lastrsError=L""; } if(m_hDbc) { for(int i=1; SQL_SUCCEEDED(SQLGetDiagRec(SQL_HANDLE_DBC, m_hDbc, i, state, &error, pmsg, size, &len)); i++) { size-=len; pmsg+=len; } } if(m_hEnv) { for(int i=1; SQL_SUCCEEDED(SQLGetDiagRec(SQL_HANDLE_ENV, m_hEnv, i, state, &error, pmsg, size, &len)); i++) { size-=len; pmsg+=len; } } err.resize(512-size); m_lasterrorString=cvs::narrow(err.c_str()); return m_lasterrorString.c_str(); } unsigned CMssqlConnection::GetInsertIdentity(const char *table_hint) { HSTMT hStmt; m_lasterror=SQLAllocStmt(m_hDbc,&hStmt); if(!SQL_SUCCEEDED(m_lasterror)) return 0; m_lasterror=SQLExecDirect(hStmt,(SQLWCHAR*)L"SELECT @@IDENTITY",SQL_NTS); if(!SQL_SUCCEEDED(m_lasterror)) { SQLFreeStmt(hStmt,SQL_DROP); return 0; } long id; SQLINTEGER len; m_lasterror=SQLBindCol(hStmt,1,SQL_C_LONG,&id,sizeof(id),&len); if(!SQL_SUCCEEDED(m_lasterror)) { SQLFreeStmt(hStmt,SQL_DROP); return 0; } m_lasterror=SQLFetch(hStmt); if(!SQL_SUCCEEDED(m_lasterror)) return 0; SQLFreeStmt(hStmt,SQL_DROP); return (unsigned)id; } bool CMssqlConnection::BeginTrans() { m_lasterror = SQLSetConnectAttr(m_hDbc,SQL_ATTR_AUTOCOMMIT,(SQLPOINTER)SQL_AUTOCOMMIT_OFF,0); if(!SQL_SUCCEEDED(m_lasterror)) return false; return true; } bool CMssqlConnection::CommitTrans() { m_lasterror = SQLEndTran(SQL_HANDLE_DBC,m_hDbc,SQL_COMMIT); if(!SQL_SUCCEEDED(m_lasterror)) return false; m_lasterror = SQLSetConnectAttr(m_hDbc,SQL_ATTR_AUTOCOMMIT,(SQLPOINTER)SQL_AUTOCOMMIT_ON,0); if(!SQL_SUCCEEDED(m_lasterror)) return false; return true; } bool CMssqlConnection::RollbackTrans() { m_lasterror = SQLEndTran(SQL_HANDLE_DBC,m_hDbc,SQL_ROLLBACK); if(!SQL_SUCCEEDED(m_lasterror)) return false; m_lasterror = SQLSetConnectAttr(m_hDbc,SQL_ATTR_AUTOCOMMIT,(SQLPOINTER)SQL_AUTOCOMMIT_ON,0); if(!SQL_SUCCEEDED(m_lasterror)) return false; return true; } bool CMssqlConnection::Bind(int variable, CSqlVariant value) { m_bindVars[variable]=value; return true; }