odbc.cpp

来自「Shorthand是一个强大的脚本语言」· C++ 代码 · 共 300 行

CPP
300
字号
#ifdef WIN32
#include <windows.h>
#else
#include <stdlib.h>
#include <stdio.h>
#endif
#include <sql.h>
#include <sqlext.h>

#include "dbdriver.h"
#include "except.h"

class OdbcErrorContainer
{
public:
    SQLTCHAR*   m_szMessage;
    SQLTCHAR*   m_szState;
    SQLINTEGER  m_nNativeError;
    SQLSMALLINT m_cbMessage;

    OdbcErrorContainer(HENV env, SQLHDBC connection = 0, SQLHSTMT statement = 0)
    {
        m_szMessage =  (SQLTCHAR*) malloc(sizeof(SQLTCHAR)*SQL_MAX_MESSAGE_LENGTH);
        m_szState   =  (SQLTCHAR*) malloc(sizeof(SQLTCHAR)*SQL_MAX_MESSAGE_LENGTH);
        m_szMessage[0] = '\0';
        m_szState[0] = '\0';
        m_cbMessage = SQL_MAX_MESSAGE_LENGTH-1;
        populate(env, connection, statement);
    }

    void populate(HENV env, SQLHDBC connection = 0, SQLHSTMT statement = 0)
    {
	SQLError(env, connection, statement, m_szState, &m_nNativeError, m_szMessage,
		SQL_MAX_MESSAGE_LENGTH-1, &m_cbMessage );
    }

    const char* getErrorMessage() const { return (const char*) m_szMessage; }
    int getErrorCode() const { return m_nNativeError; }
    const char* getOdbcState() const { return (const char*) m_szState; }

    ~OdbcErrorContainer()
    {
        if (m_szMessage != NULL) free(m_szMessage);
        if (m_szState != NULL) free(m_szState);
    }
};


class OdbcDriver : public ShhDbDriver
{
private:
    HENV m_env;

public:

    OdbcDriver() 
    {
        m_env = NULL;
    }

    void throwSQL(int code, const char* format, ...)
    {
        va_list args; va_start(args, format);
        throw new ShhObjectException(format, args);
    }

    void check_odbc_status(SQLRETURN code, SQLHDBC connection = 0, SQLHSTMT statement = 0)
    {
	if (code == SQL_SUCCESS || code == SQL_SUCCESS_WITH_INFO) return;

        if (m_env == NULL) throwSQL(7101, "ODBC Error %d (%0x08x)", code, code);
        OdbcErrorContainer e(m_env, connection, statement);
        
	throwSQL(7101, "%s %s", e.getOdbcState(), e.getErrorMessage());
    }

    bool spi_RecognizeDriverName(const char* driverName)
    {
        string name(driverName);
        return name.ieq("ODBC");
    }

    const char* spi_GetDriverPrefix()
    {
        return "ODB";
    }
    
    int spi_GetDriverDescription(string& driverDescription)
    {
        driverDescription = "Generic ODBC driver";
        return driverDescription.length();
    }
    
    virtual ShhDbHandle spi_Connect(const char* connString)
    {
        SQLRETURN rc;
        if (m_env == NULL)
        {
            rc = SQLAllocEnv(&m_env);
            check_odbc_status(rc);
        }

        HDBC session;

        rc = SQLAllocConnect(m_env, &session);
        check_odbc_status(rc);

        rc = SQLConnect(session, (SQLTCHAR*) connString, strlen(connString), 
            (SQLTCHAR*)"", 0, (SQLTCHAR*)"", 0);
        check_odbc_status(rc, session);
        
        return (ShhDbHandle) session;
        
    }

    virtual int spi_GetLastError(ShhDbHandle conn, string& message)
    {
        OdbcErrorContainer e(m_env, (SQLHDBC) conn);
	message = e.getErrorMessage();
        return e.getErrorCode();
    }

    virtual void spi_Disconnect(ShhDbHandle conn)
    {
        if (conn != NULL)
        {
            SQLDisconnect((SQLHDBC) conn);
            SQLFreeConnect((SQLHDBC) conn);
        }
    }

    virtual ShhCursorHandle spi_PrepareSQL(ShhDbHandle conn, const char* sql)
    {
        SQLHSTMT cursorHandle;
        SQLRETURN rc = SQLAllocStmt((SQLHDBC) conn, &cursorHandle);
        check_odbc_status(rc, (SQLHDBC)conn);
        return (ShhCursorHandle) cursorHandle;
    }

    virtual void spi_CloseCursor(ShhCursorHandle cursor)
    {
        if (cursor != NULL)
        {
            SQLFreeStmt((SQLHSTMT) cursor, SQL_CLOSE);
        }
    }
    
    virtual void spi_CloseResult(ShhResultHandle resultSet)
    {
        if (resultSet != 0)
        {
            //SQLFreeStmt((SQLHSTMT) resultSet, SQL_CLOSE);
        }
    }

    virtual int spi_ExecuteImmediate(ShhDbHandle conn, const char* sql)
    {
        SQLHSTMT cursorHandle;
        SQLRETURN rc;

        rc = SQLAllocStmt((SQLHDBC) conn, &cursorHandle);
        check_odbc_status(rc, (SQLHDBC) conn);
        rc = SQLExecDirect((SQLHSTMT) cursorHandle, (SQLTCHAR*) sql, strlen(sql));
        check_odbc_status(rc, (SQLHDBC)conn, (SQLHSTMT) cursorHandle);
        SQLFreeStmt((SQLHSTMT) cursorHandle, SQL_CLOSE);
        return 0;
    }

    virtual ShhResultHandle spi_ExecuteCursor(ShhDbHandle dbHandle, ShhCursorHandle cursor, const char* sql)
    {
        SQLRETURN rc = SQLExecDirect((SQLHSTMT) cursor, (SQLTCHAR*) sql, strlen(sql));
        check_odbc_status(rc, (SQLHDBC) dbHandle, (SQLHSTMT) cursor);
        return (ShhResultHandle) cursor;
    }

    virtual int spi_GetFieldCount(ShhResultHandle resultHandle)
    {
        if (resultHandle == 0) return 0;
        SQLSMALLINT nColumnCount = 0;
        SQLRETURN rc = SQLNumResultCols((SQLHSTMT) resultHandle, &nColumnCount);
        return nColumnCount;
    }

    virtual int spi_GetRowCount(ShhResultHandle resultHandle)
    {
        if (resultHandle == 0) return 0;
        SQLINTEGER nRowCount = 0;
        SQLRETURN rc = SQLGetStmtOption((SQLHSTMT) resultHandle, SQL_ROWSET_SIZE, &nRowCount);
	if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) 
        {
            return -1;// ODBC does not provide such information
        }
        else return nRowCount;
    }

    virtual int spi_GetFieldName(ShhResultHandle resultHandle, int fieldNumber, string& fieldName)
    {
        if (resultHandle == 0) { fieldName = ""; return 0; }

        char szColName[256];
        SQLSMALLINT cbColName = 0, cbColNameMax = sizeof(szColName);
        SQLUINTEGER cbColDef = 0;
        SQLSMALLINT nType, nScale, nNullable;

        SQLRETURN rc = SQLDescribeCol(
            (SQLHSTMT) resultHandle, (SQLUSMALLINT) fieldNumber+1,
            (SQLTCHAR*) szColName, cbColNameMax, &cbColName,
            &nType, &cbColDef, &nScale, &nNullable);
        
        check_odbc_status(rc, 0, (SQLHSTMT) resultHandle);
        fieldName = szColName;
        return fieldName.length();
    }

    virtual ShhRowHandle spi_Fetch(ShhResultHandle resultSet)
    {
        SQLRETURN rc = SQLFetch((SQLHSTMT) resultSet);
        if (rc == SQL_NO_DATA) return NULL;
        check_odbc_status(rc, 0, (SQLHSTMT) resultSet);
        return (ShhRowHandle) resultSet;
    }

    virtual int spi_GetStringValue(ShhResultHandle resultHandle, ShhRowHandle rowHandle, int fieldNumber, string& value)
    {
        SQLINTEGER  cbData = 256;
        SQLRETURN   rc;
        SQLTCHAR*   szData = (SQLTCHAR*) value.provide_window(cbData);
        
        *szData = '\0';

        rc = SQLGetData((SQLHSTMT) resultHandle, fieldNumber+1, 
                        SQL_C_CHAR, szData, cbData, &cbData);
        check_odbc_status(rc, 0, (SQLHSTMT) resultHandle);
        if (cbData > 256) 
        {
            // default buffer too small; reallocate it
            szData = (SQLTCHAR*) value.provide_window(cbData+2) + 255;
            rc = SQLGetData((SQLHSTMT) resultHandle, fieldNumber+1, 
                            SQL_C_CHAR, szData, cbData - 254, &cbData);
            check_odbc_status(rc, 0, (SQLHSTMT) resultHandle);
        }
        return cbData;
    }

    virtual int spl_GetAffectedRows(ShhCursorHandle handle)
    {
        SQLINTEGER rowCount = 0;
        SQLRETURN rc = SQLRowCount((SQLHSTMT) handle, &rowCount);
        check_odbc_status(rc, 0, (SQLHSTMT) handle);
        return rowCount;
    }

    virtual void spi_CloseRow(ShhRowHandle rowHandle)
    {
    }

    virtual int
        spi_ClearParameters(ShhCursorHandle hCursor)
    {
        SQLRETURN rc = SQLFreeStmt((SQLHSTMT) hCursor, SQL_RESET_PARAMS);
        //check_odbc_status(rc, 0, hCursor);
        return 0;
    }

    /**
     * Pastes parameter value into SQL statement.
     * If the driver supports parameter bindings and markers, it 
     * pastes marker (usually "?") into SQL instead of value itself 
     * and binds supplied value to that marker using native driver API.
     * If the driver doesn't support binding/markers, it pastes quotes
     * and escapes special characters inside strings as necessary.
     */
    virtual int
        spi_PasteValue(ShhCursorHandle hCursor, ShhDbExecutor* executor, int index, string& output, const char* value)
    {
        const char* szValue = executor->fpi_StoreParameter(index, value);
        
        output.append("?");

        return 0;
    }

    virtual int
        spi_BindValue(ShhCursorHandle hCursor, ShhDbExecutor* executor, int index, const char* szValue)
    {
        int cbValue = strlen(szValue);
        SQLRETURN rc = SQLSetParam((SQLHSTMT)hCursor, index+1,
            SQL_C_CHAR, SQL_CHAR, cbValue, 0, (void*) szValue, NULL);
        check_odbc_status(rc, (SQLHDBC) executor->fpi_GetConnection(), (SQLHSTMT) hCursor);
        return 0;
    }


};


ShhDbDriver* create_odbc_driver()
{
    return new OdbcDriver();
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?