TABLE.CPP
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:64k
源码类别:

Windows编程

开发平台:

Visual C++

  1. //-----------------------------------------------------------------------------
  2. // Microsoft OLE DB TABLECOPY Sample
  3. // Copyright (C) 1995-1998 Microsoft Corporation
  4. //
  5. // @doc
  6. //
  7. // @module TABLE.H
  8. //
  9. //-----------------------------------------------------------------------------
  10. /////////////////////////////////////////////////////////////////////
  11. // Includes
  12. //
  13. /////////////////////////////////////////////////////////////////////
  14. #include "winmain.h"
  15. #include "common.h"
  16. #include "tablecopy.h"
  17. #include "table.h"
  18. #include "wizard.h"
  19. #include "progress.h"
  20. //////////////////////////////////////////////////////////////////////////////
  21. // Defines / Macros
  22. //
  23. //////////////////////////////////////////////////////////////////////////////
  24. #define NO_MATCH 0x0000
  25. #define MATCH_EXACT 0x0001
  26. #define MATCH_TYPE 0x0002
  27. #define MATCH_SIZE 0x0004
  28. #define MATCH_DEFAULT 0x0008
  29. /////////////////////////////////////////////////////////////////
  30. // CTable::CTable
  31. //
  32. /////////////////////////////////////////////////////////////////
  33. CTable::CTable(CWizard* pCWizard)
  34. {
  35. ASSERT(pCWizard);
  36. m_wszIDQuote[0] = EOL;
  37. m_wszIDSeperator[0] = EOL;
  38. //TableInfo
  39. memset(&m_TableInfo, 0, sizeof(TABLEINFO));
  40. m_wszQualTableName[0] = EOL;
  41. //IndexInfo
  42. m_cIndexes = 0; // Count of indexes
  43. m_rgIndexInfo = NULL; // Index information
  44. //ColumnInfo
  45. m_cColumns = 0; // Count of columns
  46. m_rgColDesc = NULL; // Column information
  47. //DataSource
  48. m_pCDataSource  = new CDataSource;
  49. m_pCWizard = pCWizard; // Back pointer to Windowing class
  50. //Rowset
  51. m_pIAccessor = NULL;
  52. m_pIRowset = NULL;
  53. }
  54. /////////////////////////////////////////////////////////////////
  55. // CTable::~CTable
  56. //
  57. /////////////////////////////////////////////////////////////////
  58. CTable::~CTable()
  59. {
  60. delete m_pCDataSource;
  61. SAFE_FREE(m_rgIndexInfo);
  62. SAFE_FREE(m_rgColDesc);
  63. //Rowset
  64. SAFE_RELEASE(m_pIAccessor);
  65. SAFE_RELEASE(m_pIRowset);
  66. }
  67. /////////////////////////////////////////////////////////////////
  68. // BOOL CTable::Connect
  69. //
  70. /////////////////////////////////////////////////////////////////
  71. BOOL CTable::Connect(HWND hWnd, CDataSource* pCDataSource)
  72. {
  73. ASSERT(m_pCDataSource);
  74. if(m_pCDataSource->Connect(hWnd, pCDataSource))
  75. {
  76. //Get LiteralInfo for this table
  77. GetLiteralInfo();
  78. return TRUE;
  79. }
  80. return FALSE;
  81. }
  82. /////////////////////////////////////////////////////////////////
  83. // BOOL CTable::IsConnected
  84. //
  85. /////////////////////////////////////////////////////////////////
  86. BOOL CTable::IsConnected()
  87. {
  88. ASSERT(m_pCDataSource);
  89. return m_pCDataSource->IsConnected();
  90. }
  91. /////////////////////////////////////////////////////////////////
  92. // HRESULT CTable::GetLiteralInfo
  93. //
  94. /////////////////////////////////////////////////////////////////
  95. HRESULT CTable::GetLiteralInfo()
  96. {
  97. ASSERT(m_pCDataSource);
  98. ASSERT(m_pCDataSource->m_pIDBInitialize);
  99. HRESULT hr;
  100. const static ULONG cLiterals = 2;
  101. const static DBLITERAL rgLiterals[cLiterals] = {DBLITERAL_QUOTE, DBLITERAL_CATALOG_SEPARATOR};
  102. IDBInfo* pIDBInfo = NULL;
  103. ULONG cLiteralInfo = 0;
  104. DBLITERALINFO* rgLiteralInfo = NULL;
  105. WCHAR* pwszCharBuffer = NULL;
  106. //Reset Info
  107. m_wszIDQuote[0] = EOL;
  108. m_wszIDSeperator[0] = EOL;
  109. //Obtain IDBInfo interface
  110. //Some providers may not support IDBInfo so don't display dialog
  111. QTESTC(hr = m_pCDataSource->m_pIDBInitialize->QueryInterface(IID_IDBInfo, (void **)&pIDBInfo));
  112. //GetLiteralInfo
  113. //Can return an error for unsupported literals
  114. hr = pIDBInfo->GetLiteralInfo(cLiterals, rgLiterals, &cLiteralInfo, &rgLiteralInfo, &pwszCharBuffer);
  115.    
  116. //DBLITERAL_QUOTE
  117. if(rgLiteralInfo && rgLiteralInfo[0].fSupported) 
  118. wcscpy(m_wszIDQuote, rgLiteralInfo[0].pwszLiteralValue);
  119. //DBLITERAL_CATALOG_SEPARATOR
  120. if(rgLiteralInfo && rgLiteralInfo[1].fSupported) 
  121. wcscpy(m_wszIDSeperator, rgLiteralInfo[1].pwszLiteralValue);
  122. CLEANUP:
  123. SAFE_RELEASE(pIDBInfo);
  124. SAFE_FREE(rgLiteralInfo);
  125. SAFE_FREE(pwszCharBuffer);
  126. return hr;
  127. }
  128. /////////////////////////////////////////////////////////////////
  129. // BOOL CTable::GetQuotedID
  130. //
  131. /////////////////////////////////////////////////////////////////
  132. BOOL CTable::GetQuotedID(WCHAR* pwszOutBuff, WCHAR* pwszInBuff)
  133. {
  134. WCHAR pwsz[MAX_NAME_LEN*2];
  135. WCHAR* pwszItr = pwsz;
  136. ULONG cQuoteLen = wcslen(m_wszIDQuote);
  137. ULONG cSepLen = wcslen(m_wszIDSeperator);
  138. ULONG cPeriodLen= wcslen(wsz_PERIOD);
  139. //No-op case
  140. //If the provider doesn't have a delimiter, then just use input name.
  141. if(cQuoteLen == 0)
  142. {
  143. wcscpy(pwszOutBuff, pwszInBuff);
  144. return (TRUE);
  145. }
  146. //Put on front delimeter
  147. wcscpy(pwszItr, m_wszIDQuote);
  148. pwszItr += cQuoteLen;
  149. //Copy from Source to Temp
  150. while(*pwszInBuff!=EOL)
  151. {
  152. //If we have a seperator char, we need to quote both pieces
  153. if(wcsncmp(pwszInBuff, m_wszIDSeperator, cSepLen)==0)
  154. {
  155. // "nstl@odbc.authors -> "nstl"@"odbc.authors"
  156. wcscat(pwszItr, m_wszIDQuote); //Ending Quote
  157. wcscat(pwszItr, m_wszIDSeperator); //Seperator
  158. wcscat(pwszItr, m_wszIDQuote); //Beginning Quote
  159. pwszItr += cQuoteLen + cSepLen + cQuoteLen;
  160. }
  161. else if(*pwszInBuff == L'.')
  162. {
  163. // "nstl@odbc.authors -> "nstl@odbc"."authors"
  164. wcscat(pwszItr, m_wszIDQuote); //Ending Quote
  165. wcscat(pwszItr, wsz_PERIOD); //Period
  166. wcscat(pwszItr, m_wszIDQuote); //Beginning Quote
  167. pwszItr += cQuoteLen + cPeriodLen + cQuoteLen;
  168. }
  169. else
  170. {
  171. *pwszItr = *pwszInBuff; //Actual Character
  172. pwszItr++;
  173. }
  174. pwszInBuff++;
  175. *pwszItr = EOL;
  176. }
  177. //Put on the Tail delimeter
  178. wcscat(pwszItr, m_wszIDQuote);
  179. //Give back to the user
  180. wcscpy(pwszOutBuff, pwsz);
  181. return TRUE;
  182. }
  183. /////////////////////////////////////////////////////////////////
  184. // HRESULT CTable::GetTypeNameAndParams
  185. //
  186. /////////////////////////////////////////////////////////////////
  187. HRESULT CTable::GetTypeNameAndParams(ULONG iCol, WCHAR* pwszName)
  188. {
  189. ASSERT(iCol < m_cColumns);
  190. ASSERT(pwszName);
  191. WCHAR  wszBuffer[MAX_NAME_LEN];  // Buffer
  192. // Add CreateParams precision and scale information
  193. wszBuffer[0] = EOL;
  194. if(m_rgColDesc[iCol].ulCreateParams & CP_PRECISION && m_rgColDesc[iCol].ulCreateParams & CP_SCALE)
  195. {
  196. swprintf(wszBuffer, L"(%lu,%lu)", COLINFO_SIZE(m_rgColDesc[iCol]), m_rgColDesc[iCol].bScale);
  197. }
  198. else if(m_rgColDesc[iCol].ulCreateParams & CP_PRECISION || m_rgColDesc[iCol].ulCreateParams & CP_LENGTH || m_rgColDesc[iCol].ulCreateParams & CP_MAXLENGTH)
  199. {
  200. swprintf(wszBuffer, L"(%lu)", COLINFO_SIZE(m_rgColDesc[iCol]));
  201. }
  202. //Add the ColumnType
  203. //Check for "()" in the typename, indicating creation params
  204. //are required at some other position besides the end, ie: "numeric() identity"
  205. pwszName[0] = EOL;
  206. if(wcsstr(m_rgColDesc[iCol].wszTypeName, L"()"))
  207. {
  208. // Add the ColumnType, upto the '()'
  209. wcsncat(pwszName, m_rgColDesc[iCol].wszTypeName, wcsstr(m_rgColDesc[iCol].wszTypeName, L"()") - m_rgColDesc[iCol].wszTypeName);
  210. // Add the precision and scale information
  211. wcscat(pwszName, wszBuffer);
  212. // Add the ColumnType, after the '()'
  213. wcscat(pwszName, wcsstr(m_rgColDesc[iCol].wszTypeName, L"()") + 2);
  214. }
  215. else
  216. {
  217. // Add the ColumnType
  218. wcscpy(pwszName, m_rgColDesc[iCol].wszTypeName);
  219. // If required, add the precision and scale information
  220. wcscat(pwszName, wszBuffer);
  221. }
  222. return S_OK;
  223. }
  224. /////////////////////////////////////////////////////////////////
  225. // HRESULT CTable::GetColInfo
  226. //
  227. /////////////////////////////////////////////////////////////////
  228. HRESULT CTable::GetColInfo(DWORD dwInsertOpt)
  229. {
  230. HRESULT hr;
  231. ULONG i,cColumns = 0;
  232. DBCOLUMNINFO* rgColInfo = NULL;
  233. WCHAR* rgStringBuffer = NULL;
  234. IColumnsInfo* pIColumnsInfo = NULL;
  235. //Obtain the rowset (m_pIRowset);
  236. QTESTC(hr = GetRowset(dwInsertOpt));
  237. CHECKC(m_pIRowset);
  238. //Now finally GetColInfo
  239. XTESTC(hr = m_pIRowset->QueryInterface(IID_IColumnsInfo, (void **)&pIColumnsInfo));
  240. XTESTC(hr = pIColumnsInfo->GetColumnInfo(&cColumns, &rgColInfo, &rgStringBuffer));
  241. //Alloc room for the COLDESC
  242. SAFE_FREE(m_rgColDesc);
  243. SAFE_ALLOC(m_rgColDesc, COLDESC, cColumns);
  244. memset(m_rgColDesc, 0, cColumns*sizeof(COLDESC));
  245. //Loop through the ColInfo and Copy to our ColDesc
  246. m_cColumns = 0;
  247. for(i=0; i<cColumns; i++)
  248. {
  249. //ignore Bookmark columns
  250. if(rgColInfo[i].iOrdinal == 0)
  251. continue;
  252. //DBCOLUMNINFO pwszName
  253. if(rgColInfo[i].pwszName==NULL)
  254. {
  255.     //Although ColInfo is allowed to return NULL for unknown column names
  256. //TableCopy needs column names for many operations, 
  257. //(CREATE TABLE, INSERT, INDEXES), so just "generate" one
  258. swprintf(m_rgColDesc[m_cColumns].wszColName, L"Unknown%d", i);
  259. }
  260. else
  261. {
  262. wcscpy(m_rgColDesc[m_cColumns].wszColName, rgColInfo[i].pwszName);
  263. }
  264. //Now copy the rest of the info
  265. m_rgColDesc[m_cColumns].iOrdinal = rgColInfo[i].iOrdinal;
  266. m_rgColDesc[m_cColumns].ulColumnSize = rgColInfo[i].ulColumnSize;
  267. m_rgColDesc[m_cColumns].wType = rgColInfo[i].wType;
  268. m_rgColDesc[m_cColumns].dwFlags = rgColInfo[i].dwFlags;
  269. m_rgColDesc[m_cColumns].bPrecision = rgColInfo[i].bPrecision;
  270. m_rgColDesc[m_cColumns].bScale = rgColInfo[i].bScale;
  271. m_cColumns++;
  272. }
  273. CLEANUP:
  274. SAFE_RELEASE(pIColumnsInfo); 
  275. SAFE_FREE(rgColInfo);
  276. SAFE_FREE(rgStringBuffer);
  277. return hr;
  278. }
  279. /////////////////////////////////////////////////////////////////
  280. // HRESULT CTable::GetTypeInfo
  281. //
  282. /////////////////////////////////////////////////////////////////
  283. HRESULT CTable::GetTypeInfo()
  284. {
  285. ASSERT(m_pCDataSource);
  286. HRESULT hr;
  287. IRowset* pIRowset = NULL;
  288. IAccessor* pIAccessor = NULL;
  289. HACCESSOR hAccessor = DB_NULL_HACCESSOR;
  290. ULONG i;
  291. ULONG cRowsObtained = 0;
  292. HROW* rghRows = NULL;
  293. //Current SchemaInfo for each type, until we find the correct match
  294. TYPEINFO TypeInfo;
  295. TYPEINFO* rgTypeInfo = NULL;
  296. ULONG* rgMatch = NULL;
  297. //Arrays to store best TypeInfo
  298. SAFE_ALLOC(rgTypeInfo, TYPEINFO, m_cColumns);
  299. SAFE_ALLOC(rgMatch, ULONG, m_cColumns);
  300. memset(rgMatch, NO_MATCH, m_cColumns*sizeof(ULONG));
  301. //Get ProviderTypes rowset IDBSchemaRowset
  302. QTESTC(hr = GetTypeInfoRowset(&pIAccessor, &hAccessor, &pIRowset));
  303. //Loop over all the Schema TypeInfo rowset
  304. //And match up with ColInfo
  305. while(TRUE)
  306. {
  307. XTESTC(hr = pIRowset->GetNextRows(NULL,0,MAX_BLOCK_SIZE,&cRowsObtained, &rghRows));
  308. //ENDOFROWSET
  309. if(cRowsObtained==0)
  310. break;
  311. //Loop over the BLOCK of rows obtained
  312. for(i=0; i<cRowsObtained; i++)
  313. {
  314. //Reset all the TypeInfo fields
  315. memset(&TypeInfo, 0, sizeof(TYPEINFO));
  316. //Put the data for one type into the TypeInfo Struct
  317. XTESTC(hr = pIRowset->GetData(rghRows[i],hAccessor, (void*)&TypeInfo));
  318. //Loop over all the columns and see if they match this type
  319. for(ULONG iCol=0; iCol<m_cColumns; iCol++)
  320. {
  321. ASSERT(m_rgColDesc); 
  322. //Only try matching if this is the correct type and
  323. //the column doesn't already have a perfect match
  324. if(TypeInfo.wType != m_rgColDesc[iCol].wType || (rgMatch[iCol] & MATCH_EXACT))
  325. continue;
  326. //An NonAutoInc Column cannot be placed into an AutoInc type
  327. if(TypeInfo.fIsAutoInc && !m_rgColDesc[iCol].fIsNullable)
  328. continue;
  329. //A Nullable type cannot be mapped to a non-Nullable type
  330. if(m_rgColDesc[iCol].fIsNullable && !TypeInfo.fIsNullable)
  331. continue;
  332. //If never matched before, we at least know they match by type
  333. if(!rgMatch[iCol])
  334. {
  335. rgMatch[iCol] |= MATCH_TYPE;
  336. rgTypeInfo[iCol] = TypeInfo;
  337. }
  338. // Exact type/size matches take precedence
  339.   if(COLINFO_SIZE(m_rgColDesc[iCol]) == TypeInfo.ulColumnSize)
  340. {
  341. rgMatch[iCol] |= MATCH_EXACT;
  342. rgTypeInfo[iCol] = TypeInfo;
  343. }
  344. // Otherwise try best fit size
  345. if(COLINFO_SIZE(m_rgColDesc[iCol]) < TypeInfo.ulColumnSize &&
  346. TypeInfo.ulColumnSize <= rgTypeInfo[iCol].ulColumnSize)
  347. {
  348. rgMatch[iCol] |= MATCH_SIZE;
  349. rgTypeInfo[iCol] = TypeInfo;
  350. }
  351. }
  352. }
  353. //Release this group of rows
  354. XTESTC(hr = pIRowset->ReleaseRows(cRowsObtained,rghRows,NULL,NULL,NULL));
  355. SAFE_FREE(rghRows);
  356. }
  357. //Now that we have the TypeInfo matched, fill in our ColDesc struct
  358. for(i=0; i<m_cColumns; i++) 
  359. {
  360. ASSERT(m_rgColDesc); 
  361. if(rgMatch[i])
  362. {
  363. //TYPEINFO
  364. wcscpy(m_rgColDesc[i].wszTypeName, rgTypeInfo[i].wszTypeName);
  365. m_rgColDesc[i].ulCreateParams = GetCreateParams(rgTypeInfo[i].wszCreateParams);
  366. m_rgColDesc[i].fIsNullable = rgTypeInfo[i].fIsNullable == VARIANT_TRUE;
  367. m_rgColDesc[i].fIsAutoInc = rgTypeInfo[i].fIsAutoInc == VARIANT_TRUE;
  368. }
  369. else
  370. {
  371. wMessageBox(NULL, MB_TASKMODAL | MB_ICONEXCLAMATION | MB_OK, wsz_ERROR, 
  372. wsz_NO_TYPE_FOUND_, GetDBTypeName(m_rgColDesc[i].wType));
  373. }
  374. }
  375. CLEANUP:
  376. if(hAccessor && pIAccessor)
  377. XTEST(pIAccessor->ReleaseAccessor(hAccessor,NULL));
  378. SAFE_RELEASE(pIAccessor);
  379. SAFE_RELEASE(pIRowset);
  380. SAFE_FREE(rgMatch);
  381. SAFE_FREE(rgTypeInfo);
  382. SAFE_FREE(rghRows);
  383. return hr;
  384. }
  385. /////////////////////////////////////////////////////////////////
  386. // HRESULT CTable::MapTableInfo
  387. //
  388. /////////////////////////////////////////////////////////////////
  389. HRESULT CTable::MapTableInfo(CTable* pCSourceTable)
  390. {
  391. ASSERT(pCSourceTable);
  392. ASSERT(m_pCDataSource);
  393. HRESULT hr;
  394. IAccessor* pIAccessor = NULL;
  395. HACCESSOR  hAccessor = DB_NULL_HACCESSOR;
  396. IRowset* pIRowset = NULL;
  397. ULONG cRowsObtained = 0;
  398. HROW* rghRows = NULL;
  399. //Match bitmask, indicating what type of match was found
  400. BOOL fMatchedAll = FALSE;
  401. TYPEINFO TypeInfo;
  402. TYPEINFO* rgTypeInfo = NULL;
  403. ULONG* rgMatch = NULL;
  404. //ColumnInfo
  405. m_cColumns = pCSourceTable->m_cColumns;
  406. SAFE_FREE(m_rgColDesc);
  407. SAFE_ALLOC(m_rgColDesc, COLDESC, m_cColumns);
  408. memcpy(m_rgColDesc, pCSourceTable->m_rgColDesc, m_cColumns * sizeof(COLDESC));
  409. //IndexInfo
  410. m_cIndexes = pCSourceTable->m_cIndexes;
  411. SAFE_FREE(m_rgIndexInfo);
  412. SAFE_ALLOC(m_rgIndexInfo, INDEXINFO, m_cIndexes);
  413. memcpy(m_rgIndexInfo, pCSourceTable->m_rgIndexInfo, m_cIndexes * sizeof(INDEXINFO));
  414. //Arrays to store best TypeInfo
  415. SAFE_ALLOC(rgTypeInfo, TYPEINFO, m_cColumns);
  416. SAFE_ALLOC(rgMatch, ULONG, m_cColumns);
  417. memset(rgMatch, NO_MATCH, m_cColumns*sizeof(ULONG));
  418. //Get ProviderTypes rowset IDBSchemaRowset
  419. QTESTC(hr = GetTypeInfoRowset(&pIAccessor, &hAccessor, &pIRowset));
  420. //Loop until all types are matched.
  421. //We may not find a match, which promotes the type to the next higher
  422. //type, which will require another cycle to match that type...
  423. while(!fMatchedAll) 
  424. {
  425. //Get data for each row in rowset
  426. XTESTC(hr = pIRowset->RestartPosition(NULL));
  427. //Loops over the entire SchemaRowset
  428. while(TRUE)
  429. {
  430. XTESTC(hr = pIRowset->GetNextRows(NULL, 0, MAX_BLOCK_SIZE, &cRowsObtained, &rghRows));
  431. //ENDOFROWSET
  432. if(cRowsObtained == 0)
  433. break;
  434. //Loop over the BLOCK of rows obtained
  435. for(ULONG i=0; i<cRowsObtained; i++)
  436. {
  437. ASSERT(m_rgColDesc); 
  438. //Reset all the TypeInfo fields
  439. memset(&TypeInfo, 0, sizeof(TYPEINFO));
  440. //Put the data for one type into the TypeInfo Struct
  441. XTESTC(hr = pIRowset->GetData(rghRows[i], hAccessor, (void *)&TypeInfo));
  442. //Loop over the columns and get TypeInfo
  443. for(ULONG iCol=0; iCol<m_cColumns; iCol++)
  444. {
  445. //Only try matching if this is the correct type and
  446. //the column doesn't already have a perfect match
  447. if(TypeInfo.wType != m_rgColDesc[iCol].wType || (rgMatch[iCol] & MATCH_EXACT)) 
  448. continue;
  449. //An NonAutoInc Column cannot be placed into an AutoInc type
  450. if(TypeInfo.fIsAutoInc && !m_rgColDesc[iCol].fIsNullable)
  451. continue;
  452. //A Nullable type cannot be mapped to a non-Nullable type
  453. if(m_rgColDesc[iCol].fIsNullable && !TypeInfo.fIsNullable)
  454. continue;
  455. //If never matched before, we at least know they match by type
  456. if(!rgMatch[iCol])
  457. {
  458. rgMatch[iCol] |= MATCH_TYPE;
  459. rgTypeInfo[iCol] = TypeInfo;
  460. }
  461. // Exact type/size matches take precedence
  462. if(COLINFO_SIZE(m_rgColDesc[iCol]) == TypeInfo.ulColumnSize)
  463. {
  464. rgMatch[iCol] |= MATCH_EXACT;
  465. rgTypeInfo[iCol] = TypeInfo;
  466. }
  467. // Otherwise try best fit size
  468. if(COLINFO_SIZE(m_rgColDesc[iCol]) < TypeInfo.ulColumnSize &&
  469. TypeInfo.ulColumnSize <= rgTypeInfo[iCol].ulColumnSize)
  470. {
  471. rgMatch[iCol] |= MATCH_SIZE;
  472. rgTypeInfo[iCol] = TypeInfo;
  473. }
  474. }
  475. }
  476. //Release this group of rows
  477. XTESTC(hr = pIRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL));
  478. SAFE_FREE(rghRows);
  479. }
  480. // See if every type has a match
  481. fMatchedAll = TRUE;
  482. for(ULONG i=0; i<m_cColumns; i++) 
  483. {
  484. ASSERT(m_rgColDesc); 
  485. // If not we will have to promote a type and try again
  486. if(rgMatch[i])
  487. {
  488. //If found a match fill in the TypeInfo fileds of our ColDesc
  489. wcscpy(m_rgColDesc[i].wszTypeName, rgTypeInfo[i].wszTypeName);
  490. m_rgColDesc[i].ulCreateParams = GetCreateParams(rgTypeInfo[i].wszCreateParams);
  491. m_rgColDesc[i].fIsNullable = rgTypeInfo[i].fIsNullable == VARIANT_TRUE;
  492. m_rgColDesc[i].fIsAutoInc = rgTypeInfo[i].fIsAutoInc == VARIANT_TRUE;
  493. //TODO: why is this here?
  494. if (m_rgColDesc[i].ulColumnSize > rgTypeInfo[i].ulColumnSize)
  495. m_rgColDesc[i].ulColumnSize = rgTypeInfo[i].ulColumnSize;
  496. }
  497. else
  498. {
  499. fMatchedAll = FALSE;
  500. //Try to promote it to the next largest type
  501. if(!GetPromotedType(&m_rgColDesc[i].wType)) 
  502. {
  503. //If unable to promote, we are out of luck
  504. wMessageBox(NULL, MB_TASKMODAL | MB_ICONEXCLAMATION | MB_OK, wsz_ERROR, 
  505. wsz_NO_TYPE_MATCH_, GetDBTypeName(m_rgColDesc[i].wType));
  506. goto CLEANUP;
  507. }
  508. }
  509. }
  510. }
  511. CLEANUP:
  512. if(hAccessor && pIAccessor)
  513. XTEST(pIAccessor->ReleaseAccessor(hAccessor, NULL));
  514. SAFE_RELEASE(pIAccessor);
  515. SAFE_RELEASE(pIRowset);
  516. SAFE_FREE(rgMatch);
  517. SAFE_FREE(rgTypeInfo);
  518. SAFE_FREE(rghRows);
  519. return hr;
  520. }
  521. /////////////////////////////////////////////////////////////////
  522. // HRESULT CTable::CreateTable
  523. //
  524. /////////////////////////////////////////////////////////////////
  525. HRESULT CTable::CreateTable()
  526. {
  527. ASSERT(m_pCDataSource);
  528. ASSERT(m_pCDataSource->m_pICommandText || m_pCDataSource->m_pIOpenRowset);
  529. HRESULT hr;
  530. BSTR bstrSqlState = NULL;
  531. WCHAR wszSqlStmt[MAX_QUERY_LEN]; // Create table statement
  532. ULONG cRecords = 0;
  533. IErrorRecords* pIErrorRecords = NULL;
  534. ICommandText* pICommandText = NULL;
  535. DBCOLUMNDESC* rgColumnDesc = NULL;
  536. //Release any existing rowsets
  537. SAFE_RELEASE(m_pIRowset);
  538. SAFE_RELEASE(m_pIAccessor);
  539. //If ITableDefinition is supported by the provider, use it by default.
  540. if(m_pCDataSource->m_pITableDefinition)
  541. {
  542. DBID TableID;
  543. //Get DBCOLUMNDESC info
  544. QTESTC(hr = GetColumnDesc(&rgColumnDesc));
  545. //Create TableID
  546. TableID.eKind = DBKIND_NAME;
  547. TableID.uName.pwszName = wszSqlStmt;
  548. GetQuotedID(wszSqlStmt, m_wszQualTableName);
  549. //ITableDefinition::CreateTable
  550. hr = m_pCDataSource->m_pITableDefinition->CreateTable(NULL, &TableID, m_cColumns, rgColumnDesc, IID_NULL, NULL, NULL, NULL, NULL);
  551. //If table already exists, offer to drop it
  552. if(hr == DB_E_DUPLICATETABLEID)
  553. {
  554. //If the user doesn't wants to drop it, exit
  555. if(IDNO == wMessageBox(NULL, MB_TASKMODAL | MB_ICONQUESTION | MB_YESNO, wsz_ERROR, 
  556. wsz_ASK_DROP_TABLE_, m_pCDataSource->m_pwszTableTerm, m_wszQualTableName)) 
  557. goto CLEANUP;
  558. //Otherwise drop that table and continue
  559. XTESTC(hr = m_pCDataSource->m_pITableDefinition->DropTable(&TableID));
  560. XTESTC(hr = m_pCDataSource->m_pITableDefinition->CreateTable(NULL, &TableID, m_cColumns, rgColumnDesc, IID_NULL, NULL, NULL, NULL, NULL));
  561. }
  562. else
  563. {
  564. //Some other failure
  565. //Display Extended ErrorInfo
  566. XTESTC(hr);
  567. }
  568. }
  569. // Otherwise use SQL to create the table.
  570. else if(m_pCDataSource->m_pICommandText)
  571. {
  572. // Setup the initialize CREATE TABLE '<CTableName>'
  573. CreateSQLStmt(ESQL_CREATE_TABLE, wszSqlStmt);
  574. // Set the Command Text
  575. pICommandText = m_pCDataSource->m_pICommandText;
  576. XTESTC(hr = pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));
  577. // Execute the command
  578. hr = pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL);
  579. // If this didn't work, then we need to display messages, and if the
  580. // error was a duplicate table, offer to drop.
  581. if(FAILED(hr)) 
  582. {
  583. //Get the Error Records, need to save them, since every call
  584. //cleans the previous error objects
  585. QTESTC(GetErrorRecords(&cRecords, &pIErrorRecords));
  586. //If Error was due to an existing table, just ask to drop it
  587. if(GetSqlErrorInfo(0, pIErrorRecords, &bstrSqlState)==S_OK && 
  588. bstrSqlState && wcscmp(bstrSqlState, L"S0001")==0) 
  589. {
  590. WCHAR  wszBuffer[MAX_QUERY_LEN];
  591. //If the user doesn't wants to drop it, exit
  592. if(IDNO == wMessageBox(NULL, MB_TASKMODAL | MB_ICONQUESTION | MB_YESNO, wsz_ERROR, 
  593. wsz_ASK_DROP_TABLE_, m_pCDataSource->m_pwszTableTerm, m_wszQualTableName)) 
  594. goto CLEANUP;
  595. //Otherwise drop that table and continue
  596. CreateSQLStmt(ESQL_DROP_TABLE, wszBuffer);
  597. //Drop the existing Table
  598. XTESTC(hr = pICommandText->SetCommandText(DBGUID_DBSQL, wszBuffer));
  599. XTESTC(hr = pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL));
  600. //Now reset the CreateTable text to the SqlStmt and Execute
  601. XTESTC(hr = pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));
  602. XTESTC(hr = pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL));
  603. }
  604. else
  605. {
  606. //Otherwsie unknown error, just display it to the user
  607. DisplayErrorRecords(NULL, cRecords, pIErrorRecords);
  608. }
  609. }
  610. }
  611. CLEANUP:
  612. SAFE_SYSFREE(bstrSqlState);
  613. SAFE_RELEASE(pIErrorRecords);
  614. //Free DBCOLUMNDESC
  615. if(rgColumnDesc)
  616. {
  617. for(ULONG i=0; i<m_cColumns && rgColumnDesc; i++)
  618. FreeProperties(rgColumnDesc[i].cPropertySets, rgColumnDesc[i].rgPropertySets);
  619. SAFE_FREE(rgColumnDesc);
  620. }
  621. return hr;
  622. }
  623. /////////////////////////////////////////////////////////////////
  624. // HRESULT CTable::CopyIndexes
  625. //
  626. /////////////////////////////////////////////////////////////////
  627. HRESULT CTable::CopyIndexes(CTable* pCTable)
  628. {
  629. ASSERT(pCTable);
  630. HRESULT hr;
  631. WCHAR wszBuffer[MAX_NAME_LEN];
  632. WCHAR wszSqlStmt[MAX_QUERY_LEN];
  633. ULONG i;
  634. // Don't waste time if there aren't any indexes.
  635. if (m_cIndexes == 0)
  636. return (S_OK);
  637. //Copy Index Info from Source table
  638. m_cIndexes = pCTable->m_cIndexes;
  639. ULONG cIndexColumnDescs = 0;
  640. DBINDEXCOLUMNDESC* rgIndexColumnDescs = NULL;
  641. DBID* rgDBIDs = NULL;
  642. //Array to indicate which index/columns we have used
  643. ULONG* rgIndexUsed = NULL;
  644. SAFE_ALLOC(rgIndexUsed, ULONG, m_cIndexes);
  645. memset(rgIndexUsed, 0, m_cIndexes * sizeof(ULONG));
  646. SAFE_FREE(m_rgIndexInfo);
  647. SAFE_ALLOC(m_rgIndexInfo, INDEXINFO, m_cIndexes);
  648. memcpy(m_rgIndexInfo, pCTable->m_rgIndexInfo, m_cIndexes * sizeof(INDEXINFO));
  649. //Alloc DBINDEXCOLUMNDESC
  650. SAFE_ALLOC(rgIndexColumnDescs, DBINDEXCOLUMNDESC, m_cColumns);
  651. SAFE_ALLOC(rgDBIDs, DBID, m_cColumns);
  652. //Use IIndexDefinition::CreateIndex if supported
  653. if(m_pCDataSource->m_pIIndexDefinition)
  654. {
  655. //Create TableID
  656. DBID TableID;
  657. TableID.eKind = DBKIND_NAME;
  658. GetQuotedID(wszSqlStmt, m_wszQualTableName);
  659. TableID.uName.pwszName = wszSqlStmt;
  660. // Loop around each index that is valid.  See if any are to be created
  661. for(i=0; i<m_cIndexes; i++) 
  662. {
  663. //If this index has already been created, skip
  664. //might have been used in another index creation
  665. if(rgIndexUsed[i])
  666. continue;
  667. //If this index is used as a primary key, skip
  668. //PrimaryKeys are taken care of differently
  669. if(m_rgIndexInfo[i].fIsPrimaryKey)
  670. continue;
  671. //Create IndexID
  672. DBID IndexID;
  673. IndexID.eKind = DBKIND_NAME;
  674. GetQuotedID(wszBuffer, m_rgIndexInfo[i].wszIndexName);
  675. IndexID.uName.pwszName = wszBuffer;
  676. // Now loop through all columns that belong to this index
  677. cIndexColumnDescs = 0;
  678. for(ULONG iCol=i; iCol<m_cIndexes; iCol++) 
  679. {
  680. //If not the same index skip
  681. if(wcscmp(m_rgIndexInfo[i].wszIndexName, m_rgIndexInfo[iCol].wszIndexName)!=0)
  682. continue;
  683. //mark this Index as used
  684. rgIndexUsed[iCol] = TRUE;
  685. //DBINDEXCOLUMNDESC info
  686. rgIndexColumnDescs[cIndexColumnDescs].pColumnID = &rgDBIDs[cIndexColumnDescs];
  687. rgIndexColumnDescs[cIndexColumnDescs].pColumnID->eKind = DBKIND_NAME;
  688. rgIndexColumnDescs[cIndexColumnDescs].pColumnID->uName.pwszName = m_rgIndexInfo[iCol].wszColName;
  689. //Indicate column order
  690. rgIndexColumnDescs[cIndexColumnDescs].eIndexColOrder = (m_rgIndexInfo[iCol].dwCollation == DB_COLLATION_DESC) ? DBINDEX_COL_ORDER_DESC : DBINDEX_COL_ORDER_ASC;
  691. cIndexColumnDescs++;
  692. }
  693. //Now Setup Index Properties
  694. ULONG cPropSets = 0;
  695. DBPROPSET* rgPropSets = NULL;
  696. //DBPROP_INDEX_AUTOUPDATE
  697. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_AUTOUPDATE, DBPROPSET_INDEX))
  698. SetProperty(DBPROP_INDEX_AUTOUPDATE, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_BOOL, m_rgIndexInfo[i].fAutoUpdate);
  699. //DBPROP_INDEX_CLUSTERED
  700. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_CLUSTERED, DBPROPSET_INDEX))
  701. SetProperty(DBPROP_INDEX_CLUSTERED, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_BOOL, m_rgIndexInfo[i].fClustered);
  702. //DBPROP_INDEX_FILLFACTOR
  703. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_FILLFACTOR, DBPROPSET_INDEX))
  704. SetProperty(DBPROP_INDEX_FILLFACTOR, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_I4, m_rgIndexInfo[i].dwFillFactor);
  705. //DBPROP_INDEX_INITIALSIZE
  706. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_INITIALSIZE, DBPROPSET_INDEX))
  707. SetProperty(DBPROP_INDEX_INITIALSIZE, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_I4, m_rgIndexInfo[i].dwInitialSize);
  708. //DBPROP_INDEX_NULLCOLLATION
  709. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_NULLCOLLATION, DBPROPSET_INDEX))
  710. SetProperty(DBPROP_INDEX_NULLCOLLATION, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_I4, m_rgIndexInfo[i].dwNullCollation);
  711. //DBPROP_INDEX_NULLS
  712. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_NULLS, DBPROPSET_INDEX))
  713. SetProperty(DBPROP_INDEX_NULLS, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_I4, m_rgIndexInfo[i].dwNulls);
  714. //DBPROP_INDEX_PRIMARYKEY
  715. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_PRIMARYKEY, DBPROPSET_INDEX))
  716. SetProperty(DBPROP_INDEX_PRIMARYKEY, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_BOOL, m_rgIndexInfo[i].fIsPrimaryKey);
  717. //DBPROP_INDEX_SORTBOOKMARKS
  718. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_SORTBOOKMARKS, DBPROPSET_INDEX))
  719. SetProperty(DBPROP_INDEX_SORTBOOKMARKS, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_BOOL, m_rgIndexInfo[i].fSortBookmarks);
  720. //DBPROP_INDEX_TEMPINDEX
  721. // if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_TEMPINDEX, DBPROPSET_INDEX))
  722. // SetProperty(DBPROP_INDEX_TEMPINDEX, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_BOOL, m_rgIndexInfo[i].fTempIndex);
  723. //DBPROP_INDEX_TYPE
  724. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_TYPE, DBPROPSET_INDEX))
  725. SetProperty(DBPROP_INDEX_TYPE, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_I4, m_rgIndexInfo[i].wType);
  726. //DBPROP_INDEX_UNIQUE
  727. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_INDEX_UNIQUE, DBPROPSET_INDEX))
  728. SetProperty(DBPROP_INDEX_UNIQUE, DBPROPSET_INDEX, &cPropSets, &rgPropSets, DBTYPE_BOOL, m_rgIndexInfo[i].fUnique);
  729. //IIndexDefinition::CreateIndex
  730. //Don't exit yet, the user might want to continue even though this index failed
  731. XTEST(hr = m_pCDataSource->m_pIIndexDefinition->CreateIndex(&TableID, &IndexID, cIndexColumnDescs, rgIndexColumnDescs, cPropSets, rgPropSets, NULL));
  732. //Free Properties
  733. FreeProperties(cPropSets, rgPropSets);
  734. //If INDEX Failed
  735. if(FAILED(hr))
  736. {
  737. //Index Failed, Do you want to Continue?
  738. if(IDNO == wMessageBox(NULL, MB_TASKMODAL | MB_ICONINFORMATION | MB_YESNO, wsz_ERROR, 
  739. wsz_INDEX_FAILED_, m_rgIndexInfo[i].wszIndexName))
  740. goto CLEANUP;
  741. }
  742. //Since the User didn't exit, continue as normal
  743. hr = S_OK;
  744. }
  745. }
  746. //Using SQL Commands to insert the index
  747. else if(m_pCDataSource->m_pICommandText)
  748. {
  749. // Loop around each index that is valid.  See if any are to be created
  750. for(i=0; i<m_cIndexes; i++) 
  751. {
  752. //If this index has already been created, skip
  753. //might have been used in another index creation
  754. if(rgIndexUsed[i])
  755. continue;
  756. //If this index is used as a primary key, skip
  757. //PrimaryKeys are taken care of differently
  758. if(m_rgIndexInfo[i].fIsPrimaryKey)
  759. continue;
  760. //"CREATE <UNIQUE> INDEX "
  761. swprintf(wszSqlStmt, wsz_CREATE_INDEX_, (m_rgIndexInfo[i].fUnique == VARIANT_TRUE) ? wsz_UNIQUE_INDEX : wsz_SPACE);
  762. //Add IndexName to the list (quoted)
  763. GetQuotedID(wszBuffer, m_rgIndexInfo[i].wszIndexName);
  764. wcscat(wszSqlStmt, wszBuffer); 
  765. //Add TableName
  766. wcscat(wszSqlStmt, L" ON ");
  767. GetQuotedID(wszBuffer, m_wszQualTableName);
  768. wcscat(wszSqlStmt, wszBuffer); 
  769. wcscat(wszSqlStmt, wsz_LPAREN);
  770. // Now loop through find all columns that belong to this index
  771. for(ULONG iCol=i; iCol<m_cIndexes; iCol++) 
  772. {
  773. //If not the same index skip
  774. if(wcscmp(m_rgIndexInfo[i].wszIndexName, m_rgIndexInfo[iCol].wszIndexName)!=0)
  775. continue;
  776. //mark this Index as used
  777. rgIndexUsed[iCol] = TRUE;
  778. //Add Column Name to the list (quoted)
  779. GetQuotedID(wszBuffer, m_rgIndexInfo[iCol].wszColName);
  780. wcscat(wszSqlStmt, wszBuffer);
  781. // Indicate Asending or Decending
  782. if(m_rgIndexInfo[iCol].dwCollation == DB_COLLATION_DESC)
  783. wcscat(wszSqlStmt, wsz_INDEX_DESC); 
  784. //Add trailing "," between col names
  785. wcscat(wszSqlStmt, wsz_COMMA);
  786. }
  787. //Replace last trailing "," with a ")"
  788. wcscpy(&wszSqlStmt[wcslen(wszSqlStmt)-wcslen(wsz_COMMA)], wsz_RPAREN);
  789. // If user wants to see the statement, show it to them
  790. if(m_pCWizard->m_pCTableCopy->m_fShowQuery)
  791. wMessageBox(NULL, MB_TASKMODAL | MB_OK | MB_ICONINFORMATION, wsz_OLEDB, 
  792. wsz_SHOW_SQL_, m_pCDataSource->m_pwszDataSource, wszSqlStmt);
  793. //Set the command text
  794. XTESTC(hr = m_pCDataSource->m_pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));
  795. //Execute the command
  796. //Don't exit yet, the user might want to continue even though this index failed
  797. XTEST(hr = m_pCDataSource->m_pICommandText->Execute(NULL, IID_NULL, NULL, NULL, NULL));
  798. //If INDEX Failed
  799. if(FAILED(hr))
  800. {
  801. //Index Failed, Do you want to Continue?
  802. if(IDNO == wMessageBox(NULL, MB_TASKMODAL | MB_ICONINFORMATION | MB_YESNO, wsz_ERROR, 
  803. wsz_INDEX_FAILED_, m_rgIndexInfo[i].wszIndexName))
  804. goto CLEANUP;
  805. }
  806. //Since the User didn't exit, continue as normal
  807. hr = S_OK;
  808. }
  809. }
  810. CLEANUP:
  811. SAFE_FREE(rgIndexUsed);
  812. SAFE_FREE(rgIndexColumnDescs);
  813. SAFE_FREE(rgDBIDs);
  814. return hr;
  815. }
  816. /////////////////////////////////////////////////////////////////
  817. // HRESULT CTable::CreateSQLStmt
  818. //
  819. /////////////////////////////////////////////////////////////////
  820. HRESULT CTable::CreateSQLStmt(ESQL_STMT eSqlStmt, WCHAR* pwszSqlStmt, BOOL fShowSql)
  821. {
  822. ASSERT(pwszSqlStmt);
  823. HRESULT hr = S_OK;
  824. WCHAR     wszBuffer[MAX_NAME_LEN*2];  // Buffer
  825. switch(eSqlStmt)
  826. {
  827. //SELECT <ColumnList> FROM <QualifiedTableName>
  828. case ESQL_SELECT:
  829. {
  830. //Create the SELECT statment
  831. wcscpy(pwszSqlStmt, wsz_SELECT);
  832. // Loop through each column
  833. for(ULONG i=0; i<m_cColumns; i++) 
  834. {
  835. // Add the column to the list (quoted)
  836. GetQuotedID(wszBuffer, m_rgColDesc[i].wszColName);
  837. wcscat(pwszSqlStmt, wszBuffer);
  838. if(i<m_cColumns-1)
  839. wcscat(pwszSqlStmt, wsz_COMMA);
  840. }     
  841.     
  842. // Add the Table Name
  843. wcscat(pwszSqlStmt, wsz_FROM);
  844. GetQuotedID(wszBuffer, m_wszQualTableName);
  845. wcscat(pwszSqlStmt, wszBuffer);
  846. }
  847. break;
  848. case ESQL_INSERT:
  849. {
  850. wcscpy(pwszSqlStmt, wsz_INSERT_INTO);
  851. //Add the Table Name
  852. GetQuotedID(wszBuffer, m_wszQualTableName);
  853. wcscat(pwszSqlStmt, wszBuffer);
  854. wcscat(pwszSqlStmt, wsz_LPAREN);
  855. ULONG cColumns = 0;
  856. // Add the column list
  857. for(ULONG i=0; i<m_cColumns; i++) 
  858. {
  859. //Only Bind updatable columns
  860. if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITE || m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN)
  861. {
  862. //Only add a leading comma if there are preceding columns 
  863. if(cColumns++)
  864. wcscat(pwszSqlStmt, wsz_COMMA);
  865. // Add the column to the list (quoted)
  866. GetQuotedID(wszBuffer, m_rgColDesc[i].wszColName);
  867. wcscat(pwszSqlStmt, wszBuffer);
  868. }
  869. //Add VALUES clause
  870. wcscat(pwszSqlStmt, wsz_VALUES_CLAUSE);
  871. // Loop through each column
  872. cColumns = 0;
  873. for(i=0; i<m_cColumns; i++) 
  874. {
  875. //Only Bind those columns that are updatable
  876. if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITE || m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN)
  877. {
  878. //Only add a leading comma if there are preceding columns 
  879. if(cColumns++)
  880. wcscat(pwszSqlStmt, wsz_COMMA);
  881. wcscat(pwszSqlStmt, wsz_PARAM);
  882. }
  883. // Finish off the string
  884. wcscat(pwszSqlStmt, wsz_RPAREN);
  885. }
  886. break;
  887. case ESQL_CREATE_TABLE:
  888. {
  889. // Setup the initialize CREATE TABLE '<CTableName>'
  890. wcscpy(pwszSqlStmt, wsz_CREATE_TABLE);
  891. GetQuotedID(wszBuffer, m_wszQualTableName);
  892. wcscat(pwszSqlStmt, wszBuffer);
  893. // Setup (<ColList>)
  894. wcscat(pwszSqlStmt, wsz_LPAREN);
  895. // Loop through each column and format the column name, type, and precision
  896. for(ULONG i=0; i<m_cColumns; i++) 
  897. {
  898. // Add the column to the list (quoted)
  899. GetQuotedID(wszBuffer, m_rgColDesc[i].wszColName);
  900. wcscat(pwszSqlStmt, wszBuffer);
  901. wcscat(pwszSqlStmt, wsz_SPACE);
  902. // Add ColumnType (formatted with CreateParams)
  903. GetTypeNameAndParams(i, wszBuffer);
  904. wcscat(pwszSqlStmt, wszBuffer);
  905. //Add PRIMARY KEY if Supported and a PrimaryKey Column
  906. if(m_rgColDesc[i].fIsPrimaryKey && m_pCDataSource->m_fPrimaryKeysSupported && m_pCWizard->m_pCTableCopy->m_fCopyPrimaryKeys)
  907. wcscat(pwszSqlStmt, wsz_PRIMARY_KEY);
  908. //Add Comma
  909. if(i<m_cColumns-1)
  910. wcscat(pwszSqlStmt, wsz_COMMA);
  911. }
  912. //Add trailing ")"
  913. wcscat(pwszSqlStmt, wsz_RPAREN);
  914. }
  915. break;
  916. case ESQL_DROP_TABLE:
  917. {
  918. GetQuotedID(wszBuffer, m_wszQualTableName);
  919. swprintf(pwszSqlStmt, wsz_DROP_TABLE_, wszBuffer);
  920. }
  921. break;
  922. default:
  923. ASSERT(!"Unhandled Case!");
  924. break;
  925. };
  926. // If user wants to see the statement, show it to them
  927. if(m_pCWizard->m_pCTableCopy->m_fShowQuery && fShowSql)
  928. wMessageBox(NULL, MB_TASKMODAL | MB_OK | MB_ICONINFORMATION, wsz_OLEDB, 
  929. wsz_SHOW_SQL_, m_pCDataSource->m_pwszDataSource, pwszSqlStmt);
  930. return hr;
  931. }
  932. /////////////////////////////////////////////////////////////////
  933. // HRESULT CTable::AdjustBindings
  934. //
  935. /////////////////////////////////////////////////////////////////
  936. HRESULT CTable::AdjustBindings(ULONG cBindings, DBBINDING* rgBindings, void* pData)
  937. {
  938. ASSERT(pData);
  939. HRESULT hr = S_OK;
  940. //Adjust all Storage Objects
  941. //We have 2 problems.  First some providers (MSDASQL) may require the LENGTH
  942. //of the storage object bound.  We have no clue what the LENGTH is unless 
  943. //we read the entire stream.  Second some providers may only allow 1 storage
  944. //object open at any one time.  The simplest soltuion would be just to buffer
  945. //the provider storage objects into our own and release the providers...
  946. for(ULONG i=0; i<cBindings; i++)
  947. {
  948. //DBSTATUS_S_ISNULL - nothing to adjust
  949. if(BINDING_STATUS(rgBindings[i],pData) == DBSTATUS_S_ISNULL)
  950. continue;
  951. //DB_S_TRUNCATED
  952. //Need to Adjust the LENGTH binding to be cbMaxLen
  953. if(BINDING_STATUS(rgBindings[i],pData) == DBSTATUS_S_TRUNCATED)
  954. BINDING_LENGTH(rgBindings[i], pData) = rgBindings[i].cbMaxLen;
  955. //DBTYPE_IUNKNOWN
  956. if(rgBindings[i].wType == DBTYPE_IUNKNOWN)
  957. {
  958.  //Obtain the providers ISeqStream object
  959. ISequentialStream* pISequentialStream = (ISequentialStream*)BINDING_VALUE(rgBindings[i], pData);
  960. //Copy the providers stream into our own CISeqStream object
  961. CISeqStream* pCISeqStream = new CISeqStream();
  962. pCISeqStream->Write(pISequentialStream, NULL);
  963. BINDING_VALUE(rgBindings[i], pData) = (ULONG)pCISeqStream;
  964. SAFE_RELEASE(pISequentialStream);
  965. //Indicate the LENGTH Binding
  966. BINDING_LENGTH(rgBindings[i], pData) = pCISeqStream->Length();
  967. }
  968. //DBSTATUS_S_OK
  969. BINDING_STATUS(rgBindings[i],pData) = DBSTATUS_S_OK;
  970. }
  971. return hr;
  972. }
  973. /////////////////////////////////////////////////////////////////
  974. // HRESULT CTable::CreateAccessors
  975. //
  976. /////////////////////////////////////////////////////////////////
  977. HRESULT CTable::CreateAccessors(ULONG* pcBindingInfo, BINDINGINFO** prgBindingInfo, ULONG* pcRowSize, ULONG ulBlobSize, BOOL* pbOutofLine)
  978. {
  979. ASSERT(m_pIRowset);
  980. ASSERT(m_pIAccessor);
  981. ASSERT(pcBindingInfo);
  982. ASSERT(prgBindingInfo);
  983. ASSERT(pbOutofLine);
  984. HRESULT hr = S_OK;
  985. ULONG ulOffset = 0;
  986. ULONG i,cBindings = 0;
  987. DBBINDING* rgBindings = NULL;
  988. ULONG cStorageObjects = 0;
  989. //Alloc the space for BindingInfo
  990. ULONG cBindingInfo = 0;
  991. BINDINGINFO* rgBindingInfo = NULL;
  992. SAFE_ALLOC(rgBindingInfo, BINDINGINFO, m_cColumns);
  993. //Alloc the space to hold the Bindings and Accessors
  994. SAFE_ALLOC(rgBindings, DBBINDING, m_cColumns);
  995. cBindings = 0;
  996. for(i=0; i<m_cColumns; i++) 
  997. {
  998. //SetUp the Bindings
  999. rgBindings[cBindings].iOrdinal = m_rgColDesc[i].iOrdinal;
  1000. rgBindings[cBindings].obStatus = ulOffset;
  1001. rgBindings[cBindings].obLength = ulOffset + sizeof(DBSTATUS);
  1002. rgBindings[cBindings].obValue = ulOffset + sizeof(DBSTATUS) + sizeof(ULONG);
  1003. rgBindings[cBindings].pTypeInfo = NULL;
  1004. rgBindings[cBindings].pBindExt  = NULL;
  1005. rgBindings[cBindings].dwPart = DBPART_VALUE|DBPART_LENGTH|DBPART_STATUS;
  1006. rgBindings[cBindings].dwMemOwner= DBMEMOWNER_CLIENTOWNED;
  1007. rgBindings[cBindings].eParamIO = DBPARAMIO_NOTPARAM;
  1008. rgBindings[cBindings].dwFlags = 0;
  1009. rgBindings[cBindings].bPrecision= m_rgColDesc[i].bPrecision;
  1010. rgBindings[cBindings].bScale = m_rgColDesc[i].bScale;
  1011. rgBindings[cBindings].pObject = NULL;
  1012. rgBindings[cBindings].wType = m_rgColDesc[i].wType;
  1013. rgBindings[cBindings].cbMaxLen = m_rgColDesc[i].ulColumnSize;
  1014. //Account for the NULL terminator
  1015. if(rgBindings[cBindings].wType == DBTYPE_STR)
  1016. rgBindings[cBindings].cbMaxLen += sizeof(CHAR);
  1017. if(rgBindings[cBindings].wType == DBTYPE_WSTR)
  1018. rgBindings[cBindings].cbMaxLen += sizeof(WCHAR);
  1019. //Adjust ISLONG Columns if not bound as ISeqStream
  1020. if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_ISLONG)
  1021. ADJUST_SIZE(rgBindings[cBindings].cbMaxLen, ulBlobSize);
  1022. //ISeqStream
  1023. //Since some providers may only allow 1 storage object bound at a time
  1024. //we will create a seperate accessor for each storage object bound.
  1025. if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_ISLONG && ulBlobSize == ULONG_MAX)
  1026. {
  1027. cStorageObjects++;
  1028. //Setup wType
  1029. rgBindings[cBindings].wType = DBTYPE_IUNKNOWN;
  1030. rgBindings[cBindings].cbMaxLen = sizeof(IUnknown*);
  1031. //Setup pObject structure
  1032. SAFE_ALLOC(rgBindings[cBindings].pObject, DBOBJECT, 1);
  1033. rgBindings[cBindings].pObject->iid = IID_ISequentialStream;
  1034. rgBindings[cBindings].pObject->dwFlags = STGM_READ;
  1035. //Only need a seperate Accessor if there is more than 1 Storage column
  1036. if(cStorageObjects > 1)
  1037. {
  1038. //Setup BindingInfo
  1039. rgBindingInfo[cBindingInfo].cBindings = 1;
  1040. SAFE_ALLOC(rgBindingInfo[cBindingInfo].rgBindings, DBBINDING, 1);
  1041. memcpy(rgBindingInfo[cBindingInfo].rgBindings, &rgBindings[cBindings], sizeof(DBBINDING));
  1042. //Create the accessor for the Storage column
  1043. XTESTC(hr = m_pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &rgBindings[cBindings], 0, &rgBindingInfo[cBindingInfo].hAccessor, NULL));
  1044. cBindingInfo++;
  1045. }
  1046. }
  1047. //Determine if there is out of line data...
  1048. switch(rgBindings[cBindings].wType)
  1049. {
  1050. case DBTYPE_VARIANT:
  1051. *pbOutofLine = TRUE;
  1052. break;
  1053. }
  1054. ulOffset = ROUNDUP(rgBindings[cBindings].obValue + rgBindings[cBindings].cbMaxLen);
  1055. //Already handled ISeqStream columns
  1056. if(!(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_ISLONG && ulBlobSize == ULONG_MAX && cStorageObjects>1))
  1057. cBindings++;
  1058. }
  1059. //Create the accessor for the entire row, (excluding Storage columns)
  1060. if(cBindings)
  1061. {
  1062. //Setup BindingInfo
  1063. rgBindingInfo[cBindingInfo].cBindings = cBindings;
  1064. rgBindingInfo[cBindingInfo].rgBindings = rgBindings;
  1065. XTESTC(hr = m_pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, 0, &rgBindingInfo[cBindingInfo].hAccessor, NULL));
  1066. cBindingInfo++;
  1067. }
  1068. //Size for pData
  1069. if(pcRowSize)
  1070. *pcRowSize = ulOffset;
  1071. //Accessors
  1072. *pcBindingInfo = cBindingInfo;
  1073. *prgBindingInfo = rgBindingInfo;
  1074. CLEANUP:
  1075. return hr;
  1076. }
  1077. /////////////////////////////////////////////////////////////////
  1078. // HRESULT CTable::GetRowset
  1079. //
  1080. /////////////////////////////////////////////////////////////////
  1081. HRESULT CTable::GetRowset(DWORD dwInsertOpt)
  1082. {
  1083. ASSERT(m_pCDataSource);
  1084. WCHAR wszBuffer[MAX_NAME_LEN];
  1085. HRESULT hr;
  1086. ULONG cPropSets = 0;
  1087. DBPROPSET* rgPropSets = NULL;
  1088. //Release the current rowset
  1089. SAFE_RELEASE(m_pIRowset);
  1090. SAFE_RELEASE(m_pIAccessor);
  1091. //Kagera's Implementation requires IID_RowsetLocate for BLOB Support
  1092. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_IRowsetLocate, DBPROPSET_ROWSET))
  1093. SetProperty(DBPROP_IRowsetLocate, DBPROPSET_ROWSET, &cPropSets, &rgPropSets, DBTYPE_BOOL, TRUE);
  1094. //DBPROP_UPDATABILITY
  1095. if(dwInsertOpt != IDR_PARAM_SETS && IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_UPDATABILITY, DBPROPSET_ROWSET))
  1096. SetProperty(DBPROP_UPDATABILITY, DBPROPSET_ROWSET, &cPropSets, &rgPropSets, DBTYPE_I4, DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT);
  1097. //DBPROP_IRowsetChange
  1098. if(dwInsertOpt == IDR_INSERTROW_IMMEDIATE && IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_IRowsetChange, DBPROPSET_ROWSET))
  1099. SetProperty(DBPROP_IRowsetChange, DBPROPSET_ROWSET, &cPropSets, &rgPropSets, DBTYPE_BOOL, TRUE);
  1100. //DBPROP_IRowsetUpdate
  1101. if(dwInsertOpt == IDR_INSERTROW_BUFFERED && IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_IRowsetUpdate, DBPROPSET_ROWSET))
  1102. {
  1103. //DBPROP_CANHOLDROWS
  1104. //In order to insert more rows while there are pending changes
  1105. SetProperty(DBPROP_CANHOLDROWS, DBPROPSET_ROWSET, &cPropSets, &rgPropSets, DBTYPE_BOOL, TRUE);
  1106. SetProperty(DBPROP_IRowsetUpdate, DBPROPSET_ROWSET, &cPropSets, &rgPropSets, DBTYPE_BOOL, TRUE);
  1107. }
  1108. //Setup TableID
  1109. DBID TableID;
  1110. TableID.eKind = DBKIND_NAME;
  1111. //Quote the TableName
  1112. TableID.uName.pwszName = wszBuffer;
  1113. GetQuotedID(wszBuffer, m_wszQualTableName);
  1114. //IOpenRowset
  1115. XTESTC(hr = m_pCDataSource->m_pIOpenRowset->OpenRowset(NULL, &TableID, NULL, IID_IRowset, cPropSets, rgPropSets, (IUnknown**)&m_pIRowset));
  1116. CHECKC(m_pIRowset);
  1117. //Obtain the Accessor
  1118. XTESTC(hr = m_pIRowset->QueryInterface(IID_IAccessor, (void**)&m_pIAccessor));
  1119. CLEANUP:
  1120. FreeProperties(cPropSets, rgPropSets);
  1121. return hr;
  1122. }
  1123. /////////////////////////////////////////////////////////////////
  1124. // HRESULT CTable::CopyData
  1125. //
  1126. /////////////////////////////////////////////////////////////////
  1127. HRESULT CTable::CopyData(CTable* pCSourceTable, ULONG* pcRowsCopied)
  1128. {
  1129. ASSERT(pCSourceTable && pcRowsCopied);
  1130. HRESULT hr;
  1131. WCHAR wszSqlStmt[MAX_QUERY_LEN]; // Format the select statement
  1132. WCHAR wszBuffer[MAX_NAME_LEN];
  1133.     ULONG i,j,ulOffset = 0;
  1134. ULONG cBindings = 0;
  1135. DBBINDING* rgBindings = NULL;
  1136. HACCESSOR  hAccessor = DB_NULL_HACCESSOR;
  1137. IAccessor* pIAccessor = NULL;
  1138. ULONG cRowSize = 0;
  1139. IRowset* pISourceRowset = pCSourceTable->m_pIRowset;
  1140. IRowsetChange* pIRowsetChange = NULL;
  1141. IRowsetUpdate* pIRowsetUpdate = NULL;
  1142. ULONG cRowsObtained = 0;
  1143. HROW* rghRows = NULL;
  1144. DBPARAMS DBParams;
  1145. void* pData = NULL;
  1146. void* pRowData = NULL;
  1147. ULONG cRows = 0;
  1148. CTableCopy* pCTableCopy = m_pCWizard->m_pCTableCopy;
  1149.     ULONG ulParamSets = pCTableCopy->m_dwInsertOpt == IDR_PARAM_SETS ? pCTableCopy->m_ulParamSets : 0;
  1150. ULONG ulBlobSize  = pCTableCopy->m_dwBlobOpt == IDR_BLOB_SIZE ? pCTableCopy->m_ulBlobSize : ULONG_MAX;
  1151. ULONG ulMaxRows   = pCTableCopy->m_dwRowOpt == IDR_ROW_COUNT ? pCTableCopy->m_ulMaxRows : ULONG_MAX;
  1152. BOOL bOutofLine = FALSE;
  1153. ULONG cBindingInfo = 0;
  1154. BINDINGINFO* rgBindingInfo = NULL;
  1155. CProgress* pCProgress = m_pCWizard->m_pCProgress;
  1156. //Get the Rowset from the SourceTable
  1157. QTESTC(hr = pCSourceTable->CreateAccessors(&cBindingInfo, &rgBindingInfo, &cRowSize, ulBlobSize, &bOutofLine));
  1158. //Obtain the Accessor
  1159. SAFE_ALLOC(rgBindings, DBBINDING, m_cColumns);
  1160. cBindings = 0; 
  1161. for(i=0; i<m_cColumns; i++) 
  1162. {
  1163. rgBindings[cBindings].iOrdinal = ulParamSets ? cBindings+1 : m_rgColDesc[i].iOrdinal;
  1164. rgBindings[cBindings].obStatus  = ulOffset;
  1165. rgBindings[cBindings].obLength  = ulOffset + sizeof(DBSTATUS);
  1166. rgBindings[cBindings].obValue   = ulOffset + sizeof(ULONG) + sizeof(DBSTATUS);
  1167. rgBindings[cBindings].pTypeInfo = NULL;
  1168. rgBindings[cBindings].pBindExt  = NULL;
  1169. rgBindings[cBindings].dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS;
  1170. rgBindings[cBindings].dwMemOwner= DBMEMOWNER_CLIENTOWNED;
  1171. rgBindings[cBindings].eParamIO = ulParamSets ? DBPARAMIO_INPUT : DBPARAMIO_NOTPARAM;
  1172. rgBindings[cBindings].dwFlags = 0;
  1173. rgBindings[cBindings].bPrecision= pCSourceTable->m_rgColDesc[i].bPrecision;
  1174. rgBindings[cBindings].bScale = pCSourceTable->m_rgColDesc[i].bScale;
  1175. rgBindings[cBindings].pObject = NULL;
  1176. rgBindings[cBindings].wType = pCSourceTable->m_rgColDesc[i].wType;
  1177. rgBindings[cBindings].cbMaxLen = pCSourceTable->m_rgColDesc[i].ulColumnSize;
  1178. //Account for the NULL terminator
  1179. if(rgBindings[cBindings].wType == DBTYPE_STR)
  1180. rgBindings[cBindings].cbMaxLen += sizeof(CHAR);
  1181. if(rgBindings[cBindings].wType == DBTYPE_WSTR)
  1182. rgBindings[cBindings].cbMaxLen += sizeof(WCHAR);
  1183. //Adjust ISLONG Columns if not bound as ISeqStream
  1184. if(pCSourceTable->m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_ISLONG) 
  1185. ADJUST_SIZE(rgBindings[cBindings].cbMaxLen, ulBlobSize);
  1186. //ISeqStream
  1187. if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_ISLONG && 
  1188. pCTableCopy->m_dwBlobOpt == IDR_ISEQ_STREAM)
  1189. {
  1190. //Setup wType
  1191. rgBindings[cBindings].wType = DBTYPE_IUNKNOWN;
  1192. rgBindings[cBindings].cbMaxLen = sizeof(IUnknown*);
  1193. //Setup pObject structure
  1194. SAFE_ALLOC(rgBindings[cBindings].pObject, DBOBJECT, 1);
  1195. rgBindings[cBindings].pObject->iid = IID_ISequentialStream;
  1196. rgBindings[cBindings].pObject->dwFlags = STGM_READ;
  1197. }
  1198. ulOffset = ROUNDUP(rgBindings[cBindings].obValue + rgBindings[cBindings].cbMaxLen);
  1199. //Only Bind Updatable columns
  1200. //Note, we are not using the Source info here, since in the process
  1201. //of adjusting columns to a different DSN, the columns may have become writeable
  1202. if(m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITE || m_rgColDesc[i].dwFlags & DBCOLUMNFLAGS_WRITEUNKNOWN)
  1203. cBindings++;
  1204. }
  1205. //If using Parameters to INSERT the Data
  1206. if(pCTableCopy->m_dwInsertOpt == IDR_PARAM_SETS)
  1207. {
  1208. // Now create the INSERT INTO statment
  1209. CreateSQLStmt(ESQL_INSERT, wszSqlStmt, ulParamSets);
  1210. //Set the command text
  1211. XTESTC(hr = m_pCDataSource->m_pICommandText->SetCommandText(DBGUID_DBSQL, wszSqlStmt));
  1212. //Create the Target Accessor
  1213. XTESTC(hr = m_pCDataSource->m_pICommandText->QueryInterface(IID_IAccessor, (void**)&pIAccessor));
  1214. XTESTC(hr = pIAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, cBindings, rgBindings, ulOffset, &hAccessor, NULL));
  1215. }
  1216. //were using InsertRow 
  1217. else
  1218. {
  1219. //Obtain IRowsetChange interface from the TargetRowset 
  1220. XTESTC(hr = m_pIRowset->QueryInterface(IID_IRowsetChange, (void**)&pIRowsetChange));
  1221. //Create the Target Accessor
  1222. XTESTC(hr = m_pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor));
  1223. XTESTC(hr = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, ulOffset, &hAccessor, NULL));
  1224. if(pCTableCopy->m_dwInsertOpt == IDR_INSERTROW_BUFFERED)
  1225. XTESTC(hr = m_pIRowset->QueryInterface(IID_IRowsetUpdate, (void**)&pIRowsetUpdate));
  1226. }
  1227. // Display the progress dialog
  1228. pCProgress->Display();
  1229. pCProgress->SetHeading(wsz_COPYING);
  1230. //Alloc room for pData
  1231. SAFE_ALLOC(pData, BYTE, max(ulParamSets, 1) * cRowSize);
  1232. memset(pData, 0, max(ulParamSets, 1) * cRowSize);
  1233. //Setup DBPARAMS Struct
  1234. DBParams.cParamSets = 1; //Numer of Parameter sets
  1235. DBParams.hAccessor = hAccessor; //Target Param Accessor
  1236. DBParams.pData = pData; //Source Data
  1237. while(cRows < ulMaxRows)
  1238. {
  1239. XTESTC(hr = pISourceRowset->GetNextRows(NULL, 0, (ulParamSets > 1) ? ulParamSets : MAX_BLOCK_SIZE, &cRowsObtained, &rghRows));
  1240. //ENDOFROWSET
  1241. if(cRowsObtained==0)
  1242. break;
  1243. //Determine the number of rows that are actually needed to retrieve
  1244. //The user might have specfified the number of rows to retrieve which could 
  1245. //be smaller than the number in the block size
  1246. ULONG cRowsNeeded = min(cRowsObtained, ulMaxRows-cRows);
  1247. //Use Parameters to INSERT the data, use MultipleParamSets
  1248. if(ulParamSets > 1)
  1249. {
  1250. //GetData
  1251. for(i=0; i<cRowsNeeded; i++) 
  1252. {
  1253. pRowData = (BYTE*)pData + (i*cRowSize);
  1254. for(j=0; j<cBindingInfo; j++)
  1255. {
  1256. XTESTC(hr = pISourceRowset->GetData(rghRows[i], rgBindingInfo[j].hAccessor, pRowData));
  1257. //AdjustBindings
  1258. QTESTC(hr = AdjustBindings(rgBindingInfo[j].cBindings, rgBindingInfo[j].rgBindings, pRowData));
  1259. }
  1260. }
  1261. //Adjust the paramerer sets
  1262. DBParams.cParamSets = cRowsNeeded;
  1263. //Execute the INSERT (multiple param sets)
  1264. XTESTC(hr = m_pCDataSource->m_pICommandText->Execute(NULL, IID_NULL, &DBParams, NULL, NULL));
  1265. //FreeBindingData - outofline memory
  1266. for(i=0; i<cRowsNeeded && bOutofLine; i++)
  1267. {
  1268. pRowData = (BYTE*)pData + (i*cRowSize);
  1269. for(j=0; j<cBindingInfo; j++)
  1270. QTESTC(FreeBindingData(rgBindingInfo[j].cBindings, rgBindingInfo[j].rgBindings, pData));
  1271. }
  1272. // Update insert progress
  1273. swprintf(wszBuffer, wsz_COPIED_RECORDS, (cRows += cRowsNeeded));
  1274. if(!pCProgress->Update(wszBuffer))
  1275. goto CLEANUP;
  1276. }
  1277. //Use Paramseters to INSERT the data, but only 1 ParamSet (not multiple)
  1278. else if(ulParamSets == 1)
  1279. {
  1280. for(i=0; i<cRowsNeeded; i++) 
  1281. {
  1282. for(j=0; j<cBindingInfo; j++)
  1283. {
  1284. //GetData
  1285. XTESTC(hr = pISourceRowset->GetData(rghRows[i], rgBindingInfo[j].hAccessor, pData));
  1286. //AdjustBindings
  1287. QTESTC(hr = AdjustBindings(rgBindingInfo[j].cBindings, rgBindingInfo[j].rgBindings, pData));
  1288. }
  1289. //Execute the INSERT
  1290. XTESTC(hr = m_pCDataSource->m_pICommandText->Execute(NULL, IID_NULL, &DBParams, NULL, NULL));
  1291. //FreeBindingData - outofline memory
  1292. for(j=0; j<cBindingInfo && bOutofLine; j++)
  1293. QTESTC(FreeBindingData(rgBindingInfo[j].cBindings, rgBindingInfo[j].rgBindings, pData));
  1294. // Update insert progress
  1295. swprintf(wszBuffer, wsz_COPIED_RECORDS, cRows++);
  1296. if(!pCProgress->Update(wszBuffer))
  1297. goto CLEANUP;
  1298. }
  1299. }
  1300. //Use InsertRow to INSERT the Data
  1301. else
  1302. {
  1303. for(i=0; i<cRowsNeeded; i++) 
  1304. {
  1305. for(j=0; j<cBindingInfo; j++)
  1306. {
  1307. //GetData from the Source
  1308. XTESTC(hr = pISourceRowset->GetData(rghRows[i], rgBindingInfo[j].hAccessor, pData));
  1309. //AdjustBindings
  1310. QTESTC(hr = AdjustBindings(rgBindingInfo[j].cBindings, rgBindingInfo[j].rgBindings, pData));
  1311. }
  1312. //InsertRow to the Target
  1313. XTESTC(hr = pIRowsetChange->InsertRow(NULL, hAccessor, pData, NULL));
  1314. // Update insert progress
  1315. if(pCTableCopy->m_dwInsertOpt == IDR_INSERTROW_IMMEDIATE)
  1316. {
  1317. // Update insert progress
  1318. swprintf(wszBuffer, wsz_COPIED_RECORDS, cRows++);
  1319. if(!pCProgress->Update(wszBuffer))
  1320. goto CLEANUP;
  1321. }
  1322. //FreeBindingData - outofline memory
  1323. for(j=0; j<cBindingInfo && bOutofLine; j++)
  1324. QTESTC(FreeBindingData(rgBindingInfo[j].cBindings, rgBindingInfo[j].rgBindings, pData));
  1325. }
  1326. //Use IRowsetUpdate::Update if in Bufferred mode
  1327. //Update all, since we don't have the inserted row handles...
  1328. if(pCTableCopy->m_dwInsertOpt == IDR_INSERTROW_BUFFERED)
  1329. {
  1330. XTESTC(hr = pIRowsetUpdate->Update(NULL, 0, NULL, NULL, NULL, NULL));
  1331. // Update insert progress
  1332. swprintf(wszBuffer, wsz_COPIED_RECORDS, cRows += cRowsNeeded);
  1333. if(!pCProgress->Update(wszBuffer))
  1334. goto CLEANUP;
  1335. }
  1336. }
  1337. //Release the group of rows
  1338. XTESTC(hr = pISourceRowset->ReleaseRows(cRowsObtained, rghRows, NULL, NULL, NULL));
  1339. SAFE_FREE(rghRows);
  1340. }
  1341. CLEANUP:
  1342. //Stop the propgress
  1343. pCProgress->Destroy();
  1344. *pcRowsCopied = cRows;
  1345. //Release Accessors
  1346. if(hAccessor)
  1347. XTEST(pIAccessor->ReleaseAccessor(hAccessor, NULL));
  1348. SAFE_RELEASE(pIAccessor);
  1349. //Free any outofbound data, (error case)
  1350. for(i=0; i<cBindingInfo; i++)
  1351. FreeBindingData(rgBindingInfo[i].cBindings, rgBindingInfo[i].rgBindings, pData);
  1352. //Release Accessors
  1353. for(i=0; i<cBindingInfo; i++)
  1354. {
  1355. XTEST(pCSourceTable->m_pIAccessor->ReleaseAccessor(rgBindingInfo[i].hAccessor, NULL));
  1356. FreeBindings(rgBindingInfo[i].cBindings, rgBindingInfo[i].rgBindings);
  1357. }
  1358. SAFE_FREE(rgBindingInfo);
  1359. FreeBindings(cBindings, rgBindings);
  1360. SAFE_FREE(pData);
  1361. SAFE_RELEASE(pIRowsetChange);
  1362. SAFE_RELEASE(pIRowsetUpdate);
  1363. SAFE_FREE(rghRows);
  1364. return hr;
  1365. }
  1366. /////////////////////////////////////////////////////////////////////////////
  1367. // HRESULT CTable::GetTypeInfoRowset
  1368. //
  1369. /////////////////////////////////////////////////////////////////////////////
  1370. HRESULT CTable::GetTypeInfoRowset(IAccessor** ppIAccessor, HACCESSOR* phAccessor, IRowset** ppIRowset)
  1371. {
  1372. ASSERT(ppIAccessor && phAccessor && ppIRowset);
  1373. ASSERT(m_pCDataSource && m_pCDataSource->m_pIOpenRowset);
  1374. HRESULT hr;
  1375. IDBSchemaRowset* pIDBSchemaRowset = m_pCDataSource->m_pIDBSchemaRowset;
  1376. //Provider doesn't have to support IDBSchemaRowset
  1377. if(pIDBSchemaRowset == NULL)
  1378. return E_FAIL;
  1379. //Bind all the columns from types rowset: 
  1380. const static ULONG cBindings = 6;
  1381. const static DBBINDING rgBindings[cBindings] = 
  1382. {
  1383. //TYPE_NAME
  1384. 1,  
  1385. offsetof(TYPEINFO, wszTypeName), // offset of value in consumers buffer
  1386. 0, // offset of length
  1387. 0, // offset of status
  1388. NULL, // reserved
  1389. NULL, // for ole object
  1390.   NULL, // reserved
  1391. DBPART_VALUE, // specifies Value is bound only
  1392. DBMEMOWNER_CLIENTOWNED, // memory is client owned
  1393. DBPARAMIO_NOTPARAM, // 
  1394. MAX_NAME_LEN, // size in bytes of the value part in the consumers buffer
  1395. 0,  // reserved
  1396. DBTYPE_WSTR,  // data type indicator
  1397. 0, // precision
  1398. 0,  // scale
  1399. //DATA_TYPE
  1400. 2,  
  1401. offsetof(TYPEINFO, wType), // offset of value in consumers buffer
  1402. 0, // offset of length
  1403. 0, // offset of status
  1404. NULL, // reserved
  1405. NULL, // for ole object
  1406.   NULL, // reserved
  1407. DBPART_VALUE, // specifies Value is bound only
  1408. DBMEMOWNER_CLIENTOWNED, // memory is client owned
  1409. DBPARAMIO_NOTPARAM, // 
  1410. sizeof(USHORT), // size in bytes of the value part in the consumers buffer
  1411. 0,  // reserved
  1412. DBTYPE_UI2,  // data type indicator
  1413. 0, // precision
  1414. 0,  // scale
  1415. //COLUMN_SIZE
  1416. 3,  
  1417. offsetof(TYPEINFO, ulColumnSize), // offset of value in consumers buffer
  1418. 0, // offset of length
  1419. 0, // offset of status
  1420. NULL, // reserved
  1421. NULL, // for ole object
  1422.   NULL, // reserved
  1423. DBPART_VALUE, // specifies Value is bound only
  1424. DBMEMOWNER_CLIENTOWNED, // memory is client owned
  1425. DBPARAMIO_NOTPARAM, // 
  1426. sizeof(ULONG), // size in bytes of the value part in the consumers buffer
  1427. 0,  // reserved
  1428. DBTYPE_UI4,  // data type indicator
  1429. 0, // precision
  1430. 0,  // scale
  1431. //CREATE_PARAMS
  1432. 6,  
  1433. offsetof(TYPEINFO, wszCreateParams),// offset of value in consumers buffer
  1434. 0, // offset of length
  1435. 0, // offset of status
  1436. NULL, // reserved
  1437. NULL, // for ole object
  1438.   NULL, // reserved
  1439. DBPART_VALUE, // specifies Value is bound only
  1440. DBMEMOWNER_CLIENTOWNED, // memory is client owned
  1441. DBPARAMIO_NOTPARAM, // 
  1442. MAX_NAME_LEN, // size in bytes of the value part in the consumers buffer
  1443. 0,  // reserved
  1444. DBTYPE_WSTR,  // data type indicator
  1445. 0, // precision
  1446. 0,  // scale
  1447. //IS_NULLABLE
  1448. 7,  
  1449. offsetof(TYPEINFO, fIsNullable), // offset of value in consumers buffer
  1450. 0, // offset of length
  1451. 0, // offset of status
  1452. NULL, // reserved
  1453. NULL, // for ole object
  1454.   NULL, // reserved
  1455. DBPART_VALUE, // specifies Value is bound only
  1456. DBMEMOWNER_CLIENTOWNED, // memory is client owned
  1457. DBPARAMIO_NOTPARAM, // 
  1458. sizeof(VARIANT_BOOL), // size in bytes of the value part in the consumers buffer
  1459. 0,  // reserved
  1460. DBTYPE_BOOL,  // data type indicator
  1461. 0, // precision
  1462. 0,  // scale
  1463. //AUTO_UNIQUE_VALUE
  1464. 12,  
  1465. offsetof(TYPEINFO, fIsAutoInc), // offset of value in consumers buffer
  1466. 0, // offset of length
  1467. 0, // offset of status
  1468. NULL, // reserved
  1469. NULL, // for ole object
  1470.   NULL, // reserved
  1471. DBPART_VALUE, // specifies Value is bound only
  1472. DBMEMOWNER_CLIENTOWNED, // memory is client owned
  1473. DBPARAMIO_NOTPARAM, // 
  1474. sizeof(VARIANT_BOOL), // size in bytes of the value part in the consumers buffer
  1475. 0,  // reserved
  1476. DBTYPE_BOOL,  // data type indicator
  1477. 0, // precision
  1478. 0,  // scale
  1479. };
  1480. //GetRowset
  1481. //DBSCHEMA_PROVIDER_TYPES is required a SCHEMA
  1482. XTESTC(hr = pIDBSchemaRowset->GetRowset(NULL, DBSCHEMA_PROVIDER_TYPES, 0, NULL, IID_IRowset,0, NULL, (IUnknown**)ppIRowset));
  1483. //Create the the Accessor
  1484. XTESTC(hr = (*ppIRowset)->QueryInterface(IID_IAccessor, (void **)ppIAccessor));
  1485. XTESTC(hr = (*ppIAccessor)->CreateAccessor(DBACCESSOR_ROWDATA, cBindings, rgBindings, 0, phAccessor, NULL));
  1486. CLEANUP:
  1487. return hr;
  1488. }
  1489. /////////////////////////////////////////////////////////////////////////////
  1490. // HRESULT CTable::GetColumnDesc
  1491. //
  1492. /////////////////////////////////////////////////////////////////////////////
  1493. HRESULT CTable::GetColumnDesc(DBCOLUMNDESC** prgColumnDesc)
  1494. {
  1495. //Init buffer
  1496. ULONG i=0;
  1497. HRESULT hr = S_OK;
  1498. DBCOLUMNDESC* rgColumnDesc = NULL;
  1499. SAFE_ALLOC(rgColumnDesc, DBCOLUMNDESC, m_cColumns);
  1500. // For each column, fill out DBCOLUMNDESC info
  1501. for(i=0; i<m_cColumns; i++)
  1502. {
  1503. //TypeName
  1504. rgColumnDesc[i].pwszTypeName = m_rgColDesc[i].wszTypeName;
  1505. rgColumnDesc[i].pTypeInfo  = NULL;
  1506. rgColumnDesc[i].pclsid  = NULL;
  1507. //ColumnID
  1508. rgColumnDesc[i].dbcid.eKind = DBKIND_NAME;
  1509. rgColumnDesc[i].dbcid.uName.pwszName = m_rgColDesc[i].wszColName;
  1510. //Typeinfo
  1511. rgColumnDesc[i].wType = m_rgColDesc[i].wType;
  1512. rgColumnDesc[i].ulColumnSize = m_rgColDesc[i].ulColumnSize;
  1513. rgColumnDesc[i].bPrecision = m_rgColDesc[i].bPrecision;
  1514. rgColumnDesc[i].bScale = m_rgColDesc[i].bScale;
  1515. //Properties
  1516. rgColumnDesc[i].cPropertySets = 0;
  1517. rgColumnDesc[i].rgPropertySets = NULL;
  1518. //DBPPROP_COL_AUTOINCREMENT
  1519. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_COL_AUTOINCREMENT, DBPROPSET_COLUMN))
  1520. SetProperty(DBPROP_COL_AUTOINCREMENT, DBPROPSET_COLUMN, &rgColumnDesc[i].cPropertySets, &rgColumnDesc[i].rgPropertySets, DBTYPE_BOOL, m_rgColDesc[i].fIsAutoInc);
  1521. //TODO DBPROP_COL_DEFAULT
  1522. //TODO DBPROP_COL_DESCRIPTION
  1523. //DBPROP_COL_FIXEDLENGTH
  1524. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_COL_FIXEDLENGTH, DBPROPSET_COLUMN))
  1525. SetProperty(DBPROP_COL_FIXEDLENGTH, DBPROPSET_COLUMN, &rgColumnDesc[i].cPropertySets, &rgColumnDesc[i].rgPropertySets, DBTYPE_BOOL, IsNumericType(m_rgColDesc[i].wType));
  1526. //DBPROP_COL_NULLABLE
  1527. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_COL_NULLABLE, DBPROPSET_COLUMN))
  1528. SetProperty(DBPROP_COL_NULLABLE, DBPROPSET_COLUMN, &rgColumnDesc[i].cPropertySets, &rgColumnDesc[i].rgPropertySets, DBTYPE_BOOL, m_rgColDesc[i].fIsNullable);
  1529. //DBPROP_COL_PRIMARYKEY
  1530. if(IsSettableProperty(m_pCDataSource->m_pIDBInitialize, DBPROP_COL_PRIMARYKEY, DBPROPSET_COLUMN))
  1531. SetProperty(DBPROP_COL_PRIMARYKEY, DBPROPSET_COLUMN, &rgColumnDesc[i].cPropertySets, &rgColumnDesc[i].rgPropertySets, DBTYPE_BOOL, m_rgColDesc[i].fIsPrimaryKey && m_pCWizard->m_pCTableCopy->m_fCopyPrimaryKeys);
  1532. //TODO DBPROP_COL_UNIQUE
  1533. }
  1534. CLEANUP:
  1535. *prgColumnDesc = rgColumnDesc;
  1536. return hr;
  1537. }
  1538. ///////////////////////////////////////////////////////////////////////////////
  1539. // Class CISeqStream
  1540. // 
  1541. // My implementation of ISeqStream interface
  1542. ///////////////////////////////////////////////////////////////////////////////
  1543. CISeqStream::CISeqStream()
  1544. {
  1545. m_iPos         = 0;
  1546. m_cRef    = 0;
  1547. m_pBuffer      = NULL;
  1548. m_cBufSize     = 0;
  1549. //The constructor AddRef's
  1550. AddRef();
  1551. }
  1552. CISeqStream::~CISeqStream()
  1553. {
  1554. //Shouldn't have any references left
  1555. ASSERT(m_cRef == 0);
  1556. SAFE_FREE(m_pBuffer);
  1557. }
  1558. ULONG CISeqStream::AddRef(void)
  1559. {
  1560. return ++m_cRef;
  1561. }
  1562. ULONG CISeqStream::Release(void)
  1563. {
  1564. ASSERT(m_cRef);
  1565. if(--m_cRef)
  1566. return m_cRef;
  1567. delete this;
  1568. return 0;
  1569. }
  1570. HRESULT CISeqStream::QueryInterface(REFIID riid, void** ppv)
  1571. {
  1572. ASSERT(ppv);
  1573. *ppv = NULL;
  1574. if (riid == IID_IUnknown)
  1575. *ppv = this;
  1576. if (riid == IID_ISequentialStream)
  1577. *ppv = this;
  1578. if(*ppv)
  1579. {
  1580. ((IUnknown*)*ppv)->AddRef();
  1581. return S_OK;
  1582. }
  1583. return E_NOINTERFACE;
  1584. }
  1585. BOOL CISeqStream::Seek(ULONG iPos)
  1586. {
  1587. //Make sure the desired position is within the buffer
  1588. ASSERT(iPos == 0 || iPos < m_cBufSize);
  1589. //Reset the current buffer position
  1590. m_iPos = iPos;
  1591. return TRUE;
  1592. }
  1593. BOOL CISeqStream::Clear()
  1594. {
  1595. //Frees the buffer
  1596. m_iPos    = 0;
  1597. m_cBufSize     = 0;
  1598. SAFE_FREE(m_pBuffer);
  1599. return TRUE;
  1600. }
  1601. BOOL CISeqStream::CompareData(void* pBuffer)
  1602. {
  1603. ASSERT(pBuffer);
  1604. //Quick and easy way to compare user buffer with the stream
  1605. return memcmp(pBuffer, m_pBuffer, m_cBufSize)==0;
  1606. }
  1607. HRESULT CISeqStream::Read(void *pv, ULONG cb, ULONG* pcbRead)
  1608. {
  1609. //Parameter checking
  1610. if(pcbRead)
  1611. *pcbRead = 0;
  1612. if(!pv)
  1613. return STG_E_INVALIDPOINTER;
  1614. if(cb == 0)
  1615. return S_OK;
  1616. //Actual code
  1617. ULONG cBytesLeft = m_cBufSize - m_iPos;
  1618. ULONG cBytesRead = cb > cBytesLeft ? cBytesLeft : cb;
  1619. //if no more bytes to retrive return 
  1620. if(cBytesLeft == 0)
  1621. return S_FALSE; 
  1622. //Copy to users buffer the number of bytes requested or remaining
  1623. memcpy(pv, (void*)((BYTE*)m_pBuffer + m_iPos), cBytesRead);
  1624. m_iPos += cBytesRead;
  1625. if(pcbRead)
  1626. *pcbRead = cBytesRead;
  1627. if(cb != cBytesRead)
  1628. return S_FALSE; 
  1629. return S_OK;
  1630. }
  1631.         
  1632. HRESULT CISeqStream::Write(const void *pv, ULONG cb, ULONG* pcbWritten)
  1633. {
  1634. //Parameter checking
  1635. if(!pv)
  1636. return STG_E_INVALIDPOINTER;
  1637. if(pcbWritten)
  1638. *pcbWritten = 0;
  1639. if(cb == 0)
  1640. return S_OK;
  1641. //Enlarge the current buffer
  1642. m_cBufSize += cb;
  1643. //Need to append to the end of the stream
  1644. SAFE_REALLOC(m_pBuffer, BYTE, m_cBufSize);
  1645. memcpy((void*)((BYTE*)m_pBuffer + m_iPos), pv, cb);
  1646. if(pcbWritten)
  1647. *pcbWritten = cb;
  1648. CLEANUP:
  1649. return S_OK;
  1650. }
  1651. HRESULT CISeqStream::Write(ISequentialStream* pISeqStream, ULONG* pcbWritten)
  1652. {
  1653. //Parameter checking
  1654. if(!pISeqStream)
  1655. return STG_E_INVALIDPOINTER;
  1656. if(pcbWritten)
  1657. *pcbWritten = 0;
  1658. HRESULT hr = S_OK;
  1659. ULONG cbRead = MAX_STREAM_BLOCK_SIZE;
  1660. //Need to read (in chunks) from the Stream passed in and sotre in our CISeqStream object
  1661. while(hr==S_OK && cbRead==MAX_STREAM_BLOCK_SIZE)
  1662. {
  1663. //Keep appending the data to the end of our buffer
  1664. SAFE_REALLOC(m_pBuffer, BYTE, m_cBufSize + MAX_STREAM_BLOCK_SIZE);
  1665. hr = pISeqStream->Read((BYTE*)m_pBuffer + m_cBufSize, MAX_STREAM_BLOCK_SIZE, &cbRead);
  1666. m_cBufSize += cbRead;
  1667. if(pcbWritten)
  1668. *pcbWritten += cbRead;
  1669. }
  1670. CLEANUP:
  1671. return S_OK;
  1672. }