Pregunta

Estoy trabajando en una aplicación que está utilizando OLE DB y SQL Server Native Client para acceder a una base de datos SQL Server. Hasta la fecha, sólo he estado tratando con bastante simple SQL. Por esto, yo he sido la obtención de un ICommandText y el uso de SetCommandText. Ahora quiero insertar un objeto grande en la base de datos. Veo que existe ICommandStream, pero parece que el uso de este se me requiere para agregar una clase que implementa IStream y también para citar a mi BLOB adecuadamente (escapar apóstrofes, etc.). Seguramente hay una manera más fácil?

Nota al margen: OLE DB no fue mi elección y no puedo cambiarlo en esta etapa. Así que la forma más fácil "usar algo más alto nivel" no está disponible.

¿Fue útil?

Solución 2

Resulta que hay un respuesta en el blog del equipo de Microsoft SQLNCLI .

Para ampliar sobre esto, aquí está el código que terminé usando. En primer lugar, se necesita un ISequentialStream para SQL Server Native Client para ser la lectura de. Tengo mis datos en la memoria, por lo que sólo podía construir este con un puntero a mi BLOB, pero es trivial para ir a buscar los datos de otros lugares. No es parte del contrato, pero es quizás útil saber que las lecturas parecen tener lugar a trozos de 1024 bytes. Aquí está mi clase de secuencia:

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

La aplicación de esto es 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;
}

El uso de un ICommandText, a continuación, puede ejecutar un SELECT sobre la mesa. Usted no está realmente va a recuperar los datos utilizando este, es sólo una manera de conseguir un IRowsetChange. Tengo un método ExecuteCommand extra por esto. El SQL aprobada en psql es SELECT x,y,z FROM TableWithBlob (similares a). FAIL es una macro personalizada que registra el problema y vuelve.

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

Ahora tengo una IRowset y un IRowsetChange de la tabla en cuestión. A continuación, construir un DBBINDING como lo haría normalmente. Estoy elidiendo esto - en realidad no es relevante para la cuestión. El bit correspondiente es:

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

Cuando posteriormente rellenar el bloque de memoria de datos correspondiente, a continuación, puede hacer esto (lo siento por los elencos feos):

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;

Consígase un IAccessor la utilización de su IRowsetChange y enlazarlo:

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

Por último, puede insertar su consecutivas:

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

SQL Server Native Client leerá de su torrente e insertar la fila. El aro de salto se hace ahora. ReleaseAccessor, limpieza, etc. elidido.

Otros consejos

Una burbuja es sólo datos binarios, por lo que tendrá que utilizar algún tipo de matriz de bytes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top