إدراج BLOB مع OLE DB
-
20-09-2019 - |
سؤال
وأنا أعمل على التطبيق الذي يستخدم OLE DB و العميل الأصلي SQL Server لوصول خادم SQL DB. حتى الآن، لقد قمت فقط تم التعامل مع SQL بسيطة الى حد كبير. لهذا، لقد كنت الحصول على ICommandText
واستخدام SetCommandText
. أريد الآن أن إدراج كائن كبير في قاعدة البيانات. أرى أن وجود ICommandStream
، ولكن يبدو أن استخدام هذا يتطلب مني أن إضافة فئة أن تنفذ IStream
وأيضا أن أقتبس بلدي BLOB بشكل مناسب (الهروب الفواصل العليا، وما إلى ذلك). بالتأكيد هناك طريقة أسهل؟
ملحوظة جانبية: كان OLE DB لا خياري وأنا لا يمكن تغييره في هذه المرحلة. لذا فإن أسهل طريقة "استخدام شيء أعلى مستوى" غير متوفر.
المحلول 2
وكما تبين، هناك <لأ href = "http://blogs.msdn.com/sqlnativeclient/archive/2008/01/21/sending-blob-data-to-sql-server-using-irowsetfastload- وisequentialstream.aspx "يختلط =" noreferrer نوفولو "> الإجابة على Microsoft SQLNCli فريق بلوق .
لتوسيع حول هذا الموضوع، وهنا رمز انتهى بي الأمر باستخدام. أولا، تحتاج إلى ISequentialStream للعميل الأصلي SQL Server لتكون القراءة من. لدي البيانات الخاصة بي في الذاكرة، حتى أتمكن من مجرد بناء هذا مع مؤشر إلى بلدي 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 إضافية لهذا الغرض. وSQL مرت في pSQL هو (على غرار) 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
كما تفعل عادة. أنا eliding هذا - انها ليست حقا ذات الصلة لهذه المسألة. بت ذات الصلة هي:
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 خادم العميل الأصلي سيقرأ من تيار وإدراج الصف. ويتم القفز طارة الآن. ReleaseAccessor
، تنظيف، الخ elided.
نصائح أخرى
وسائل ليست سوى البيانات الثنائية، لذلك ستحتاج إلى استخدام شكل من أشكال صفيف بايت.