2009-08-27 38 views
0

OLE DBとSQL Server Native Clientを使用してSQL Serverデータベースにアクセスするアプリケーションで作業しています。これまで、私はかなり単純なSQLを扱ってきました。このため、ICommandTextを取得し、SetCommandTextを使用しています。私は今大きなオブジェクトをデータベースに挿入したい。私はICommandStreamが存在すると思うが、これを使うと、IStreamを実装するクラスを追加し、BLOBを適切に引用する(アポストロフィなどをエスケープする)必要があるようだ。確かに簡単な方法がありますか?OLE DBを使用したBLOBの挿入

サイドノート:OLE DBは私の選択ではなく、この段階では変更できません。だから、 "より高いレベルのものを使う"という簡単な方法は利用できません。

答えて

0

これは、answer on the Microsoft SQLNCli team blogです。

これを拡張するには、私が使い終わったコードを次に示します。まず、SQL Server Native Clientの読み取りにISequentialStreamが必要です。私は自分のデータを記憶しているので、BLOBへのポインタでこれを構築することはできますが、他の場所からデータを取得するのは簡単です。契約の一部ではありませんが、読み込みが1024バイトのチャンクで起こっているように見えることはおそらく有用でしょう。

struct ISequentialStream; 

class XYZSQLStream : public ISequentialStream 
{ 
public: 
    XYZSQLStream(LPBYTE data, __int64 ulLength); 
    virtual ~XYZSQLStream(); 

    virtual BOOL Clear(); 
    virtual ULONG Length() { return m_cBufSize; }; 

    virtual operator void* const() { return m_pBuffer; }; 

    STDMETHODIMP_(ULONG) AddRef(void); 
    STDMETHODIMP_(ULONG) Release(void); 
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv); 

    STDMETHODIMP Read(void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbRead); 
    STDMETHODIMP Write(const void __RPC_FAR *pv, ULONG cb, ULONG __RPC_FAR *pcbWritten); 

private: 
    ULONG m_cRef; // reference count 
    void* m_pBuffer; // buffer 
    ULONG m_cBufSize; // buffer size 
    ULONG m_iPos; // current index position in the buffer 
}; 

これの実装は簡単です::ここに私のストリームクラスの

XYZSQLStream::XYZSQLStream(LPBYTE data, ULONG ulLength) 
{ 
    m_iPos = 0; 
    m_cRef = 0; 
    m_pBuffer = data; 
    m_cBufSize = ulLength; 

    AddRef(); 
} 

XYZSQLStream::~XYZSQLStream() 
{ 
    // Shouldn't have any references left 
    if (m_cRef) 
     throw L"Destroying SQLStream with references"; 
    delete[] m_pBuffer; 
} 

ULONG XYZSQLStream::AddRef() 
{ 
    return ++m_cRef; 
} 

ULONG XYZSQLStream::Release() 
{ 
    if (!m_cRef) 
     throw L"Releasing referenceless SQLStream"; 
    if (--m_cRef) 
     return m_cRef; 

    delete this; 
    return 0; 
} 

HRESULT XYZSQLStream::QueryInterface(REFIID riid, void** ppv) 
{ 
    if (!ppv) 
     return E_INVALIDARG; 
    *ppv = NULL; 

    if (riid == IID_IUnknown) 
     *ppv = this; 

    if (riid == IID_ISequentialStream) 
     *ppv = this; 

    if(*ppv) 
    { 
     ((IUnknown*)*ppv)->AddRef(); 
     return S_OK; 
    } 

    return E_NOINTERFACE; 
} 

BOOL XYZSQLStream::Clear() 
{ 
    m_iPos = 0; 
    m_cBufSize = 0; 

    m_pBuffer = NULL; 

    return TRUE; 
} 

HRESULT XYZSQLStream::Read(void *pv, ULONG cb, ULONG* pcbRead) 
{ 
    if (pcbRead) 
     *pcbRead = 0; 

    if (!pv) 
     return STG_E_INVALIDPOINTER; 

    if (cb == 0) 
     return S_OK; 

    ULONG cBytesLeft = m_cBufSize - m_iPos; 
    ULONG cBytesRead = cb > cBytesLeft ? cBytesLeft : cb; 

    //DEBUG(L"cb %d, left %d, read %d\n", cb, cBytesLeft, cBytesRead); 

    if (cBytesLeft == 0) 
     return S_FALSE; 

    // Copy to users buffer the number of bytes requested or remaining 
    memcpy(pv, (void*)((BYTE*)m_pBuffer + m_iPos), cBytesRead); 
    m_iPos += cBytesRead; 

    if (pcbRead) 
     *pcbRead = cBytesRead; 

    if (cb != cBytesRead) 
     return S_FALSE; 

    return S_OK; 
} 

HRESULT XYZSQLStream::Write(const void *pv, ULONG cb, ULONG* pcbWritten) 
{ 
    // Parameter checking 
    if (!pv) 
     return STG_E_INVALIDPOINTER; 

    if (pcbWritten) 
     *pcbWritten = 0; 

    if (cb == 0) 
     return S_OK; 

    // Enlarge the current buffer 
    m_cBufSize += cb; 

    // Need to append to the end of the stream 
    m_pBuffer = CoTaskMemRealloc(m_pBuffer, m_cBufSize); 
    memcpy((void*)((BYTE*)m_pBuffer + m_iPos), pv, cb); 
    // m_iPos += cb; 

    if (pcbWritten) 
     *pcbWritten = cb; 

    return S_OK; 
} 

ICommandTextを使用して、その後、テーブルの上にSELECTを実行することができます。実際にこれを使用してデータを取得するわけではありません。IRowsetChangeを取得する方法です。私はこれのために余分なExecuteCommandメソッドを持っています。 pSQLで渡されたSQLはSELECT x,y,z FROM TableWithBlobと同じです。 FAILは、問題を記録して戻すカスタムマクロです。

HRESULT XYZSQLCommand::ExecuteCommand(TCHAR* pSQL, IRowset** ppRowSet, IRowsetChange** ppRowSetChange) 
{ 
    HRESULT hr; 
    IRowsetChange* pIRowsetChange; 
    IRowset* pIRowset; 
    hr = m_pICommandText->SetCommandText(DBGUID_DBSQL, pSQL); 
    if (FAILED(hr)) 
     FAIL(hr); 

    hr = m_pICommandText->Execute(NULL, IID_IRowsetChange, NULL, NULL, (IUnknown**)&pIRowsetChange); 
    if (FAILED(hr)) 
     FAIL(hr); 

    hr = pIRowsetChange->QueryInterface(IID_IRowset, (void**)&pIRowset); 
    if (FAILED(hr)) 
    { 
     pIRowsetChange->Release(); 
     FAIL(hr); 
    } 

    *ppRowSet = pIRowset; 
    *ppRowSetChange = pIRowsetChange; 
    return S_OK; 
} 

私は、問題のテーブルのIRowsetとIRowsetChangeを持っています。通常通り、DBBINDINGを作成します。私はこれを払っています - それは本当に問題には関係ありません。関連するビットは次のとおりです。

static DBOBJECT streamObj = {STGM_READ, IID_ISequentialStream}; 
pDBBindings[nCol].pObject = &streamObj; 
pDBBindings[nCol].wType = DBTYPE_IUNKNOWN; 
pDBBindings[nCol].cbMaxLen = sizeof(ISequentialStream*); 

その後、一致するデータメモリブロックに充填する場合、あなたは、この(醜いキ​​ャストを気の毒に)行うことができます。

XYZSQLStream *stream = new XYZSQLStream(data_to_write, length_of_data); 
*((ISequentialStream**)(pbData+pDBBindings[x].obValue)) = stream; 
*((DBLENGTH*)(pbData+pDBBindings[x].obLength)) = (DBLENGTH)length_of_data; 
*((DBSTATUS*)(pbData+pDBBindings[x].obStatus)) = DBSTATUS_S_OK; 

を自分であなたのIRowsetChangeを使用してIAccessorを取得し、それをバインドします

最後に
IAccessor* pIAccessor; 
HACCESSOR hAccessor; 
DBBINDSTATUS* pDBBindStatus; 

hr = pRowsetChange->QueryInterface(IID_IAccessor, (void**) &pIAccessor); 
// Error handling elided 

pDBBindStatus = new DBBINDSTATUS[ulCols]; 

//Associate the bindings with the data accessor for the rowset 
hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, ulCols, pDBBindings, 0, hAccessor, pDBBindStatus); 
// Error handling, cleanup elided 

、あなたはあなたの行を挿入することができます

hr = pRowsetChange->InsertRow(NULL, hAccessor, pbData, NULL); 

SQL Server Native Clientはストリームから読み取り、行を挿入します。フープジャンプが完了しました。 ReleaseAccessor、クリーンアップなどが省略されました。

0

BLOBはバイナリデータなので、バイト配列の形式を使用する必要があります。

+0

申し訳ありませんが、あなたの答えは本当にOLE DBのコンテキストで私を助けません。 –

関連する問題