home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / dbmsg / oledb / tablecopy / table.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-03-12  |  63.9 KB  |  2,077 lines

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