سؤال

وأنا أعمل على التطبيق الذي يستخدم 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.

نصائح أخرى

وسائل ليست سوى البيانات الثنائية، لذلك ستحتاج إلى استخدام شكل من أشكال صفيف بايت.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top