Frage

Ich arbeite an einer Anwendung, die ein SQL Server DB OLE DB und SQL Server Native Client für den Zugriff verwendet. Bisher habe ich nur mit relativ einfachen SQL beschäftigt. Dazu habe ich eine ICommandText gewesen zu erhalten und mit SetCommandText. Ich möchte jetzt ein großes Objekt in die Datenbank einzufügen. Ich sehe, dass ICommandStream existiert, aber es scheint, wie dies mit mir würde erfordern eine Klasse hinzufügen, dass Geräte IStream und auch meine BLOB entsprechend zu zitieren (entweichende Apostrophe, etc.). Sicherlich gibt es einen einfacheren Weg?

Side Hinweis: OLE DB war nicht meine Wahl, und ich kann es in diesem Stadium nicht ändern. So den einfacheren Weg „Verwendung etwas höhere Ebene“ ist nicht verfügbar.

War es hilfreich?

Lösung 2

Es stellt sich heraus, gibt es eine Antwort auf dem Microsoft SQLNCLI Team-Blog .

auf diese zu erweitern, hier ist der Code, den ich am Ende mit. Zunächst müssen Sie eine ISequentialStream für SQL Server Native Client aus zu lesen. Ich habe meine Daten im Speicher, so dass ich nur diese mit einem Zeiger auf meine BLOB konstruieren könnte, aber es ist trivial zu gehen und die Daten aus anderen Ländern zu erhalten. Es ist nicht Bestandteil des Vertrages, aber es ist vielleicht nützlich zu wissen, dass die in 1024-Byte-Blöcken passieren liest scheinen. Hier ist meine Stream-Klasse:

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
};

Die Umsetzung dieser ist trivial:

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;
}

Mit einem ICommandText, können Sie dann eine SELECT auf dem Tisch auszuführen. Sie sind nicht eigentlich diese abzurufen gehen alle Daten verwenden, es ist nur ein Weg, um eine IRowsetChange zu bekommen. Ich habe eine zusätzliche ExecuteCommand Methode dafür. Die SQL in PSQL geben wird (ähnlich) SELECT x,y,z FROM TableWithBlob. FAIL ist ein benutzerdefiniertes Makro, dass Datensätze das Problem und kehrt zurück.

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;
}

Ich habe jetzt ein IRowset und ein IRowsetChange für die Tabelle in Frage. Sie bauen dann eine DBBINDING wie gewohnt. Ich eliding dies - es ist nicht wirklich relevant für die Frage. Das entsprechende Bit ist:

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

Wenn anschließend in dem entsprechenden Datenspeicherblock füllt, können Sie dies tun (sorry für die hässlichen Abgüsse):

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;

Holen Sie sich einen IAccessor mit Ihrem IRowsetChange und binden es:

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

Schließlich können Sie Ihre Zeile einfügen:

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

SQL Server Native Client wird von Ihrem Stream lesen und die Zeile einzufügen. Der Reifen-Springen ist nun abgeschlossen. ReleaseAccessor, Reinigung usw. elided.

Andere Tipps

Ein Blob ist nur binäre Daten, so dass Sie irgendeine Form von Byte-Array verwenden, benötigen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top