STDMETHODIMP CCUBRIDRowset::InsertRow(HCHAPTER hReserved, HACCESSOR hAccessor, void *pData, HROW *phRow) { ATLTRACE(atlTraceDBProvider, 2, "CCUBRIDRowset::InsertRow\n"); if(phRow) *phRow = NULL; if(phRow) CHECK_CANHOLDROWS(__uuidof(IRowsetChange)); ClearError(); if(m_nStatus==1) return RaiseError(E_UNEXPECTED, 1, __uuidof(IRowsetChange), L"This object is in a zombie state"); CHECK_UPDATABILITY(DBPROPVAL_UP_INSERT); // Determine if we're in immediate or deferred mode bool bDeferred = IsDeferred(this); // 바인딩 정보를 구함 ATLBINDINGS *pBinding; { bool bFound = m_rgBindings.Lookup((ULONG)hAccessor, pBinding); if(!bFound || pBinding==NULL) return RaiseError(DB_E_BADACCESSORHANDLE, 0, __uuidof(IRowsetChange)); if(!(pBinding->dwAccessorFlags & DBACCESSOR_ROWDATA)) return RaiseError(DB_E_BADACCESSORTYPE, 0, __uuidof(IRowsetChange)); // row accessor 가 아니다. if(pData==NULL && pBinding->cBindings!=0) return RaiseError(E_INVALIDARG, 0, __uuidof(IRowsetChange)); } CCUBRIDRowsetRow *pRow; CCUBRIDRowsetRow::KeyType key; { // 새 row를 위한 Row class를 생성한다. DBORDINAL cCols; ATLCOLUMNINFO *pColInfo = GetColumnInfo(this, &cCols); ATLTRY(pRow = new CCUBRIDRowsetRow(m_uCodepage, -1, cCols, pColInfo, m_spConvert, m_Columns.m_defaultVal)); if(pRow==NULL) return RaiseError(E_OUTOFMEMORY, 0, __uuidof(IRowsetChange)); // row handle에 추가 key = (CCUBRIDRowsetRow::KeyType)m_rgRowData.GetCount(); if (key == 0) key++; while(m_rgRowHandles.Lookup(key)) key++; m_rgRowHandles.SetAt(key, pRow); // rowset에 데이터가 하나 늘었다. // m_rgRowData.SetCount(m_rgRowData.GetCount()+1); } HRESULT hrRead = pRow->ReadData(pBinding, pData, m_uCodepage); if(FAILED(hrRead)) { // m_rgRowData.SetCount(m_rgRowData.GetCount()-1); m_rgRowHandles.RemoveKey(key); delete pRow; return hrRead; } pRow->m_status = DBPENDINGSTATUS_NEW; if(phRow) { // handle을 반환할 때는 참조 카운트를 하나 증가시킨다. pRow->AddRefRow(); *phRow = (HROW)key; } // m_rgBookmarks.Add(pRow->m_iRowset+1); if(!bDeferred) { // 변화를 지금 적용 int hConn = GetSessionPtr()->GetConnection(); UINT uCodepage = GetSessionPtr()->GetCodepage(); HRESULT hr = pRow->WriteData(hConn, uCodepage, GetRequestHandle(), m_strTableName); if(FAILED(hr)) { pRow->ReleaseRow(); if (phRow != NULL) *phRow = NULL; return hr; } pRow->m_status = 0; // or UNCHANGED? if(pRow->m_dwRef==0) { m_rgRowHandles.RemoveKey(key); delete pRow; } DoCommit(this); // commit //// deferred update 모드였다가 immediate update 모드로 바뀌지는 않으므로 //// 다른 NEW 상태의 row는 없다. 즉 m_iRowset을 변경해야 할 row는 없다. //// 새로 추가된 Row의 OID를 읽어들인다. //pRow->ReadData(GetRequestHandle(), true); } return hrRead; }
HRESULT CMultipleResult::GetResult(IUnknown *pUnkOuter, DBRESULTFLAG lResultFlag, REFIID riid, DBROWCOUNT *pcRowsAffected, IUnknown **ppRowset) { ATLTRACE2(atlTraceDBProvider, 2, _T("CMultipleResult::GetResult\n")); HRESULT hr = S_OK; CCUBRIDCommand* cmd = NULL; CCUBRIDRowset* pRowset = NULL; int result_count = 0, rc; T_CCI_CUBRID_STMT cmd_type; T_CCI_ERROR error; ClearError(); error.err_msg[0] = 0; //E_INVALIDARG 처리 if (!(lResultFlag == DBRESULTFLAG_DEFAULT || lResultFlag == DBRESULTFLAG_ROWSET || lResultFlag == DBRESULTFLAG_ROW)) { hr = E_INVALIDARG; goto error; } //E_NOINTERFACE 처리 if (lResultFlag == DBRESULTFLAG_ROWSET) { if (riid == IID_IRow) hr = E_NOINTERFACE; } else if (lResultFlag == DBRESULTFLAG_ROW) { if (riid == IID_IRowset) hr = E_NOINTERFACE; } if ( riid == IID_IRowsetUpdate ) hr = E_NOINTERFACE; else if ( riid == IID_IMultipleResults) hr = E_NOINTERFACE; if (hr == E_NOINTERFACE) goto error; if (pUnkOuter && riid != IID_IUnknown) { hr = DB_E_NOAGGREGATION; goto error; } if (pcRowsAffected) *pcRowsAffected = DB_COUNTUNAVAILABLE; //모든 쿼리가 다 수행되었으면 m_qr 해제 후 DB_S_NORESULT 리턴 if (m_resultIndex > m_numQuery) { ATLTRACE2(atlTraceDBProvider, 2, _T("DB_S_NORESULT\n")); cci_query_result_free(m_qr, m_numQuery); m_qr = NULL; if (ppRowset) *ppRowset = NULL; if (pcRowsAffected) *pcRowsAffected = DB_COUNTUNAVAILABLE; return DB_S_NORESULT; } if (m_bInvalidated) { hr = E_UNEXPECTED; goto error; } //Rowset이 열려있는 경우 DB_E_OBJECTOPEN을 리턴 if (m_command->m_cRowsetsOpen > 0) { hr = DB_E_OBJECTOPEN; goto error; } //다음번 결과셋을 가져온다 if (m_resultIndex != 1 && !m_bCommitted) { rc = cci_next_result(m_hReq, &error); if (rc < 0) { hr = E_FAIL; goto error; } } result_count = CCI_QUERY_RESULT_RESULT(m_qr, m_resultIndex); cmd_type = (T_CCI_CUBRID_STMT)CCI_QUERY_RESULT_STMT_TYPE(m_qr, m_resultIndex); //쿼리 인덱스 증가 m_resultIndex++; //CCUBRIDCommand 객체에 대한 레퍼런스를 받아옴 cmd = m_command; if (riid == IID_IRow) { if (ppRowset && (cmd_type==CUBRID_STMT_SELECT || cmd_type==CUBRID_STMT_GET_STATS || cmd_type==CUBRID_STMT_CALL || cmd_type==CUBRID_STMT_EVALUATE)) { CComPolyObject<CCUBRIDRow> *pRow; HRESULT hr = CComPolyObject<CCUBRIDRow>::CreateInstance(pUnkOuter, &pRow); if(FAILED(hr)) goto error; // 생성된 COM 객체를 참조해서, 실패시 자동 해제하도록 한다. CComPtr<IUnknown> spUnk; hr = pRow->QueryInterface(&spUnk); if(FAILED(hr)) { delete pRow; // 참조되지 않았기 때문에 수동으로 지운다. goto error; } //Command object의 IUnknown을 Row의 Site로 설정한다. CComPtr<IUnknown> spOuterUnk; GetCommandPtr()->QueryInterface(__uuidof(IUnknown), (void **)&spOuterUnk); hr = pRow->m_contained.SetSite(spOuterUnk, CCUBRIDRow::Type::FromCommand); if(FAILED(hr)) goto error; hr = pRow->m_contained.Initialize(m_hReq); if (FAILED(hr)) { if (ppRowset) *ppRowset = NULL; if (pcRowsAffected) *pcRowsAffected = DB_COUNTUNAVAILABLE; return RaiseError(hr, 0, __uuidof(IMultipleResults)); } //생성된 Row 객체의 IRow 인터페이스 반환 hr = pRow->QueryInterface(riid, (void **)ppRowset); if(FAILED(hr)) goto error; //if (result_count > 1) // return DB_S_NOTSINGLETON; } else { if (cmd_type == CUBRID_STMT_SELECT) { if (pcRowsAffected) *pcRowsAffected = -1; } else { if (pcRowsAffected) *pcRowsAffected = result_count; } if (m_resultIndex > m_numQuery) //마지막 쿼리인 경우 { GetSessionPtr()->AutoCommit(this); m_hReq = 0; m_bCommitted = true; } if (ppRowset != NULL) *ppRowset = NULL; } } //IID_IRowset이 아닌 경우 Rowset을 생성 //IID_IRow 처럼 다르게 처리해 주어야 하는 경우가 또 있을까? else { if(cmd_type==CUBRID_STMT_SELECT || cmd_type==CUBRID_STMT_GET_STATS || cmd_type==CUBRID_STMT_CALL || cmd_type==CUBRID_STMT_EVALUATE) { if (ppRowset) { if (riid != IID_NULL) { ATLTRACE2(atlTraceDBProvider, 2, _T("CMultipleResult::CreateRowset\n")); //Rowset object 생성 hr = cmd->CreateRowset<CCUBRIDRowset>(pUnkOuter, riid, m_pParams, pcRowsAffected, ppRowset, pRowset); if (FAILED(hr)) goto error; pRowset->InitFromCommand(m_hReq, result_count); } else *ppRowset = NULL; } } else { if (pcRowsAffected) *pcRowsAffected = result_count; //커밋을 하지 않으므로 다음번 쿼리에서 현재 쿼리의 업데이트 내용을 볼 수 없다!! //마지막 쿼리인 경우는 커밋 if (m_resultIndex > m_numQuery && m_numQuery > 1) //질의수가 2개 이상이면서 마지막 질의인 경우 커밋 { GetSessionPtr()->AutoCommit(this); m_hReq = 0; m_bCommitted = true; } *ppRowset = NULL; } } return S_OK; error: if (ppRowset) *ppRowset = NULL; if (pcRowsAffected) *pcRowsAffected = DB_COUNTUNAVAILABLE; m_hReq = 0; if (strlen(error.err_msg) > 0) return RaiseError(hr, 1, __uuidof(IMultipleResults), error.err_msg); else return RaiseError(hr, 0, __uuidof(IMultipleResults)); }
STDMETHODIMP CCUBRIDRowset::SetData(HROW hRow, HACCESSOR hAccessor, void *pData) { ATLTRACE(atlTraceDBProvider, 2, "CCUBRIDRowset::SetData\n"); ClearError(); if(m_nStatus==1) return RaiseError(E_UNEXPECTED, 1, __uuidof(IRowsetChange), L"This object is in a zombie state"); // cci_cursor_update를 이용하면 질의를 재실행할 때까지 SetData를 실행할 수 없다. // SQL and cci_execute를 이용하면 가능하다. CHECK_RESTART(__uuidof(IRowsetChange)); CHECK_UPDATABILITY(DBPROPVAL_UP_CHANGE); // Determine if we're in immediate or deferred mode bool bDeferred = IsDeferred(this); // Attempt to locate the row in our map CCUBRIDRowsetRow *pRow; { bool bFound = m_rgRowHandles.Lookup((ULONG)hRow, pRow); if(!bFound || pRow==NULL) return RaiseError(DB_E_BADROWHANDLE, 0, __uuidof(IRowsetChange)); } ATLASSERT( pRow->m_iRowset==(ULONG)-1 || pRow->m_iRowset<m_rgRowData.GetCount() ); // 이미 지워진 row if(pRow->m_status==DBPENDINGSTATUS_DELETED || pRow->m_status==DBPENDINGSTATUS_INVALIDROW) return RaiseError(DB_E_DELETEDROW, 0, __uuidof(IRowsetChange)); // 새로 삽입되었고 Storage로 전송됐다. if(pRow->m_iRowset==(ULONG)-1 && pRow->m_status!=DBPENDINGSTATUS_NEW) return DB_E_NEWLYINSERTED; // 바인딩 정보를 구함 ATLBINDINGS *pBinding; { bool bFound = m_rgBindings.Lookup((ULONG)hAccessor, pBinding); if(!bFound || pBinding==NULL) return RaiseError(DB_E_BADACCESSORHANDLE, 0, __uuidof(IRowsetChange)); if(!(pBinding->dwAccessorFlags & DBACCESSOR_ROWDATA)) return RaiseError(DB_E_BADACCESSORTYPE, 0, __uuidof(IRowsetChange)); // row accessor 가 아니다. if(pData==NULL && pBinding->cBindings!=0) return RaiseError(E_INVALIDARG, 0, __uuidof(IRowsetChange)); } HRESULT hr = pRow->ReadData(pBinding, pData, m_uCodepage); if(FAILED(hr)) return hr; // 새로 삽입된 row는 변경이 있어도 그냥 새로 삽입된 것으로 표시 if(pRow->m_status!=DBPENDINGSTATUS_NEW) pRow->m_status = DBPENDINGSTATUS_CHANGED; if(!bDeferred) { // 변화를 지금 적용 int hConn = GetSessionPtr()->GetConnection(); UINT uCodepage = GetSessionPtr()->GetCodepage(); hr = pRow->WriteData(hConn, uCodepage, GetRequestHandle(), m_strTableName); if (hr == DB_E_INTEGRITYVIOLATION) return RaiseError(DB_E_INTEGRITYVIOLATION, 0, __uuidof(IRowsetChange)); if(FAILED(hr)) return RaiseError(DB_E_ERRORSOCCURRED, 0, __uuidof(IRowsetChange)); pRow->m_status = 0; // or UNCHANGED? DoCommit(this); // commit } return hr; // S_OK or DB_S_ERRORSOCCURRED }
HRESULT CMultipleResult::SetSite(IUnknown *pUnkSite) { HRESULT hr = IObjectWithSiteImpl<CMultipleResult>::SetSite(pUnkSite); GetSessionPtr()->RegisterTxnCallback(this, true); return hr; }