ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

table.c
Go to the documentation of this file.
00001 /*
00002  * Implementation of the Microsoft Installer (msi.dll)
00003  *
00004  * Copyright 2002-2005 Mike McCormack for CodeWeavers
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Lesser General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2.1 of the License, or (at your option) any later version.
00010  *
00011  * This library is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  * Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public
00017  * License along with this library; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00019  */
00020 
00021 #include <stdarg.h>
00022 #include <assert.h>
00023 
00024 #define COBJMACROS
00025 #define NONAMELESSUNION
00026 #define NONAMELESSSTRUCT
00027 
00028 #include "windef.h"
00029 #include "winbase.h"
00030 #include "winerror.h"
00031 #include "msi.h"
00032 #include "msiquery.h"
00033 #include "objbase.h"
00034 #include "objidl.h"
00035 #include "winnls.h"
00036 #include "msipriv.h"
00037 #include "query.h"
00038 
00039 #include "wine/debug.h"
00040 #include "wine/unicode.h"
00041 
00042 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
00043 
00044 #define MSITABLE_HASH_TABLE_SIZE 37
00045 
00046 typedef struct tagMSICOLUMNHASHENTRY
00047 {
00048     struct tagMSICOLUMNHASHENTRY *next;
00049     UINT value;
00050     UINT row;
00051 } MSICOLUMNHASHENTRY;
00052 
00053 typedef struct tagMSICOLUMNINFO
00054 {
00055     LPCWSTR tablename;
00056     UINT    number;
00057     LPCWSTR colname;
00058     UINT    type;
00059     UINT    offset;
00060     INT     ref_count;
00061     BOOL    temporary;
00062     MSICOLUMNHASHENTRY **hash_table;
00063 } MSICOLUMNINFO;
00064 
00065 struct tagMSITABLE
00066 {
00067     BYTE **data;
00068     BOOL *data_persistent;
00069     UINT row_count;
00070     struct list entry;
00071     MSICOLUMNINFO *colinfo;
00072     UINT col_count;
00073     MSICONDITION persistent;
00074     INT ref_count;
00075     WCHAR name[1];
00076 };
00077 
00078 /* information for default tables */
00079 static const WCHAR szTables[]  = {'_','T','a','b','l','e','s',0};
00080 static const WCHAR szTable[]   = {'T','a','b','l','e',0};
00081 static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0};
00082 static const WCHAR szNumber[]  = {'N','u','m','b','e','r',0};
00083 static const WCHAR szType[]    = {'T','y','p','e',0};
00084 
00085 static const MSICOLUMNINFO _Columns_cols[4] = {
00086     { szColumns, 1, szTable,  MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL },
00087     { szColumns, 2, szNumber, MSITYPE_VALID | MSITYPE_KEY | 2,     2, 0, 0, NULL },
00088     { szColumns, 3, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 4, 0, 0, NULL },
00089     { szColumns, 4, szType,   MSITYPE_VALID | 2,                   6, 0, 0, NULL },
00090 };
00091 
00092 static const MSICOLUMNINFO _Tables_cols[1] = {
00093     { szTables,  1, szName,   MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL },
00094 };
00095 
00096 #define MAX_STREAM_NAME 0x1f
00097 
00098 static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref )
00099 {
00100     if( MSITYPE_IS_BINARY(col->type) )
00101         return 2;
00102 
00103     if( col->type & MSITYPE_STRING )
00104         return bytes_per_strref;
00105 
00106     if( (col->type & 0xff) <= 2)
00107         return 2;
00108 
00109     if( (col->type & 0xff) != 4 )
00110         ERR("Invalid column size!\n");
00111 
00112     return 4;
00113 }
00114 
00115 static int utf2mime(int x)
00116 {
00117     if( (x>='0') && (x<='9') )
00118         return x-'0';
00119     if( (x>='A') && (x<='Z') )
00120         return x-'A'+10;
00121     if( (x>='a') && (x<='z') )
00122         return x-'a'+10+26;
00123     if( x=='.' )
00124         return 10+26+26;
00125     if( x=='_' )
00126         return 10+26+26+1;
00127     return -1;
00128 }
00129 
00130 LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
00131 {
00132     DWORD count = MAX_STREAM_NAME;
00133     DWORD ch, next;
00134     LPWSTR out, p;
00135 
00136     if( !bTable )
00137         count = lstrlenW( in )+2;
00138     if (!(out = msi_alloc( count*sizeof(WCHAR) ))) return NULL;
00139     p = out;
00140 
00141     if( bTable )
00142     {
00143          *p++ = 0x4840;
00144          count --;
00145     }
00146     while( count -- ) 
00147     {
00148         ch = *in++;
00149         if( !ch )
00150         {
00151             *p = ch;
00152             return out;
00153         }
00154         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
00155         {
00156             ch = utf2mime(ch) + 0x4800;
00157             next = *in;
00158             if( next && (next<0x80) )
00159             {
00160                 next = utf2mime(next);
00161                 if( next != -1 )
00162                 {
00163                      next += 0x3ffffc0;
00164                      ch += (next<<6);
00165                      in++;
00166                 }
00167             }
00168         }
00169         *p++ = ch;
00170     }
00171     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
00172     msi_free( out );
00173     return NULL;
00174 }
00175 
00176 static int mime2utf(int x)
00177 {
00178     if( x<10 )
00179         return x + '0';
00180     if( x<(10+26))
00181         return x - 10 + 'A';
00182     if( x<(10+26+26))
00183         return x - 10 - 26 + 'a';
00184     if( x == (10+26+26) )
00185         return '.';
00186     return '_';
00187 }
00188 
00189 BOOL decode_streamname(LPCWSTR in, LPWSTR out)
00190 {
00191     WCHAR ch;
00192     DWORD count = 0;
00193 
00194     while ( (ch = *in++) )
00195     {
00196         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
00197         {
00198             if( ch >= 0x4800 )
00199                 ch = mime2utf(ch-0x4800);
00200             else
00201             {
00202                 ch -= 0x3800;
00203                 *out++ = mime2utf(ch&0x3f);
00204                 count++;
00205                 ch = mime2utf((ch>>6)&0x3f);
00206             }
00207         }
00208         *out++ = ch;
00209         count++;
00210     }
00211     *out = 0;
00212     return count;
00213 }
00214 
00215 void enum_stream_names( IStorage *stg )
00216 {
00217     IEnumSTATSTG *stgenum = NULL;
00218     HRESULT r;
00219     STATSTG stat;
00220     ULONG n, count;
00221     WCHAR name[0x40];
00222 
00223     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
00224     if( FAILED( r ) )
00225         return;
00226 
00227     n = 0;
00228     while( 1 )
00229     {
00230         count = 0;
00231         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
00232         if( FAILED( r ) || !count )
00233             break;
00234         decode_streamname( stat.pwcsName, name );
00235         TRACE("stream %2d -> %s %s\n", n,
00236               debugstr_w(stat.pwcsName), debugstr_w(name) );
00237         CoTaskMemFree( stat.pwcsName );
00238         n++;
00239     }
00240 
00241     IEnumSTATSTG_Release( stgenum );
00242 }
00243 
00244 UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table,
00245                        BYTE **pdata, UINT *psz )
00246 {
00247     HRESULT r;
00248     UINT ret = ERROR_FUNCTION_FAILED;
00249     VOID *data;
00250     ULONG sz, count;
00251     IStream *stm = NULL;
00252     STATSTG stat;
00253     LPWSTR encname;
00254 
00255     encname = encode_streamname(table, stname);
00256 
00257     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
00258 
00259     r = IStorage_OpenStream(stg, encname, NULL, 
00260             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
00261     msi_free( encname );
00262     if( FAILED( r ) )
00263     {
00264         WARN("open stream failed r = %08x - empty table?\n", r);
00265         return ret;
00266     }
00267 
00268     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
00269     if( FAILED( r ) )
00270     {
00271         WARN("open stream failed r = %08x!\n", r);
00272         goto end;
00273     }
00274 
00275     if( stat.cbSize.QuadPart >> 32 )
00276     {
00277         WARN("Too big!\n");
00278         goto end;
00279     }
00280         
00281     sz = stat.cbSize.QuadPart;
00282     data = msi_alloc( sz );
00283     if( !data )
00284     {
00285         WARN("couldn't allocate memory r=%08x!\n", r);
00286         ret = ERROR_NOT_ENOUGH_MEMORY;
00287         goto end;
00288     }
00289         
00290     r = IStream_Read(stm, data, sz, &count );
00291     if( FAILED( r ) || ( count != sz ) )
00292     {
00293         msi_free( data );
00294         WARN("read stream failed r = %08x!\n", r);
00295         goto end;
00296     }
00297 
00298     *pdata = data;
00299     *psz = sz;
00300     ret = ERROR_SUCCESS;
00301 
00302 end:
00303     IStream_Release( stm );
00304 
00305     return ret;
00306 }
00307 
00308 UINT write_stream_data( IStorage *stg, LPCWSTR stname,
00309                         LPCVOID data, UINT sz, BOOL bTable )
00310 {
00311     HRESULT r;
00312     UINT ret = ERROR_FUNCTION_FAILED;
00313     ULONG count;
00314     IStream *stm = NULL;
00315     ULARGE_INTEGER size;
00316     LARGE_INTEGER pos;
00317     LPWSTR encname;
00318 
00319     encname = encode_streamname(bTable, stname );
00320     r = IStorage_OpenStream( stg, encname, NULL, 
00321             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
00322     if( FAILED(r) )
00323     {
00324         r = IStorage_CreateStream( stg, encname,
00325                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
00326     }
00327     msi_free( encname );
00328     if( FAILED( r ) )
00329     {
00330         WARN("open stream failed r = %08x\n", r);
00331         return ret;
00332     }
00333 
00334     size.QuadPart = sz;
00335     r = IStream_SetSize( stm, size );
00336     if( FAILED( r ) )
00337     {
00338         WARN("Failed to SetSize\n");
00339         goto end;
00340     }
00341 
00342     pos.QuadPart = 0;
00343     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
00344     if( FAILED( r ) )
00345     {
00346         WARN("Failed to Seek\n");
00347         goto end;
00348     }
00349 
00350     if (sz)
00351     {
00352         r = IStream_Write(stm, data, sz, &count );
00353         if( FAILED( r ) || ( count != sz ) )
00354         {
00355             WARN("Failed to Write\n");
00356             goto end;
00357         }
00358     }
00359 
00360     ret = ERROR_SUCCESS;
00361 
00362 end:
00363     IStream_Release( stm );
00364 
00365     return ret;
00366 }
00367 
00368 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
00369 {
00370     UINT i;
00371     for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table );
00372 }
00373 
00374 static void free_table( MSITABLE *table )
00375 {
00376     UINT i;
00377     for( i=0; i<table->row_count; i++ )
00378         msi_free( table->data[i] );
00379     msi_free( table->data );
00380     msi_free( table->data_persistent );
00381     msi_free_colinfo( table->colinfo, table->col_count );
00382     msi_free( table->colinfo );
00383     msi_free( table );
00384 }
00385 
00386 static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref )
00387 {
00388     const MSICOLUMNINFO *last_col;
00389 
00390     if (!count)
00391         return 0;
00392 
00393     if (bytes_per_strref != LONG_STR_BYTES)
00394     {
00395         UINT i, size = 0;
00396         for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref );
00397         return size;
00398     }
00399     last_col = &cols[count - 1];
00400     return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref );
00401 }
00402 
00403 /* add this table to the list of cached tables in the database */
00404 static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg )
00405 {
00406     BYTE *rawdata = NULL;
00407     UINT rawsize = 0, i, j, row_size, row_size_mem;
00408 
00409     TRACE("%s\n",debugstr_w(t->name));
00410 
00411     row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref );
00412     row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES );
00413 
00414     /* if we can't read the table, just assume that it's empty */
00415     read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize );
00416     if( !rawdata )
00417         return ERROR_SUCCESS;
00418 
00419     TRACE("Read %d bytes\n", rawsize );
00420 
00421     if( rawsize % row_size )
00422     {
00423         WARN("Table size is invalid %d/%d\n", rawsize, row_size );
00424         goto err;
00425     }
00426 
00427     t->row_count = rawsize / row_size;
00428     t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) );
00429     if( !t->data )
00430         goto err;
00431     t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL));
00432     if ( !t->data_persistent )
00433         goto err;
00434 
00435     /* transpose all the data */
00436     TRACE("Transposing data from %d rows\n", t->row_count );
00437     for (i = 0; i < t->row_count; i++)
00438     {
00439         UINT ofs = 0, ofs_mem = 0;
00440 
00441         t->data[i] = msi_alloc( row_size_mem );
00442         if( !t->data[i] )
00443             goto err;
00444         t->data_persistent[i] = TRUE;
00445 
00446         for (j = 0; j < t->col_count; j++)
00447         {
00448             UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
00449             UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref );
00450             UINT k;
00451 
00452             if ( n != 2 && n != 3 && n != 4 )
00453             {
00454                 ERR("oops - unknown column width %d\n", n);
00455                 goto err;
00456             }
00457             if (t->colinfo[j].type & MSITYPE_STRING && n < m)
00458             {
00459                 for (k = 0; k < m; k++)
00460                 {
00461                     if (k < n)
00462                         t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
00463                     else
00464                         t->data[i][ofs_mem + k] = 0;
00465                 }
00466             }
00467             else
00468             {
00469                 for (k = 0; k < n; k++)
00470                     t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
00471             }
00472             ofs_mem += m;
00473             ofs += n;
00474         }
00475     }
00476 
00477     msi_free( rawdata );
00478     return ERROR_SUCCESS;
00479 err:
00480     msi_free( rawdata );
00481     return ERROR_FUNCTION_FAILED;
00482 }
00483 
00484 void free_cached_tables( MSIDATABASE *db )
00485 {
00486     while( !list_empty( &db->tables ) )
00487     {
00488         MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
00489 
00490         list_remove( &t->entry );
00491         free_table( t );
00492     }
00493 }
00494 
00495 static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
00496 {
00497     MSITABLE *t;
00498 
00499     LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
00500         if( !strcmpW( name, t->name ) )
00501             return t;
00502 
00503     return NULL;
00504 }
00505 
00506 static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count )
00507 {
00508     DWORD i;
00509 
00510     for (i = 0; colinfo && i < count; i++)
00511     {
00512          assert( i + 1 == colinfo[i].number );
00513          if (i) colinfo[i].offset = colinfo[i - 1].offset +
00514                                     bytes_per_column( db, &colinfo[i - 1], LONG_STR_BYTES );
00515          else colinfo[i].offset = 0;
00516 
00517          TRACE("column %d is [%s] with type %08x ofs %d\n",
00518                colinfo[i].number, debugstr_w(colinfo[i].colname),
00519                colinfo[i].type, colinfo[i].offset);
00520     }
00521 }
00522 
00523 static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz )
00524 {
00525     const MSICOLUMNINFO *p;
00526     DWORD i, n;
00527 
00528     TRACE("%s\n", debugstr_w(name));
00529 
00530     if (!strcmpW( name, szTables ))
00531     {
00532         p = _Tables_cols;
00533         n = 1;
00534     }
00535     else if (!strcmpW( name, szColumns ))
00536     {
00537         p = _Columns_cols;
00538         n = 4;
00539     }
00540     else return ERROR_FUNCTION_FAILED;
00541 
00542     for (i = 0; i < n; i++)
00543     {
00544         if (colinfo && i < *sz) colinfo[i] = p[i];
00545         if (colinfo && i >= *sz) break;
00546     }
00547     table_calc_column_offsets( db, colinfo, n );
00548     *sz = n;
00549     return ERROR_SUCCESS;
00550 }
00551 
00552 static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz );
00553 
00554 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
00555 {
00556     UINT r, column_count = 0;
00557     MSICOLUMNINFO *columns;
00558 
00559     /* get the number of columns in this table */
00560     column_count = 0;
00561     r = get_tablecolumns( db, name, NULL, &column_count );
00562     if (r != ERROR_SUCCESS)
00563         return r;
00564 
00565     *pcount = column_count;
00566 
00567     /* if there's no columns, there's no table */
00568     if (!column_count)
00569         return ERROR_INVALID_PARAMETER;
00570 
00571     TRACE("table %s found\n", debugstr_w(name));
00572 
00573     columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) );
00574     if (!columns)
00575         return ERROR_FUNCTION_FAILED;
00576 
00577     r = get_tablecolumns( db, name, columns, &column_count );
00578     if (r != ERROR_SUCCESS)
00579     {
00580         msi_free( columns );
00581         return ERROR_FUNCTION_FAILED;
00582     }
00583     *pcols = columns;
00584     return r;
00585 }
00586 
00587 static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret )
00588 {
00589     MSITABLE *table;
00590     UINT r;
00591 
00592     /* first, see if the table is cached */
00593     table = find_cached_table( db, name );
00594     if (table)
00595     {
00596         *table_ret = table;
00597         return ERROR_SUCCESS;
00598     }
00599 
00600     /* nonexistent tables should be interpreted as empty tables */
00601     table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) );
00602     if (!table)
00603         return ERROR_FUNCTION_FAILED;
00604 
00605     table->row_count = 0;
00606     table->data = NULL;
00607     table->data_persistent = NULL;
00608     table->colinfo = NULL;
00609     table->col_count = 0;
00610     table->persistent = MSICONDITION_TRUE;
00611     lstrcpyW( table->name, name );
00612 
00613     if (!strcmpW( name, szTables ) || !strcmpW( name, szColumns ))
00614         table->persistent = MSICONDITION_NONE;
00615 
00616     r = table_get_column_info( db, name, &table->colinfo, &table->col_count );
00617     if (r != ERROR_SUCCESS)
00618     {
00619         free_table( table );
00620         return r;
00621     }
00622     r = read_table_from_storage( db, table, db->storage );
00623     if (r != ERROR_SUCCESS)
00624     {
00625         free_table( table );
00626         return r;
00627     }
00628     list_add_head( &db->tables, &table->entry );
00629     *table_ret = table;
00630     return ERROR_SUCCESS;
00631 }
00632 
00633 static UINT read_table_int( BYTE *const *data, UINT row, UINT col, UINT bytes )
00634 {
00635     UINT ret = 0, i;
00636 
00637     for (i = 0; i < bytes; i++)
00638         ret += data[row][col + i] << i * 8;
00639 
00640     return ret;
00641 }
00642 
00643 static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz )
00644 {
00645     UINT r, i, n = 0, table_id, count, maxcount = *sz;
00646     MSITABLE *table = NULL;
00647 
00648     TRACE("%s\n", debugstr_w(szTableName));
00649 
00650     /* first check if there is a default table with that name */
00651     r = get_defaulttablecolumns( db, szTableName, colinfo, sz );
00652     if (r == ERROR_SUCCESS && *sz)
00653         return r;
00654 
00655     r = get_table( db, szColumns, &table );
00656     if (r != ERROR_SUCCESS)
00657     {
00658         ERR("couldn't load _Columns table\n");
00659         return ERROR_FUNCTION_FAILED;
00660     }
00661 
00662     /* convert table and column names to IDs from the string table */
00663     r = msi_string2idW( db->strings, szTableName, &table_id );
00664     if (r != ERROR_SUCCESS)
00665     {
00666         WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
00667         return r;
00668     }
00669     TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
00670 
00671     /* Note: _Columns table doesn't have non-persistent data */
00672 
00673     /* if maxcount is non-zero, assume it's exactly right for this table */
00674     memset( colinfo, 0, maxcount * sizeof(*colinfo) );
00675     count = table->row_count;
00676     for (i = 0; i < count; i++)
00677     {
00678         if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue;
00679         if (colinfo)
00680         {
00681             UINT id = read_table_int( table->data, i, table->colinfo[2].offset, LONG_STR_BYTES );
00682             UINT col = read_table_int( table->data, i, table->colinfo[1].offset, sizeof(USHORT) ) - (1 << 15);
00683 
00684             /* check the column number is in range */
00685             if (col < 1 || col > maxcount)
00686             {
00687                 ERR("column %d out of range\n", col);
00688                 continue;
00689             }
00690             /* check if this column was already set */
00691             if (colinfo[col - 1].number)
00692             {
00693                 ERR("duplicate column %d\n", col);
00694                 continue;
00695             }
00696             colinfo[col - 1].tablename = msi_string_lookup_id( db->strings, table_id );
00697             colinfo[col - 1].number = col;
00698             colinfo[col - 1].colname = msi_string_lookup_id( db->strings, id );
00699             colinfo[col - 1].type = read_table_int( table->data, i, table->colinfo[3].offset,
00700                                                     sizeof(USHORT) ) - (1 << 15);
00701             colinfo[col - 1].offset = 0;
00702             colinfo[col - 1].ref_count = 0;
00703             colinfo[col - 1].hash_table = NULL;
00704         }
00705         n++;
00706     }
00707     TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
00708 
00709     if (colinfo && n != maxcount)
00710     {
00711         ERR("missing column in table %s\n", debugstr_w(szTableName));
00712         msi_free_colinfo( colinfo, maxcount );
00713         return ERROR_FUNCTION_FAILED;
00714     }
00715     table_calc_column_offsets( db, colinfo, n );
00716     *sz = n;
00717     return ERROR_SUCCESS;
00718 }
00719 
00720 UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info,
00721                        MSICONDITION persistent )
00722 {
00723     enum StringPersistence string_persistence = (persistent) ? StringPersistent : StringNonPersistent;
00724     UINT r, nField;
00725     MSIVIEW *tv = NULL;
00726     MSIRECORD *rec = NULL;
00727     column_info *col;
00728     MSITABLE *table;
00729     UINT i;
00730 
00731     /* only add tables that don't exist already */
00732     if( TABLE_Exists(db, name ) )
00733     {
00734         WARN("table %s exists\n", debugstr_w(name));
00735         return ERROR_BAD_QUERY_SYNTAX;
00736     }
00737 
00738     table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
00739     if( !table )
00740         return ERROR_FUNCTION_FAILED;
00741 
00742     table->ref_count = 1;
00743     table->row_count = 0;
00744     table->data = NULL;
00745     table->data_persistent = NULL;
00746     table->colinfo = NULL;
00747     table->col_count = 0;
00748     table->persistent = persistent;
00749     lstrcpyW( table->name, name );
00750 
00751     for( col = col_info; col; col = col->next )
00752         table->col_count++;
00753 
00754     table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) );
00755     if (!table->colinfo)
00756     {
00757         free_table( table );
00758         return ERROR_FUNCTION_FAILED;
00759     }
00760 
00761     for( i = 0, col = col_info; col; i++, col = col->next )
00762     {
00763         UINT table_id = msi_addstringW( db->strings, col->table, -1, 1, string_persistence );
00764         UINT col_id = msi_addstringW( db->strings, col->column, -1, 1, string_persistence );
00765 
00766         table->colinfo[ i ].tablename = msi_string_lookup_id( db->strings, table_id );
00767         table->colinfo[ i ].number = i + 1;
00768         table->colinfo[ i ].colname = msi_string_lookup_id( db->strings, col_id );
00769         table->colinfo[ i ].type = col->type;
00770         table->colinfo[ i ].offset = 0;
00771         table->colinfo[ i ].ref_count = 0;
00772         table->colinfo[ i ].hash_table = NULL;
00773         table->colinfo[ i ].temporary = col->temporary;
00774     }
00775     table_calc_column_offsets( db, table->colinfo, table->col_count);
00776 
00777     r = TABLE_CreateView( db, szTables, &tv );
00778     TRACE("CreateView returned %x\n", r);
00779     if( r )
00780     {
00781         free_table( table );
00782         return r;
00783     }
00784 
00785     r = tv->ops->execute( tv, 0 );
00786     TRACE("tv execute returned %x\n", r);
00787     if( r )
00788         goto err;
00789 
00790     rec = MSI_CreateRecord( 1 );
00791     if( !rec )
00792         goto err;
00793 
00794     r = MSI_RecordSetStringW( rec, 1, name );
00795     if( r )
00796         goto err;
00797 
00798     r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE );
00799     TRACE("insert_row returned %x\n", r);
00800     if( r )
00801         goto err;
00802 
00803     tv->ops->delete( tv );
00804     tv = NULL;
00805 
00806     msiobj_release( &rec->hdr );
00807     rec = NULL;
00808 
00809     if( persistent != MSICONDITION_FALSE )
00810     {
00811         /* add each column to the _Columns table */
00812         r = TABLE_CreateView( db, szColumns, &tv );
00813         if( r )
00814             return r;
00815 
00816         r = tv->ops->execute( tv, 0 );
00817         TRACE("tv execute returned %x\n", r);
00818         if( r )
00819             goto err;
00820 
00821         rec = MSI_CreateRecord( 4 );
00822         if( !rec )
00823             goto err;
00824 
00825         r = MSI_RecordSetStringW( rec, 1, name );
00826         if( r )
00827             goto err;
00828 
00829         /*
00830          * need to set the table, column number, col name and type
00831          * for each column we enter in the table
00832          */
00833         nField = 1;
00834         for( col = col_info; col; col = col->next )
00835         {
00836             r = MSI_RecordSetInteger( rec, 2, nField );
00837             if( r )
00838                 goto err;
00839 
00840             r = MSI_RecordSetStringW( rec, 3, col->column );
00841             if( r )
00842                 goto err;
00843 
00844             r = MSI_RecordSetInteger( rec, 4, col->type );
00845             if( r )
00846                 goto err;
00847 
00848             r = tv->ops->insert_row( tv, rec, -1, FALSE );
00849             if( r )
00850                 goto err;
00851 
00852             nField++;
00853         }
00854         if( !col )
00855             r = ERROR_SUCCESS;
00856     }
00857 
00858 err:
00859     if (rec)
00860         msiobj_release( &rec->hdr );
00861     /* FIXME: remove values from the string table on error */
00862     if( tv )
00863         tv->ops->delete( tv );
00864 
00865     if (r == ERROR_SUCCESS)
00866         list_add_head( &db->tables, &table->entry );
00867     else
00868         free_table( table );
00869 
00870     return r;
00871 }
00872 
00873 static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref )
00874 {
00875     BYTE *rawdata = NULL;
00876     UINT rawsize, i, j, row_size, row_count;
00877     UINT r = ERROR_FUNCTION_FAILED;
00878 
00879     /* Nothing to do for non-persistent tables */
00880     if( t->persistent == MSICONDITION_FALSE )
00881         return ERROR_SUCCESS;
00882 
00883     TRACE("Saving %s\n", debugstr_w( t->name ) );
00884 
00885     row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref );
00886     row_count = t->row_count;
00887     for (i = 0; i < t->row_count; i++)
00888     {
00889         if (!t->data_persistent[i])
00890         {
00891             row_count = 1; /* yes, this is bizarre */
00892             break;
00893         }
00894     }
00895     rawsize = row_count * row_size;
00896     rawdata = msi_alloc_zero( rawsize );
00897     if( !rawdata )
00898     {
00899         r = ERROR_NOT_ENOUGH_MEMORY;
00900         goto err;
00901     }
00902 
00903     rawsize = 0;
00904     for (i = 0; i < t->row_count; i++)
00905     {
00906         UINT ofs = 0, ofs_mem = 0;
00907 
00908         if (!t->data_persistent[i]) break;
00909 
00910         for (j = 0; j < t->col_count; j++)
00911         {
00912             UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
00913             UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref );
00914             UINT k;
00915 
00916             if (n != 2 && n != 3 && n != 4)
00917             {
00918                 ERR("oops - unknown column width %d\n", n);
00919                 goto err;
00920             }
00921             if (t->colinfo[j].type & MSITYPE_STRING && n < m)
00922             {
00923                 UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES );
00924                 if (id > 1 << bytes_per_strref * 8)
00925                 {
00926                     ERR("string id %u out of range\n", id);
00927                     goto err;
00928                 }
00929             }
00930             for (k = 0; k < n; k++)
00931             {
00932                 rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k];
00933             }
00934             ofs_mem += m;
00935             ofs += n;
00936         }
00937         rawsize += row_size;
00938     }
00939 
00940     TRACE("writing %d bytes\n", rawsize);
00941     r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE );
00942 
00943 err:
00944     msi_free( rawdata );
00945     return r;
00946 }
00947 
00948 static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name )
00949 {
00950     MSITABLE *table;
00951     UINT size, offset, old_count;
00952     UINT n;
00953 
00954     table = find_cached_table( db, name );
00955     old_count = table->col_count;
00956     msi_free_colinfo( table->colinfo, table->col_count );
00957     msi_free( table->colinfo );
00958     table->colinfo = NULL;
00959 
00960     table_get_column_info( db, name, &table->colinfo, &table->col_count );
00961     if (!table->col_count) return;
00962 
00963     size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES );
00964     offset = table->colinfo[table->col_count - 1].offset;
00965 
00966     for ( n = 0; n < table->row_count; n++ )
00967     {
00968         table->data[n] = msi_realloc( table->data[n], size );
00969         if (old_count < table->col_count)
00970             memset( &table->data[n][offset], 0, size - offset );
00971     }
00972 }
00973 
00974 /* try to find the table name in the _Tables table */
00975 BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name )
00976 {
00977     UINT r, table_id, i;
00978     MSITABLE *table;
00979 
00980     if( !strcmpW( name, szTables ) || !strcmpW( name, szColumns ) ||
00981         !strcmpW( name, szStreams ) || !strcmpW( name, szStorages ) )
00982         return TRUE;
00983 
00984     r = msi_string2idW( db->strings, name, &table_id );
00985     if( r != ERROR_SUCCESS )
00986     {
00987         TRACE("Couldn't find id for %s\n", debugstr_w(name));
00988         return FALSE;
00989     }
00990 
00991     r = get_table( db, szTables, &table );
00992     if( r != ERROR_SUCCESS )
00993     {
00994         ERR("table %s not available\n", debugstr_w(szTables));
00995         return FALSE;
00996     }
00997 
00998     for( i = 0; i < table->row_count; i++ )
00999     {
01000         if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id )
01001             return TRUE;
01002     }
01003 
01004     return FALSE;
01005 }
01006 
01007 /* below is the query interface to a table */
01008 
01009 typedef struct tagMSITABLEVIEW
01010 {
01011     MSIVIEW        view;
01012     MSIDATABASE   *db;
01013     MSITABLE      *table;
01014     MSICOLUMNINFO *columns;
01015     UINT           num_cols;
01016     UINT           row_size;
01017     WCHAR          name[1];
01018 } MSITABLEVIEW;
01019 
01020 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
01021 {
01022     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01023     UINT offset, n;
01024 
01025     if( !tv->table )
01026         return ERROR_INVALID_PARAMETER;
01027 
01028     if( (col==0) || (col>tv->num_cols) )
01029         return ERROR_INVALID_PARAMETER;
01030 
01031     /* how many rows are there ? */
01032     if( row >= tv->table->row_count )
01033         return ERROR_NO_MORE_ITEMS;
01034 
01035     if( tv->columns[col-1].offset >= tv->row_size )
01036     {
01037         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
01038         ERR("%p %p\n", tv, tv->columns );
01039         return ERROR_FUNCTION_FAILED;
01040     }
01041 
01042     n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
01043     if (n != 2 && n != 3 && n != 4)
01044     {
01045         ERR("oops! what is %d bytes per column?\n", n );
01046         return ERROR_FUNCTION_FAILED;
01047     }
01048 
01049     offset = tv->columns[col-1].offset;
01050     *val = read_table_int(tv->table->data, row, offset, n);
01051 
01052     /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
01053 
01054     return ERROR_SUCCESS;
01055 }
01056 
01057 static UINT msi_stream_name( const MSITABLEVIEW *tv, UINT row, LPWSTR *pstname )
01058 {
01059     LPWSTR p, stname = NULL;
01060     UINT i, r, type, ival;
01061     DWORD len;
01062     LPCWSTR sval;
01063     MSIVIEW *view = (MSIVIEW *) tv;
01064 
01065     TRACE("%p %d\n", tv, row);
01066 
01067     len = lstrlenW( tv->name ) + 1;
01068     stname = msi_alloc( len*sizeof(WCHAR) );
01069     if ( !stname )
01070     {
01071        r = ERROR_OUTOFMEMORY;
01072        goto err;
01073     }
01074 
01075     lstrcpyW( stname, tv->name );
01076 
01077     for ( i = 0; i < tv->num_cols; i++ )
01078     {
01079         type = tv->columns[i].type;
01080         if ( type & MSITYPE_KEY )
01081         {
01082             WCHAR number[0x20];
01083 
01084             r = TABLE_fetch_int( view, row, i+1, &ival );
01085             if ( r != ERROR_SUCCESS )
01086                 goto err;
01087 
01088             if ( tv->columns[i].type & MSITYPE_STRING )
01089             {
01090                 sval = msi_string_lookup_id( tv->db->strings, ival );
01091                 if ( !sval )
01092                 {
01093                     r = ERROR_INVALID_PARAMETER;
01094                     goto err;
01095                 }
01096             }
01097             else
01098             {
01099                 static const WCHAR fmt[] = { '%','d',0 };
01100                 UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES );
01101 
01102                 switch( n )
01103                 {
01104                 case 2:
01105                     sprintfW( number, fmt, ival-0x8000 );
01106                     break;
01107                 case 4:
01108                     sprintfW( number, fmt, ival^0x80000000 );
01109                     break;
01110                 default:
01111                     ERR( "oops - unknown column width %d\n", n );
01112                     r = ERROR_FUNCTION_FAILED;
01113                     goto err;
01114                 }
01115                 sval = number;
01116             }
01117 
01118             len += lstrlenW( szDot ) + lstrlenW( sval );
01119             p = msi_realloc ( stname, len*sizeof(WCHAR) );
01120             if ( !p )
01121             {
01122                 r = ERROR_OUTOFMEMORY;
01123                 goto err;
01124             }
01125             stname = p;
01126 
01127             lstrcatW( stname, szDot );
01128             lstrcatW( stname, sval );
01129         }
01130         else
01131            continue;
01132     }
01133 
01134     *pstname = stname;
01135     return ERROR_SUCCESS;
01136 
01137 err:
01138     msi_free( stname );
01139     *pstname = NULL;
01140     return r;
01141 }
01142 
01143 /*
01144  * We need a special case for streams, as we need to reference column with
01145  * the name of the stream in the same table, and the table name
01146  * which may not be available at higher levels of the query
01147  */
01148 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
01149 {
01150     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01151     UINT r;
01152     LPWSTR encname, full_name = NULL;
01153 
01154     if( !view->ops->fetch_int )
01155         return ERROR_INVALID_PARAMETER;
01156 
01157     r = msi_stream_name( tv, row, &full_name );
01158     if ( r != ERROR_SUCCESS )
01159     {
01160         ERR("fetching stream, error = %d\n", r);
01161         return r;
01162     }
01163 
01164     encname = encode_streamname( FALSE, full_name );
01165     r = msi_get_raw_stream( tv->db, encname, stm );
01166     if( r )
01167         ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
01168 
01169     msi_free( full_name );
01170     msi_free( encname );
01171     return r;
01172 }
01173 
01174 static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
01175 {
01176     UINT offset, n, i;
01177 
01178     if( !tv->table )
01179         return ERROR_INVALID_PARAMETER;
01180 
01181     if( (col==0) || (col>tv->num_cols) )
01182         return ERROR_INVALID_PARAMETER;
01183 
01184     if( row >= tv->table->row_count )
01185         return ERROR_INVALID_PARAMETER;
01186 
01187     if( tv->columns[col-1].offset >= tv->row_size )
01188     {
01189         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
01190         ERR("%p %p\n", tv, tv->columns );
01191         return ERROR_FUNCTION_FAILED;
01192     }
01193 
01194     msi_free( tv->columns[col-1].hash_table );
01195     tv->columns[col-1].hash_table = NULL;
01196 
01197     n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
01198     if ( n != 2 && n != 3 && n != 4 )
01199     {
01200         ERR("oops! what is %d bytes per column?\n", n );
01201         return ERROR_FUNCTION_FAILED;
01202     }
01203 
01204     offset = tv->columns[col-1].offset;
01205     for ( i = 0; i < n; i++ )
01206         tv->table->data[row][offset + i] = (val >> i * 8) & 0xff;
01207 
01208     return ERROR_SUCCESS;
01209 }
01210 
01211 static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
01212 {
01213     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
01214 
01215     if (!tv->table)
01216         return ERROR_INVALID_PARAMETER;
01217 
01218     return msi_view_get_row(tv->db, view, row, rec);
01219 }
01220 
01221 static UINT msi_addstreamW( MSIDATABASE *db, LPCWSTR name, IStream *data )
01222 {
01223     static const WCHAR insert[] = {
01224         'I','N','S','E','R','T',' ','I','N','T','O',' ',
01225         '`','_','S','t','r','e','a','m','s','`',' ',
01226         '(','`','N','a','m','e','`',',','`','D','a','t','a','`',')',' ',
01227         'V','A','L','U','E','S',' ','(','?',',','?',')',0};
01228     MSIQUERY *query = NULL;
01229     MSIRECORD *rec;
01230     UINT r;
01231 
01232     TRACE("%p %s %p\n", db, debugstr_w(name), data);
01233 
01234     rec = MSI_CreateRecord( 2 );
01235     if ( !rec )
01236         return ERROR_OUTOFMEMORY;
01237 
01238     r = MSI_RecordSetStringW( rec, 1, name );
01239     if ( r != ERROR_SUCCESS )
01240        goto err;
01241 
01242     r = MSI_RecordSetIStream( rec, 2, data );
01243     if ( r != ERROR_SUCCESS )
01244        goto err;
01245 
01246     r = MSI_DatabaseOpenViewW( db, insert, &query );
01247     if ( r != ERROR_SUCCESS )
01248        goto err;
01249 
01250     r = MSI_ViewExecute( query, rec );
01251 
01252 err:
01253     msiobj_release( &query->hdr );
01254     msiobj_release( &rec->hdr );
01255     return r;
01256 }
01257 
01258 static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue )
01259 {
01260     MSICOLUMNINFO columninfo;
01261     UINT r;
01262 
01263     if ( (iField <= 0) ||
01264          (iField > tv->num_cols) ||
01265           MSI_RecordIsNull( rec, iField ) )
01266         return ERROR_FUNCTION_FAILED;
01267 
01268     columninfo = tv->columns[ iField - 1 ];
01269 
01270     if ( MSITYPE_IS_BINARY(columninfo.type) )
01271     {
01272         *pvalue = 1; /* refers to the first key column */
01273     }
01274     else if ( columninfo.type & MSITYPE_STRING )
01275     {
01276         LPCWSTR sval = MSI_RecordGetString( rec, iField );
01277         if (sval)
01278         {
01279             r = msi_string2idW(tv->db->strings, sval, pvalue);
01280             if (r != ERROR_SUCCESS)
01281                 return ERROR_NOT_FOUND;
01282         }
01283         else *pvalue = 0;
01284     }
01285     else if ( bytes_per_column( tv->db, &columninfo, LONG_STR_BYTES ) == 2 )
01286     {
01287         *pvalue = 0x8000 + MSI_RecordGetInteger( rec, iField );
01288         if ( *pvalue & 0xffff0000 )
01289         {
01290             ERR("field %u value %d out of range\n", iField, *pvalue - 0x8000);
01291             return ERROR_FUNCTION_FAILED;
01292         }
01293     }
01294     else
01295     {
01296         INT ival = MSI_RecordGetInteger( rec, iField );
01297         *pvalue = ival ^ 0x80000000;
01298     }
01299 
01300     return ERROR_SUCCESS;
01301 }
01302 
01303 static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
01304 {
01305     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01306     UINT i, val, r = ERROR_SUCCESS;
01307 
01308     if ( !tv->table )
01309         return ERROR_INVALID_PARAMETER;
01310 
01311     /* test if any of the mask bits are invalid */
01312     if ( mask >= (1<<tv->num_cols) )
01313         return ERROR_INVALID_PARAMETER;
01314 
01315     for ( i = 0; i < tv->num_cols; i++ )
01316     {
01317         BOOL persistent;
01318 
01319         /* only update the fields specified in the mask */
01320         if ( !(mask&(1<<i)) )
01321             continue;
01322 
01323         persistent = (tv->table->persistent != MSICONDITION_FALSE) &&
01324                      (tv->table->data_persistent[row]);
01325         /* FIXME: should we allow updating keys? */
01326 
01327         val = 0;
01328         if ( !MSI_RecordIsNull( rec, i + 1 ) )
01329         {
01330             r = get_table_value_from_record (tv, rec, i + 1, &val);
01331             if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
01332             {
01333                 IStream *stm;
01334                 LPWSTR stname;
01335 
01336                 if ( r != ERROR_SUCCESS )
01337                     return ERROR_FUNCTION_FAILED;
01338 
01339                 r = MSI_RecordGetIStream( rec, i + 1, &stm );
01340                 if ( r != ERROR_SUCCESS )
01341                     return r;
01342 
01343                 r = msi_stream_name( tv, row, &stname );
01344                 if ( r != ERROR_SUCCESS )
01345                 {
01346                     IStream_Release( stm );
01347                     return r;
01348                 }
01349 
01350                 r = msi_addstreamW( tv->db, stname, stm );
01351                 IStream_Release( stm );
01352                 msi_free ( stname );
01353 
01354                 if ( r != ERROR_SUCCESS )
01355                     return r;
01356             }
01357             else if ( tv->columns[i].type & MSITYPE_STRING )
01358             {
01359                 UINT x;
01360 
01361                 if ( r != ERROR_SUCCESS )
01362                 {
01363                     LPCWSTR sval = MSI_RecordGetString( rec, i + 1 );
01364                     val = msi_addstringW( tv->db->strings, sval, -1, 1,
01365                       persistent ? StringPersistent : StringNonPersistent );
01366                 }
01367                 else
01368                 {
01369                     TABLE_fetch_int(&tv->view, row, i + 1, &x);
01370                     if (val == x)
01371                         continue;
01372                 }
01373             }
01374             else
01375             {
01376                 if ( r != ERROR_SUCCESS )
01377                     return ERROR_FUNCTION_FAILED;
01378             }
01379         }
01380 
01381         r = TABLE_set_int( tv, row, i+1, val );
01382         if ( r != ERROR_SUCCESS )
01383             break;
01384     }
01385     return r;
01386 }
01387 
01388 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary )
01389 {
01390     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01391     BYTE **p, *row;
01392     BOOL *b;
01393     UINT sz;
01394     BYTE ***data_ptr;
01395     BOOL **data_persist_ptr;
01396     UINT *row_count;
01397 
01398     TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE");
01399 
01400     if( !tv->table )
01401         return ERROR_INVALID_PARAMETER;
01402 
01403     row = msi_alloc_zero( tv->row_size );
01404     if( !row )
01405         return ERROR_NOT_ENOUGH_MEMORY;
01406 
01407     row_count = &tv->table->row_count;
01408     data_ptr = &tv->table->data;
01409     data_persist_ptr = &tv->table->data_persistent;
01410     if (*num == -1)
01411         *num = tv->table->row_count;
01412 
01413     sz = (*row_count + 1) * sizeof (BYTE*);
01414     if( *data_ptr )
01415         p = msi_realloc( *data_ptr, sz );
01416     else
01417         p = msi_alloc( sz );
01418     if( !p )
01419     {
01420         msi_free( row );
01421         return ERROR_NOT_ENOUGH_MEMORY;
01422     }
01423 
01424     sz = (*row_count + 1) * sizeof (BOOL);
01425     if( *data_persist_ptr )
01426         b = msi_realloc( *data_persist_ptr, sz );
01427     else
01428         b = msi_alloc( sz );
01429     if( !b )
01430     {
01431         msi_free( row );
01432         msi_free( p );
01433         return ERROR_NOT_ENOUGH_MEMORY;
01434     }
01435 
01436     *data_ptr = p;
01437     (*data_ptr)[*row_count] = row;
01438 
01439     *data_persist_ptr = b;
01440     (*data_persist_ptr)[*row_count] = !temporary;
01441 
01442     (*row_count)++;
01443 
01444     return ERROR_SUCCESS;
01445 }
01446 
01447 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
01448 {
01449     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01450 
01451     TRACE("%p %p\n", tv, record);
01452 
01453     TRACE("There are %d columns\n", tv->num_cols );
01454 
01455     return ERROR_SUCCESS;
01456 }
01457 
01458 static UINT TABLE_close( struct tagMSIVIEW *view )
01459 {
01460     TRACE("%p\n", view );
01461     
01462     return ERROR_SUCCESS;
01463 }
01464 
01465 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
01466 {
01467     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01468 
01469     TRACE("%p %p %p\n", view, rows, cols );
01470 
01471     if( cols )
01472         *cols = tv->num_cols;
01473     if( rows )
01474     {
01475         if( !tv->table )
01476             return ERROR_INVALID_PARAMETER;
01477         *rows = tv->table->row_count;
01478     }
01479 
01480     return ERROR_SUCCESS;
01481 }
01482 
01483 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
01484                 UINT n, LPCWSTR *name, UINT *type, BOOL *temporary,
01485                 LPCWSTR *table_name )
01486 {
01487     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01488 
01489     TRACE("%p %d %p %p\n", tv, n, name, type );
01490 
01491     if( ( n == 0 ) || ( n > tv->num_cols ) )
01492         return ERROR_INVALID_PARAMETER;
01493 
01494     if( name )
01495     {
01496         *name = tv->columns[n-1].colname;
01497         if( !*name )
01498             return ERROR_FUNCTION_FAILED;
01499     }
01500 
01501     if( table_name )
01502     {
01503         *table_name = tv->columns[n-1].tablename;
01504         if( !*table_name )
01505             return ERROR_FUNCTION_FAILED;
01506     }
01507 
01508     if( type )
01509         *type = tv->columns[n-1].type;
01510 
01511     if( temporary )
01512         *temporary = tv->columns[n-1].temporary;
01513 
01514     return ERROR_SUCCESS;
01515 }
01516 
01517 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column );
01518 
01519 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column )
01520 {
01521     UINT r, row, i;
01522 
01523     /* check there's no null values where they're not allowed */
01524     for( i = 0; i < tv->num_cols; i++ )
01525     {
01526         if ( tv->columns[i].type & MSITYPE_NULLABLE )
01527             continue;
01528 
01529         if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
01530             TRACE("skipping binary column\n");
01531         else if ( tv->columns[i].type & MSITYPE_STRING )
01532         {
01533             LPCWSTR str;
01534 
01535             str = MSI_RecordGetString( rec, i+1 );
01536             if (str == NULL || str[0] == 0)
01537             {
01538                 if (column) *column = i;
01539                 return ERROR_INVALID_DATA;
01540             }
01541         }
01542         else
01543         {
01544             UINT n;
01545 
01546             n = MSI_RecordGetInteger( rec, i+1 );
01547             if (n == MSI_NULL_INTEGER)
01548             {
01549                 if (column) *column = i;
01550                 return ERROR_INVALID_DATA;
01551             }
01552         }
01553     }
01554 
01555     /* check there's no duplicate keys */
01556     r = msi_table_find_row( tv, rec, &row, column );
01557     if (r == ERROR_SUCCESS)
01558         return ERROR_FUNCTION_FAILED;
01559 
01560     return ERROR_SUCCESS;
01561 }
01562 
01563 static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec )
01564 {
01565     UINT r, i, ivalue, x;
01566 
01567     for (i = 0; i < tv->num_cols; i++ )
01568     {
01569         if (!(tv->columns[i].type & MSITYPE_KEY)) continue;
01570 
01571         r = get_table_value_from_record( tv, rec, i + 1, &ivalue );
01572         if (r != ERROR_SUCCESS)
01573             return 1;
01574 
01575         r = TABLE_fetch_int( &tv->view, row, i + 1, &x );
01576         if (r != ERROR_SUCCESS)
01577         {
01578             WARN("TABLE_fetch_int should not fail here %u\n", r);
01579             return -1;
01580         }
01581         if (ivalue > x)
01582         {
01583             return 1;
01584         }
01585         else if (ivalue == x)
01586         {
01587             if (i < tv->num_cols - 1) continue;
01588             return 0;
01589         }
01590         else
01591             return -1;
01592     }
01593     return 1;
01594 }
01595 
01596 static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec )
01597 {
01598     int idx, c, low = 0, high = tv->table->row_count - 1;
01599 
01600     TRACE("%p %p\n", tv, rec);
01601 
01602     while (low <= high)
01603     {
01604         idx = (low + high) / 2;
01605         c = compare_record( tv, idx, rec );
01606 
01607         if (c < 0)
01608             high = idx - 1;
01609         else if (c > 0)
01610             low = idx + 1;
01611         else
01612         {
01613             TRACE("found %u\n", idx);
01614             return idx;
01615         }
01616     }
01617     TRACE("found %u\n", high + 1);
01618     return high + 1;
01619 }
01620 
01621 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
01622 {
01623     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01624     UINT i, r;
01625 
01626     TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" );
01627 
01628     /* check that the key is unique - can we find a matching row? */
01629     r = table_validate_new( tv, rec, NULL );
01630     if( r != ERROR_SUCCESS )
01631         return ERROR_FUNCTION_FAILED;
01632 
01633     if (row == -1)
01634         row = find_insert_index( tv, rec );
01635 
01636     r = table_create_new_row( view, &row, temporary );
01637     TRACE("insert_row returned %08x\n", r);
01638     if( r != ERROR_SUCCESS )
01639         return r;
01640 
01641     /* shift the rows to make room for the new row */
01642     for (i = tv->table->row_count - 1; i > row; i--)
01643     {
01644         memmove(&(tv->table->data[i][0]),
01645                 &(tv->table->data[i - 1][0]), tv->row_size);
01646         tv->table->data_persistent[i] = tv->table->data_persistent[i - 1];
01647     }
01648 
01649     /* Re-set the persistence flag */
01650     tv->table->data_persistent[row] = !temporary;
01651     return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
01652 }
01653 
01654 static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row )
01655 {
01656     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01657     UINT r, num_rows, num_cols, i;
01658 
01659     TRACE("%p %d\n", tv, row);
01660 
01661     if ( !tv->table )
01662         return ERROR_INVALID_PARAMETER;
01663 
01664     r = TABLE_get_dimensions( view, &num_rows, &num_cols );
01665     if ( r != ERROR_SUCCESS )
01666         return r;
01667 
01668     if ( row >= num_rows )
01669         return ERROR_FUNCTION_FAILED;
01670 
01671     num_rows = tv->table->row_count;
01672     tv->table->row_count--;
01673 
01674     /* reset the hash tables */
01675     for (i = 0; i < tv->num_cols; i++)
01676     {
01677         msi_free( tv->columns[i].hash_table );
01678         tv->columns[i].hash_table = NULL;
01679     }
01680 
01681     for (i = row + 1; i < num_rows; i++)
01682     {
01683         memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size);
01684         tv->table->data_persistent[i - 1] = tv->table->data_persistent[i];
01685     }
01686 
01687     msi_free(tv->table->data[num_rows - 1]);
01688 
01689     return ERROR_SUCCESS;
01690 }
01691 
01692 static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row)
01693 {
01694     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
01695     UINT r, new_row;
01696 
01697     /* FIXME: MsiViewFetch should set rec index 0 to some ID that
01698      * sets the fetched record apart from other records
01699      */
01700 
01701     if (!tv->table)
01702         return ERROR_INVALID_PARAMETER;
01703 
01704     r = msi_table_find_row(tv, rec, &new_row, NULL);
01705     if (r != ERROR_SUCCESS)
01706     {
01707         ERR("can't find row to modify\n");
01708         return ERROR_FUNCTION_FAILED;
01709     }
01710 
01711     /* the row cannot be changed */
01712     if (row != new_row + 1)
01713         return ERROR_FUNCTION_FAILED;
01714 
01715     return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1);
01716 }
01717 
01718 static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
01719 {
01720     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
01721     UINT r, row;
01722 
01723     if (!tv->table)
01724         return ERROR_INVALID_PARAMETER;
01725 
01726     r = msi_table_find_row(tv, rec, &row, NULL);
01727     if (r == ERROR_SUCCESS)
01728         return TABLE_set_row(view, row, rec, (1 << tv->num_cols) - 1);
01729     else
01730         return TABLE_insert_row( view, rec, -1, FALSE );
01731 }
01732 
01733 static UINT modify_delete_row( struct tagMSIVIEW *view, MSIRECORD *rec )
01734 {
01735     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
01736     UINT row, r;
01737 
01738     r = msi_table_find_row(tv, rec, &row, NULL);
01739     if (r != ERROR_SUCCESS)
01740         return r;
01741 
01742     return TABLE_delete_row(view, row);
01743 }
01744 
01745 static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row )
01746 {
01747     MSIRECORD *curr;
01748     UINT r, i, count;
01749 
01750     r = TABLE_get_row(view, row - 1, &curr);
01751     if (r != ERROR_SUCCESS)
01752         return r;
01753 
01754     /* Close the original record */
01755     MSI_CloseRecord(&rec->hdr);
01756 
01757     count = MSI_RecordGetFieldCount(rec);
01758     for (i = 0; i < count; i++)
01759         MSI_RecordCopyField(curr, i + 1, rec, i + 1);
01760 
01761     msiobj_release(&curr->hdr);
01762     return ERROR_SUCCESS;
01763 }
01764 
01765 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
01766                           MSIRECORD *rec, UINT row)
01767 {
01768     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01769     UINT r, frow, column;
01770 
01771     TRACE("%p %d %p\n", view, eModifyMode, rec );
01772 
01773     switch (eModifyMode)
01774     {
01775     case MSIMODIFY_DELETE:
01776         r = modify_delete_row( view, rec );
01777         break;
01778     case MSIMODIFY_VALIDATE_NEW:
01779         r = table_validate_new( tv, rec, &column );
01780         if (r != ERROR_SUCCESS)
01781         {
01782             tv->view.error = MSIDBERROR_DUPLICATEKEY;
01783             tv->view.error_column = tv->columns[column].colname;
01784             r = ERROR_INVALID_DATA;
01785         }
01786         break;
01787 
01788     case MSIMODIFY_INSERT:
01789         r = table_validate_new( tv, rec, NULL );
01790         if (r != ERROR_SUCCESS)
01791             break;
01792         r = TABLE_insert_row( view, rec, -1, FALSE );
01793         break;
01794 
01795     case MSIMODIFY_INSERT_TEMPORARY:
01796         r = table_validate_new( tv, rec, NULL );
01797         if (r != ERROR_SUCCESS)
01798             break;
01799         r = TABLE_insert_row( view, rec, -1, TRUE );
01800         break;
01801 
01802     case MSIMODIFY_REFRESH:
01803         r = msi_refresh_record( view, rec, row );
01804         break;
01805 
01806     case MSIMODIFY_UPDATE:
01807         r = msi_table_update( view, rec, row );
01808         break;
01809 
01810     case MSIMODIFY_ASSIGN:
01811         r = msi_table_assign( view, rec );
01812         break;
01813 
01814     case MSIMODIFY_MERGE:
01815         /* check row that matches this record */
01816         r = msi_table_find_row( tv, rec, &frow, &column );
01817         if (r != ERROR_SUCCESS)
01818         {
01819             r = table_validate_new( tv, rec, NULL );
01820             if (r == ERROR_SUCCESS)
01821                 r = TABLE_insert_row( view, rec, -1, FALSE );
01822         }
01823         break;
01824 
01825     case MSIMODIFY_REPLACE:
01826     case MSIMODIFY_VALIDATE:
01827     case MSIMODIFY_VALIDATE_FIELD:
01828     case MSIMODIFY_VALIDATE_DELETE:
01829         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
01830         r = ERROR_CALL_NOT_IMPLEMENTED;
01831         break;
01832 
01833     default:
01834         r = ERROR_INVALID_DATA;
01835     }
01836 
01837     return r;
01838 }
01839 
01840 static UINT TABLE_delete( struct tagMSIVIEW *view )
01841 {
01842     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01843 
01844     TRACE("%p\n", view );
01845 
01846     tv->table = NULL;
01847     tv->columns = NULL;
01848 
01849     msi_free( tv );
01850 
01851     return ERROR_SUCCESS;
01852 }
01853 
01854 static UINT TABLE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
01855     UINT val, UINT *row, MSIITERHANDLE *handle )
01856 {
01857     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01858     const MSICOLUMNHASHENTRY *entry;
01859 
01860     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
01861 
01862     if( !tv->table )
01863         return ERROR_INVALID_PARAMETER;
01864 
01865     if( (col==0) || (col > tv->num_cols) )
01866         return ERROR_INVALID_PARAMETER;
01867 
01868     if( !tv->columns[col-1].hash_table )
01869     {
01870         UINT i;
01871         UINT num_rows = tv->table->row_count;
01872         MSICOLUMNHASHENTRY **hash_table;
01873         MSICOLUMNHASHENTRY *new_entry;
01874 
01875         if( tv->columns[col-1].offset >= tv->row_size )
01876         {
01877             ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
01878             ERR("%p %p\n", tv, tv->columns );
01879             return ERROR_FUNCTION_FAILED;
01880         }
01881 
01882         /* allocate contiguous memory for the table and its entries so we
01883          * don't have to do an expensive cleanup */
01884         hash_table = msi_alloc(MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*) +
01885             num_rows * sizeof(MSICOLUMNHASHENTRY));
01886         if (!hash_table)
01887             return ERROR_OUTOFMEMORY;
01888 
01889         memset(hash_table, 0, MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*));
01890         tv->columns[col-1].hash_table = hash_table;
01891 
01892         new_entry = (MSICOLUMNHASHENTRY *)(hash_table + MSITABLE_HASH_TABLE_SIZE);
01893 
01894         for (i = 0; i < num_rows; i++, new_entry++)
01895         {
01896             UINT row_value;
01897 
01898             if (view->ops->fetch_int( view, i, col, &row_value ) != ERROR_SUCCESS)
01899                 continue;
01900 
01901             new_entry->next = NULL;
01902             new_entry->value = row_value;
01903             new_entry->row = i;
01904             if (hash_table[row_value % MSITABLE_HASH_TABLE_SIZE])
01905             {
01906                 MSICOLUMNHASHENTRY *prev_entry = hash_table[row_value % MSITABLE_HASH_TABLE_SIZE];
01907                 while (prev_entry->next)
01908                     prev_entry = prev_entry->next;
01909                 prev_entry->next = new_entry;
01910             }
01911             else
01912                 hash_table[row_value % MSITABLE_HASH_TABLE_SIZE] = new_entry;
01913         }
01914     }
01915 
01916     if( !*handle )
01917         entry = tv->columns[col-1].hash_table[val % MSITABLE_HASH_TABLE_SIZE];
01918     else
01919         entry = (*handle)->next;
01920 
01921     while (entry && entry->value != val)
01922         entry = entry->next;
01923 
01924     *handle = entry;
01925     if (!entry)
01926         return ERROR_NO_MORE_ITEMS;
01927 
01928     *row = entry->row;
01929 
01930     return ERROR_SUCCESS;
01931 }
01932 
01933 static UINT TABLE_add_ref(struct tagMSIVIEW *view)
01934 {
01935     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01936     UINT i;
01937 
01938     TRACE("%p %d\n", view, tv->table->ref_count);
01939 
01940     for (i = 0; i < tv->table->col_count; i++)
01941     {
01942         if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
01943             InterlockedIncrement(&tv->table->colinfo[i].ref_count);
01944     }
01945 
01946     return InterlockedIncrement(&tv->table->ref_count);
01947 }
01948 
01949 static UINT TABLE_remove_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number)
01950 {
01951     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01952     MSIRECORD *rec = NULL;
01953     MSIVIEW *columns = NULL;
01954     UINT row, r;
01955 
01956     rec = MSI_CreateRecord(2);
01957     if (!rec)
01958         return ERROR_OUTOFMEMORY;
01959 
01960     MSI_RecordSetStringW(rec, 1, table);
01961     MSI_RecordSetInteger(rec, 2, number);
01962 
01963     r = TABLE_CreateView(tv->db, szColumns, &columns);
01964     if (r != ERROR_SUCCESS)
01965         return r;
01966 
01967     r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL);
01968     if (r != ERROR_SUCCESS)
01969         goto done;
01970 
01971     r = TABLE_delete_row(columns, row);
01972     if (r != ERROR_SUCCESS)
01973         goto done;
01974 
01975     msi_update_table_columns(tv->db, table);
01976 
01977 done:
01978     msiobj_release(&rec->hdr);
01979     columns->ops->delete(columns);
01980     return r;
01981 }
01982 
01983 static UINT TABLE_release(struct tagMSIVIEW *view)
01984 {
01985     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
01986     INT ref = tv->table->ref_count;
01987     UINT i, r;
01988 
01989     TRACE("%p %d\n", view, ref);
01990 
01991     for (i = 0; i < tv->table->col_count; i++)
01992     {
01993         if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
01994         {
01995             ref = InterlockedDecrement(&tv->table->colinfo[i].ref_count);
01996             if (ref == 0)
01997             {
01998                 r = TABLE_remove_column(view, tv->table->colinfo[i].tablename,
01999                                         tv->table->colinfo[i].number);
02000                 if (r != ERROR_SUCCESS)
02001                     break;
02002             }
02003         }
02004     }
02005 
02006     ref = InterlockedDecrement(&tv->table->ref_count);
02007     if (ref == 0)
02008     {
02009         if (!tv->table->row_count)
02010         {
02011             list_remove(&tv->table->entry);
02012             free_table(tv->table);
02013             TABLE_delete(view);
02014         }
02015     }
02016 
02017     return ref;
02018 }
02019 
02020 static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number,
02021                              LPCWSTR column, UINT type, BOOL hold)
02022 {
02023     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
02024     MSITABLE *msitable;
02025     MSIRECORD *rec;
02026     UINT r, i;
02027 
02028     rec = MSI_CreateRecord(4);
02029     if (!rec)
02030         return ERROR_OUTOFMEMORY;
02031 
02032     MSI_RecordSetStringW(rec, 1, table);
02033     MSI_RecordSetInteger(rec, 2, number);
02034     MSI_RecordSetStringW(rec, 3, column);
02035     MSI_RecordSetInteger(rec, 4, type);
02036 
02037     r = TABLE_insert_row(&tv->view, rec, -1, FALSE);
02038     if (r != ERROR_SUCCESS)
02039         goto done;
02040 
02041     msi_update_table_columns(tv->db, table);
02042 
02043     if (!hold)
02044         goto done;
02045 
02046     msitable = find_cached_table(tv->db, table);
02047     for (i = 0; i < msitable->col_count; i++)
02048     {
02049         if (!strcmpW( msitable->colinfo[i].colname, column ))
02050         {
02051             InterlockedIncrement(&msitable->colinfo[i].ref_count);
02052             break;
02053         }
02054     }
02055 
02056 done:
02057     msiobj_release(&rec->hdr);
02058     return r;
02059 }
02060 
02061 static UINT TABLE_drop(struct tagMSIVIEW *view)
02062 {
02063     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
02064     MSIVIEW *tables = NULL;
02065     MSIRECORD *rec = NULL;
02066     UINT r, row;
02067     INT i;
02068 
02069     TRACE("dropping table %s\n", debugstr_w(tv->name));
02070 
02071     for (i = tv->table->col_count - 1; i >= 0; i--)
02072     {
02073         r = TABLE_remove_column(view, tv->table->colinfo[i].tablename,
02074                                 tv->table->colinfo[i].number);
02075         if (r != ERROR_SUCCESS)
02076             return r;
02077     }
02078 
02079     rec = MSI_CreateRecord(1);
02080     if (!rec)
02081         return ERROR_OUTOFMEMORY;
02082 
02083     MSI_RecordSetStringW(rec, 1, tv->name);
02084 
02085     r = TABLE_CreateView(tv->db, szTables, &tables);
02086     if (r != ERROR_SUCCESS)
02087         return r;
02088 
02089     r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row, NULL);
02090     if (r != ERROR_SUCCESS)
02091         goto done;
02092 
02093     r = TABLE_delete_row(tables, row);
02094     if (r != ERROR_SUCCESS)
02095         goto done;
02096 
02097     list_remove(&tv->table->entry);
02098     free_table(tv->table);
02099 
02100 done:
02101     msiobj_release(&rec->hdr);
02102     tables->ops->delete(tables);
02103 
02104     return r;
02105 }
02106 
02107 static const MSIVIEWOPS table_ops =
02108 {
02109     TABLE_fetch_int,
02110     TABLE_fetch_stream,
02111     TABLE_get_row,
02112     TABLE_set_row,
02113     TABLE_insert_row,
02114     TABLE_delete_row,
02115     TABLE_execute,
02116     TABLE_close,
02117     TABLE_get_dimensions,
02118     TABLE_get_column_info,
02119     TABLE_modify,
02120     TABLE_delete,
02121     TABLE_find_matching_rows,
02122     TABLE_add_ref,
02123     TABLE_release,
02124     TABLE_add_column,
02125     TABLE_remove_column,
02126     NULL,
02127     TABLE_drop,
02128 };
02129 
02130 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
02131 {
02132     MSITABLEVIEW *tv ;
02133     UINT r, sz;
02134 
02135     TRACE("%p %s %p\n", db, debugstr_w(name), view );
02136 
02137     if ( !strcmpW( name, szStreams ) )
02138         return STREAMS_CreateView( db, view );
02139     else if ( !strcmpW( name, szStorages ) )
02140         return STORAGES_CreateView( db, view );
02141 
02142     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
02143     tv = msi_alloc_zero( sz );
02144     if( !tv )
02145         return ERROR_FUNCTION_FAILED;
02146 
02147     r = get_table( db, name, &tv->table );
02148     if( r != ERROR_SUCCESS )
02149     {
02150         msi_free( tv );
02151         WARN("table not found\n");
02152         return r;
02153     }
02154 
02155     TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count);
02156 
02157     /* fill the structure */
02158     tv->view.ops = &table_ops;
02159     tv->db = db;
02160     tv->columns = tv->table->colinfo;
02161     tv->num_cols = tv->table->col_count;
02162     tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES );
02163 
02164     TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
02165 
02166     *view = (MSIVIEW*) tv;
02167     lstrcpyW( tv->name, name );
02168 
02169     return ERROR_SUCCESS;
02170 }
02171 
02172 UINT MSI_CommitTables( MSIDATABASE *db )
02173 {
02174     UINT r, bytes_per_strref;
02175     HRESULT hr;
02176     MSITABLE *table = NULL;
02177 
02178     TRACE("%p\n",db);
02179 
02180     r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref );
02181     if( r != ERROR_SUCCESS )
02182     {
02183         WARN("failed to save string table r=%08x\n",r);
02184         return r;
02185     }
02186 
02187     LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
02188     {
02189         r = save_table( db, table, bytes_per_strref );
02190         if( r != ERROR_SUCCESS )
02191         {
02192             WARN("failed to save table %s (r=%08x)\n",
02193                   debugstr_w(table->name), r);
02194             return r;
02195         }
02196     }
02197 
02198     hr = IStorage_Commit( db->storage, 0 );
02199     if (FAILED( hr ))
02200     {
02201         WARN("failed to commit changes 0x%08x\n", hr);
02202         r = ERROR_FUNCTION_FAILED;
02203     }
02204     return r;
02205 }
02206 
02207 MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
02208 {
02209     MSITABLE *t;
02210     UINT r;
02211 
02212     TRACE("%p %s\n", db, debugstr_w(table));
02213 
02214     if (!table)
02215         return MSICONDITION_ERROR;
02216 
02217     r = get_table( db, table, &t );
02218     if (r != ERROR_SUCCESS)
02219         return MSICONDITION_NONE;
02220 
02221     return t->persistent;
02222 }
02223 
02224 static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes)
02225 {
02226     UINT ret = 0, i;
02227 
02228     for (i = 0; i < bytes; i++)
02229         ret += (data[col + i] << i * 8);
02230 
02231     return ret;
02232 }
02233 
02234 static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname )
02235 {
02236     LPWSTR stname = NULL, sval, p;
02237     DWORD len;
02238     UINT i, r;
02239 
02240     TRACE("%p %p\n", tv, rec);
02241 
02242     len = lstrlenW( tv->name ) + 1;
02243     stname = msi_alloc( len*sizeof(WCHAR) );
02244     if ( !stname )
02245     {
02246        r = ERROR_OUTOFMEMORY;
02247        goto err;
02248     }
02249 
02250     lstrcpyW( stname, tv->name );
02251 
02252     for ( i = 0; i < tv->num_cols; i++ )
02253     {
02254         if ( tv->columns[i].type & MSITYPE_KEY )
02255         {
02256             sval = msi_dup_record_field( rec, i + 1 );
02257             if ( !sval )
02258             {
02259                 r = ERROR_OUTOFMEMORY;
02260                 goto err;
02261             }
02262 
02263             len += lstrlenW( szDot ) + lstrlenW ( sval );
02264             p = msi_realloc ( stname, len*sizeof(WCHAR) );
02265             if ( !p )
02266             {
02267                 r = ERROR_OUTOFMEMORY;
02268                 goto err;
02269             }
02270             stname = p;
02271 
02272             lstrcatW( stname, szDot );
02273             lstrcatW( stname, sval );
02274 
02275             msi_free( sval );
02276         }
02277         else
02278             continue;
02279     }
02280 
02281     *pstname = encode_streamname( FALSE, stname );
02282     msi_free( stname );
02283 
02284     return ERROR_SUCCESS;
02285 
02286 err:
02287     msi_free ( stname );
02288     *pstname = NULL;
02289     return r;
02290 }
02291 
02292 static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st,
02293                                             IStorage *stg,
02294                                             const BYTE *rawdata, UINT bytes_per_strref )
02295 {
02296     UINT i, val, ofs = 0;
02297     USHORT mask;
02298     MSICOLUMNINFO *columns = tv->columns;
02299     MSIRECORD *rec;
02300 
02301     mask = rawdata[0] | (rawdata[1] << 8);
02302     rawdata += 2;
02303 
02304     rec = MSI_CreateRecord( tv->num_cols );
02305     if( !rec )
02306         return rec;
02307 
02308     TRACE("row ->\n");
02309     for( i=0; i<tv->num_cols; i++ )
02310     {
02311         if ( (mask&1) && (i>=(mask>>8)) )
02312             break;
02313         /* all keys must be present */
02314         if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
02315             continue;
02316 
02317         if( MSITYPE_IS_BINARY(tv->columns[i].type) )
02318         {
02319             LPWSTR encname;
02320             IStream *stm = NULL;
02321             UINT r;
02322 
02323             ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref );
02324 
02325             r = msi_record_encoded_stream_name( tv, rec, &encname );
02326             if ( r != ERROR_SUCCESS )
02327                 return NULL;
02328 
02329             r = IStorage_OpenStream( stg, encname, NULL,
02330                      STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
02331             msi_free( encname );
02332             if ( r != ERROR_SUCCESS )
02333                 return NULL;
02334 
02335             MSI_RecordSetStream( rec, i+1, stm );
02336             TRACE(" field %d [%s]\n", i+1, debugstr_w(encname));
02337         }
02338         else if( columns[i].type & MSITYPE_STRING )
02339         {
02340             LPCWSTR sval;
02341 
02342             val = read_raw_int(rawdata, ofs, bytes_per_strref);
02343             sval = msi_string_lookup_id( st, val );
02344             MSI_RecordSetStringW( rec, i+1, sval );
02345             TRACE(" field %d [%s]\n", i+1, debugstr_w(sval));
02346             ofs += bytes_per_strref;
02347         }
02348         else
02349         {
02350             UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref );
02351             switch( n )
02352             {
02353             case 2:
02354                 val = read_raw_int(rawdata, ofs, n);
02355                 if (val)
02356                     MSI_RecordSetInteger( rec, i+1, val-0x8000 );
02357                 TRACE(" field %d [0x%04x]\n", i+1, val );
02358                 break;
02359             case 4:
02360                 val = read_raw_int(rawdata, ofs, n);
02361                 if (val)
02362                     MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
02363                 TRACE(" field %d [0x%08x]\n", i+1, val );
02364                 break;
02365             default:
02366                 ERR("oops - unknown column width %d\n", n);
02367                 break;
02368             }
02369             ofs += n;
02370         }
02371     }
02372     return rec;
02373 }
02374 
02375 static void dump_record( MSIRECORD *rec )
02376 {
02377     UINT i, n;
02378 
02379     n = MSI_RecordGetFieldCount( rec );
02380     for( i=1; i<=n; i++ )
02381     {
02382         LPCWSTR sval;
02383 
02384         if( MSI_RecordIsNull( rec, i ) )
02385             TRACE("row -> []\n");
02386         else if( (sval = MSI_RecordGetString( rec, i )) )
02387             TRACE("row -> [%s]\n", debugstr_w(sval));
02388         else
02389             TRACE("row -> [0x%08x]\n", MSI_RecordGetInteger( rec, i ) );
02390     }
02391 }
02392 
02393 static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize )
02394 {
02395     LPCWSTR sval;
02396     UINT i;
02397 
02398     for( i=0; i<(rawsize/2); i++ )
02399     {
02400         sval = msi_string_lookup_id( st, rawdata[i] );
02401         MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
02402     }
02403 }
02404 
02405 static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec )
02406 {
02407     LPCWSTR str;
02408     UINT i, r, *data;
02409 
02410     data = msi_alloc( tv->num_cols *sizeof (UINT) );
02411     for( i=0; i<tv->num_cols; i++ )
02412     {
02413         data[i] = 0;
02414 
02415         if ( ~tv->columns[i].type & MSITYPE_KEY )
02416             continue;
02417 
02418         /* turn the transform column value into a row value */
02419         if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
02420              ! MSITYPE_IS_BINARY(tv->columns[i].type) )
02421         {
02422             str = MSI_RecordGetString( rec, i+1 );
02423             if (str)
02424             {
02425                 r = msi_string2idW( tv->db->strings, str, &data[i] );
02426 
02427                 /* if there's no matching string in the string table,
02428                    these keys can't match any record, so fail now. */
02429                 if (r != ERROR_SUCCESS)
02430                 {
02431                     msi_free( data );
02432                     return NULL;
02433                 }
02434             }
02435             else data[i] = 0;
02436         }
02437         else
02438         {
02439             data[i] = MSI_RecordGetInteger( rec, i+1 );
02440 
02441             if (data[i] == MSI_NULL_INTEGER)
02442                 data[i] = 0;
02443             else if ((tv->columns[i].type&0xff) == 2)
02444                 data[i] += 0x8000;
02445             else
02446                 data[i] += 0x80000000;
02447         }
02448     }
02449     return data;
02450 }
02451 
02452 static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column )
02453 {
02454     UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
02455 
02456     for( i=0; i<tv->num_cols; i++ )
02457     {
02458         if ( ~tv->columns[i].type & MSITYPE_KEY )
02459             continue;
02460 
02461         /* turn the transform column value into a row value */
02462         r = TABLE_fetch_int( &tv->view, row, i+1, &x );
02463         if ( r != ERROR_SUCCESS )
02464         {
02465             ERR("TABLE_fetch_int shouldn't fail here\n");
02466             break;
02467         }
02468 
02469         /* if this key matches, move to the next column */
02470         if ( x != data[i] )
02471         {
02472             ret = ERROR_FUNCTION_FAILED;
02473             break;
02474         }
02475         if (column) *column = i;
02476         ret = ERROR_SUCCESS;
02477     }
02478     return ret;
02479 }
02480 
02481 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column )
02482 {
02483     UINT i, r = ERROR_FUNCTION_FAILED, *data;
02484 
02485     data = msi_record_to_row( tv, rec );
02486     if( !data )
02487         return r;
02488     for( i = 0; i < tv->table->row_count; i++ )
02489     {
02490         r = msi_row_matches( tv, i, data, column );
02491         if( r == ERROR_SUCCESS )
02492         {
02493             *row = i;
02494             break;
02495         }
02496     }
02497     msi_free( data );
02498     return r;
02499 }
02500 
02501 typedef struct
02502 {
02503     struct list entry;
02504     LPWSTR name;
02505 } TRANSFORMDATA;
02506 
02507 static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
02508                                       string_table *st, TRANSFORMDATA *transform,
02509                                       UINT bytes_per_strref )
02510 {
02511     BYTE *rawdata = NULL;
02512     MSITABLEVIEW *tv = NULL;
02513     UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0;
02514     MSIRECORD *rec = NULL;
02515     WCHAR coltable[32];
02516     const WCHAR *name;
02517 
02518     if (!transform)
02519         return ERROR_SUCCESS;
02520 
02521     name = transform->name;
02522 
02523     coltable[0] = 0;
02524     TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
02525 
02526     /* read the transform data */
02527     read_stream_data( stg, name, TRUE, &rawdata, &rawsize );
02528     if ( !rawdata )
02529     {
02530         TRACE("table %s empty\n", debugstr_w(name) );
02531         return ERROR_INVALID_TABLE;
02532     }
02533 
02534     /* create a table view */
02535     r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
02536     if( r != ERROR_SUCCESS )
02537         goto err;
02538 
02539     r = tv->view.ops->execute( &tv->view, NULL );
02540     if( r != ERROR_SUCCESS )
02541         goto err;
02542 
02543     TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
02544           debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
02545 
02546     /* interpret the data */
02547     for (n = 0; n < rawsize;)
02548     {
02549         mask = rawdata[n] | (rawdata[n + 1] << 8);
02550         if (mask & 1)
02551         {
02552             /*
02553              * if the low bit is set, columns are continuous and
02554              * the number of columns is specified in the high byte
02555              */
02556             sz = 2;
02557             num_cols = mask >> 8;
02558             for (i = 0; i < num_cols; i++)
02559             {
02560                 if( (tv->columns[i].type & MSITYPE_STRING) &&
02561                     ! MSITYPE_IS_BINARY(tv->columns[i].type) )
02562                     sz += bytes_per_strref;
02563                 else
02564                     sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
02565             }
02566         }
02567         else
02568         {
02569             /*
02570              * If the low bit is not set, mask is a bitmask.
02571              * Excepting for key fields, which are always present,
02572              *  each bit indicates that a field is present in the transform record.
02573              *
02574              * mask == 0 is a special case ... only the keys will be present
02575              * and it means that this row should be deleted.
02576              */
02577             sz = 2;
02578             num_cols = tv->num_cols;
02579             for (i = 0; i < num_cols; i++)
02580             {
02581                 if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask))
02582                 {
02583                     if ((tv->columns[i].type & MSITYPE_STRING) &&
02584                         !MSITYPE_IS_BINARY(tv->columns[i].type))
02585                         sz += bytes_per_strref;
02586                     else
02587                         sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
02588                 }
02589             }
02590         }
02591 
02592         /* check we didn't run of the end of the table */
02593         if (n + sz > rawsize)
02594         {
02595             ERR("borked.\n");
02596             dump_table( st, (USHORT *)rawdata, rawsize );
02597             break;
02598         }
02599 
02600         rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref );
02601         if (rec)
02602         {
02603             WCHAR table[32];
02604             DWORD sz = 32;
02605             UINT number = MSI_NULL_INTEGER;
02606             UINT row = 0;
02607 
02608             if (!strcmpW( name, szColumns ))
02609             {
02610                 MSI_RecordGetStringW( rec, 1, table, &sz );
02611                 number = MSI_RecordGetInteger( rec, 2 );
02612 
02613                 /*
02614                  * Native msi seems writes nul into the Number (2nd) column of
02615                  * the _Columns table, only when the columns are from a new table
02616                  */
02617                 if ( number == MSI_NULL_INTEGER )
02618                 {
02619                     /* reset the column number on a new table */
02620                     if (strcmpW( coltable, table ))
02621                     {
02622                         colcol = 0;
02623                         lstrcpyW( coltable, table );
02624                     }
02625 
02626                     /* fix nul column numbers */
02627                     MSI_RecordSetInteger( rec, 2, ++colcol );
02628                 }
02629             }
02630 
02631             if (TRACE_ON(msidb)) dump_record( rec );
02632 
02633             r = msi_table_find_row( tv, rec, &row, NULL );
02634             if (r == ERROR_SUCCESS)
02635             {
02636                 if (!mask)
02637                 {
02638                     TRACE("deleting row [%d]:\n", row);
02639                     r = TABLE_delete_row( &tv->view, row );
02640                     if (r != ERROR_SUCCESS)
02641                         WARN("failed to delete row %u\n", r);
02642                 }
02643                 else if (mask & 1)
02644                 {
02645                     TRACE("modifying full row [%d]:\n", row);
02646                     r = TABLE_set_row( &tv->view, row, rec, (1 << tv->num_cols) - 1 );
02647                     if (r != ERROR_SUCCESS)
02648                         WARN("failed to modify row %u\n", r);
02649                 }
02650                 else
02651                 {
02652                     TRACE("modifying masked row [%d]:\n", row);
02653                     r = TABLE_set_row( &tv->view, row, rec, mask );
02654                     if (r != ERROR_SUCCESS)
02655                         WARN("failed to modify row %u\n", r);
02656                 }
02657             }
02658             else
02659             {
02660                 TRACE("inserting row\n");
02661                 r = TABLE_insert_row( &tv->view, rec, -1, FALSE );
02662                 if (r != ERROR_SUCCESS)
02663                     WARN("failed to insert row %u\n", r);
02664             }
02665 
02666             if (number != MSI_NULL_INTEGER && !strcmpW( name, szColumns ))
02667                 msi_update_table_columns( db, table );
02668 
02669             msiobj_release( &rec->hdr );
02670         }
02671 
02672         n += sz;
02673     }
02674 
02675 err:
02676     /* no need to free the table, it's associated with the database */
02677     msi_free( rawdata );
02678     if( tv )
02679         tv->view.ops->delete( &tv->view );
02680 
02681     return ERROR_SUCCESS;
02682 }
02683 
02684 /*
02685  * msi_table_apply_transform
02686  *
02687  * Enumerate the table transforms in a transform storage and apply each one.
02688  */
02689 UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
02690 {
02691     struct list transforms;
02692     IEnumSTATSTG *stgenum = NULL;
02693     TRANSFORMDATA *transform;
02694     TRANSFORMDATA *tables = NULL, *columns = NULL;
02695     HRESULT r;
02696     STATSTG stat;
02697     string_table *strings;
02698     UINT ret = ERROR_FUNCTION_FAILED;
02699     UINT bytes_per_strref;
02700 
02701     TRACE("%p %p\n", db, stg );
02702 
02703     strings = msi_load_string_table( stg, &bytes_per_strref );
02704     if( !strings )
02705         goto end;
02706 
02707     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
02708     if( FAILED( r ) )
02709         goto end;
02710 
02711     list_init(&transforms);
02712 
02713     while ( TRUE )
02714     {
02715         MSITABLEVIEW *tv = NULL;
02716         WCHAR name[0x40];
02717         ULONG count = 0;
02718 
02719         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
02720         if ( FAILED( r ) || !count )
02721             break;
02722 
02723         decode_streamname( stat.pwcsName, name );
02724         CoTaskMemFree( stat.pwcsName );
02725         if ( name[0] != 0x4840 )
02726             continue;
02727 
02728         if ( !strcmpW( name+1, szStringPool ) ||
02729              !strcmpW( name+1, szStringData ) )
02730             continue;
02731 
02732         transform = msi_alloc_zero( sizeof(TRANSFORMDATA) );
02733         if ( !transform )
02734             break;
02735 
02736         list_add_tail( &transforms, &transform->entry );
02737 
02738         transform->name = strdupW( name + 1 );
02739 
02740         if ( !strcmpW( transform->name, szTables ) )
02741             tables = transform;
02742         else if (!strcmpW( transform->name, szColumns ) )
02743             columns = transform;
02744 
02745         TRACE("transform contains stream %s\n", debugstr_w(name));
02746 
02747         /* load the table */
02748         r = TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv );
02749         if( r != ERROR_SUCCESS )
02750             continue;
02751 
02752         r = tv->view.ops->execute( &tv->view, NULL );
02753         if( r != ERROR_SUCCESS )
02754         {
02755             tv->view.ops->delete( &tv->view );
02756             continue;
02757         }
02758 
02759         tv->view.ops->delete( &tv->view );
02760     }
02761 
02762     /*
02763      * Apply _Tables and _Columns transforms first so that
02764      * the table metadata is correct, and empty tables exist.
02765      */
02766     ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref );
02767     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
02768         goto end;
02769 
02770     ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref );
02771     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
02772         goto end;
02773 
02774     ret = ERROR_SUCCESS;
02775 
02776     while ( !list_empty( &transforms ) )
02777     {
02778         transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry );
02779 
02780         if ( strcmpW( transform->name, szColumns ) &&
02781              strcmpW( transform->name, szTables ) &&
02782              ret == ERROR_SUCCESS )
02783         {
02784             ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref );
02785         }
02786 
02787         list_remove( &transform->entry );
02788         msi_free( transform->name );
02789         msi_free( transform );
02790     }
02791 
02792     if ( ret == ERROR_SUCCESS )
02793         append_storage_to_db( db, stg );
02794 
02795 end:
02796     if ( stgenum )
02797         IEnumSTATSTG_Release( stgenum );
02798     if ( strings )
02799         msi_destroy_stringtable( strings );
02800 
02801     return ret;
02802 }

Generated on Sun May 27 2012 04:25:20 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.