문제

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, 정리 등.

다른 팁

블로브는 이진 데이터 일 뿐이므로 어떤 형태의 바이트 배열을 사용해야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top