Ole DB로 멍청이 삽입
-
20-09-2019 - |
문제
SQL Server DB에 액세스하기 위해 OLE DB 및 SQL Server 기본 클라이언트를 사용하는 앱에서 작업하고 있습니다. 지금까지 나는 상당히 간단한 SQL 만 다루고 있습니다. 이를 위해, 나는 an을 얻었다 ICommandText
그리고 사용 SetCommandText
. 이제 데이터베이스에 큰 객체를 삽입하고 싶습니다. 내가 볼 ICommandStream
존재하지만 이것을 사용하는 것 같습니다. IStream
또한 내 블로브를 적절하게 인용합니다 (아포스트로피를 탈출하는 등). 더 쉬운 방법이 있습니까?
참고 : OLE DB는 내 선택이 아니 었고이 단계에서는 변경할 수 없습니다. 따라서 "더 높은 수준의 것을 사용"하는 쉬운 방법은 사용할 수 없습니다.
해결책 2
밝혀졌습니다 Microsoft SQLNCLI 팀 블로그에 대한 답변.
이를 확장하기 위해 여기에 내가 사용한 코드가 있습니다. 첫째, SQL Server 기본 클라이언트가 읽을 수있는 Isequentialstream이 필요합니다. 메모리에 데이터가 있으므로 블로브에 대한 포인터로 이것을 구성 할 수는 있지만 다른 곳에서 데이터를 가져 오는 것은 사소한 일입니다. 계약의 일부는 아니지만 독서가 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
, 그런 다음 a를 실행할 수 있습니다 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가 있습니다. 그런 다음 a 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 기본 클라이언트는 스트림에서 읽고 행을 삽입합니다. 후프 점프가 끝났습니다. ReleaseAccessor
, 정리 등.
다른 팁
블로브는 이진 데이터 일 뿐이므로 어떤 형태의 바이트 배열을 사용해야합니다.