OLE DB を使用した BLOB の挿入
-
20-09-2019 - |
質問
私は、OLE DB と SQL Server Native Client を使用して SQL Server DB にアクセスするアプリに取り組んでいます。これまで、私はかなり単純な SQL だけを扱ってきました。このために、私は ICommandText
そして使用して SetCommandText
. 。ここで、ラージ オブジェクトをデータベースに挿入したいと考えています。私はそれを見る ICommandStream
存在しますが、これを使用するには、を実装するクラスを追加する必要があるようです IStream
また、BLOB を適切に引用します (アポストロフィのエスケープなど)。確かにもっと簡単な方法がありますか?
サイドノート:OLE DB は私の選択ではなかったので、この段階では変更できません。したがって、「より高いレベルのものを使用する」という簡単な方法は利用できません。
解決 2
結局のところ、 Microsoft SQLNCli チームのブログでの回答.
これをさらに拡張するために、最終的に使用したコードを次に示します。まず、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;
取得してください IAccessor
あなたの IRowsetChange
そしてそれをバインドします:
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
, 、掃除など。省略された。
他のヒント
ブロブは単なるバイナリデータであるので、あなたはバイト配列のいくつかのフォームを使用する必要があります。