1c2c66affSColin Finck /* 2c2c66affSColin Finck * Implementation of the Microsoft Installer (msi.dll) 3c2c66affSColin Finck * 4c2c66affSColin Finck * Copyright 2002-2005 Mike McCormack for CodeWeavers 5c2c66affSColin Finck * 6c2c66affSColin Finck * This library is free software; you can redistribute it and/or 7c2c66affSColin Finck * modify it under the terms of the GNU Lesser General Public 8c2c66affSColin Finck * License as published by the Free Software Foundation; either 9c2c66affSColin Finck * version 2.1 of the License, or (at your option) any later version. 10c2c66affSColin Finck * 11c2c66affSColin Finck * This library is distributed in the hope that it will be useful, 12c2c66affSColin Finck * but WITHOUT ANY WARRANTY; without even the implied warranty of 13c2c66affSColin Finck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14c2c66affSColin Finck * Lesser General Public License for more details. 15c2c66affSColin Finck * 16c2c66affSColin Finck * You should have received a copy of the GNU Lesser General Public 17c2c66affSColin Finck * License along with this library; if not, write to the Free Software 18c2c66affSColin Finck * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19c2c66affSColin Finck */ 20c2c66affSColin Finck 21c42b133eSAmine Khaldi #include <stdarg.h> 22c42b133eSAmine Khaldi #include <assert.h> 23c42b133eSAmine Khaldi 24c42b133eSAmine Khaldi #define COBJMACROS 25c42b133eSAmine Khaldi 26c42b133eSAmine Khaldi #include "windef.h" 27c42b133eSAmine Khaldi #include "winbase.h" 28c42b133eSAmine Khaldi #include "winerror.h" 29c42b133eSAmine Khaldi #include "msi.h" 30c42b133eSAmine Khaldi #include "msiquery.h" 31c42b133eSAmine Khaldi #include "objbase.h" 32c42b133eSAmine Khaldi #include "objidl.h" 33c42b133eSAmine Khaldi #include "winnls.h" 34c2c66affSColin Finck #include "msipriv.h" 35c42b133eSAmine Khaldi #include "query.h" 36c42b133eSAmine Khaldi 37c42b133eSAmine Khaldi #include "wine/debug.h" 38c42b133eSAmine Khaldi #include "wine/unicode.h" 39c2c66affSColin Finck 40c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL(msidb); 41c2c66affSColin Finck 42c2c66affSColin Finck #define MSITABLE_HASH_TABLE_SIZE 37 43c2c66affSColin Finck 44c2c66affSColin Finck typedef struct tagMSICOLUMNHASHENTRY 45c2c66affSColin Finck { 46c2c66affSColin Finck struct tagMSICOLUMNHASHENTRY *next; 47c2c66affSColin Finck UINT value; 48c2c66affSColin Finck UINT row; 49c2c66affSColin Finck } MSICOLUMNHASHENTRY; 50c2c66affSColin Finck 51c2c66affSColin Finck typedef struct tagMSICOLUMNINFO 52c2c66affSColin Finck { 53c2c66affSColin Finck LPCWSTR tablename; 54c2c66affSColin Finck UINT number; 55c2c66affSColin Finck LPCWSTR colname; 56c2c66affSColin Finck UINT type; 57c2c66affSColin Finck UINT offset; 58c2c66affSColin Finck INT ref_count; 59c2c66affSColin Finck BOOL temporary; 60c2c66affSColin Finck MSICOLUMNHASHENTRY **hash_table; 61c2c66affSColin Finck } MSICOLUMNINFO; 62c2c66affSColin Finck 63c2c66affSColin Finck struct tagMSITABLE 64c2c66affSColin Finck { 65c2c66affSColin Finck BYTE **data; 66c2c66affSColin Finck BOOL *data_persistent; 67c2c66affSColin Finck UINT row_count; 68c2c66affSColin Finck struct list entry; 69c2c66affSColin Finck MSICOLUMNINFO *colinfo; 70c2c66affSColin Finck UINT col_count; 71c2c66affSColin Finck MSICONDITION persistent; 72c2c66affSColin Finck INT ref_count; 73c2c66affSColin Finck WCHAR name[1]; 74c2c66affSColin Finck }; 75c2c66affSColin Finck 76c2c66affSColin Finck /* information for default tables */ 77c2c66affSColin Finck static const WCHAR szTables[] = {'_','T','a','b','l','e','s',0}; 78c2c66affSColin Finck static const WCHAR szTable[] = {'T','a','b','l','e',0}; 79c2c66affSColin Finck static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0}; 80c2c66affSColin Finck static const WCHAR szNumber[] = {'N','u','m','b','e','r',0}; 81c2c66affSColin Finck static const WCHAR szType[] = {'T','y','p','e',0}; 82c2c66affSColin Finck 83c2c66affSColin Finck static const MSICOLUMNINFO _Columns_cols[4] = { 84c2c66affSColin Finck { szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL }, 85c2c66affSColin Finck { szColumns, 2, szNumber, MSITYPE_VALID | MSITYPE_KEY | 2, 2, 0, 0, NULL }, 86c2c66affSColin Finck { szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4, 0, 0, NULL }, 87c2c66affSColin Finck { szColumns, 4, szType, MSITYPE_VALID | 2, 6, 0, 0, NULL }, 88c2c66affSColin Finck }; 89c2c66affSColin Finck 90c2c66affSColin Finck static const MSICOLUMNINFO _Tables_cols[1] = { 91c2c66affSColin Finck { szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL }, 92c2c66affSColin Finck }; 93c2c66affSColin Finck 94c2c66affSColin Finck #define MAX_STREAM_NAME 0x1f 95c2c66affSColin Finck 96c2c66affSColin Finck static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref ) 97c2c66affSColin Finck { 98c2c66affSColin Finck if( MSITYPE_IS_BINARY(col->type) ) 99c2c66affSColin Finck return 2; 100c2c66affSColin Finck 101c2c66affSColin Finck if( col->type & MSITYPE_STRING ) 102c2c66affSColin Finck return bytes_per_strref; 103c2c66affSColin Finck 104c2c66affSColin Finck if( (col->type & 0xff) <= 2) 105c2c66affSColin Finck return 2; 106c2c66affSColin Finck 107c2c66affSColin Finck if( (col->type & 0xff) != 4 ) 108c2c66affSColin Finck ERR("Invalid column size %u\n", col->type & 0xff); 109c2c66affSColin Finck 110c2c66affSColin Finck return 4; 111c2c66affSColin Finck } 112c2c66affSColin Finck 113c2c66affSColin Finck static int utf2mime(int x) 114c2c66affSColin Finck { 115c2c66affSColin Finck if( (x>='0') && (x<='9') ) 116c2c66affSColin Finck return x-'0'; 117c2c66affSColin Finck if( (x>='A') && (x<='Z') ) 118c2c66affSColin Finck return x-'A'+10; 119c2c66affSColin Finck if( (x>='a') && (x<='z') ) 120c2c66affSColin Finck return x-'a'+10+26; 121c2c66affSColin Finck if( x=='.' ) 122c2c66affSColin Finck return 10+26+26; 123c2c66affSColin Finck if( x=='_' ) 124c2c66affSColin Finck return 10+26+26+1; 125c2c66affSColin Finck return -1; 126c2c66affSColin Finck } 127c2c66affSColin Finck 128c2c66affSColin Finck LPWSTR encode_streamname(BOOL bTable, LPCWSTR in) 129c2c66affSColin Finck { 130c2c66affSColin Finck DWORD count = MAX_STREAM_NAME; 131c2c66affSColin Finck DWORD ch, next; 132c2c66affSColin Finck LPWSTR out, p; 133c2c66affSColin Finck 134c2c66affSColin Finck if( !bTable ) 135c2c66affSColin Finck count = lstrlenW( in )+2; 136c2c66affSColin Finck if (!(out = msi_alloc( count*sizeof(WCHAR) ))) return NULL; 137c2c66affSColin Finck p = out; 138c2c66affSColin Finck 139c2c66affSColin Finck if( bTable ) 140c2c66affSColin Finck { 141c2c66affSColin Finck *p++ = 0x4840; 142c2c66affSColin Finck count --; 143c2c66affSColin Finck } 144c2c66affSColin Finck while( count -- ) 145c2c66affSColin Finck { 146c2c66affSColin Finck ch = *in++; 147c2c66affSColin Finck if( !ch ) 148c2c66affSColin Finck { 149c2c66affSColin Finck *p = ch; 150c2c66affSColin Finck return out; 151c2c66affSColin Finck } 152c2c66affSColin Finck if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) ) 153c2c66affSColin Finck { 154c2c66affSColin Finck ch = utf2mime(ch) + 0x4800; 155c2c66affSColin Finck next = *in; 156c2c66affSColin Finck if( next && (next<0x80) ) 157c2c66affSColin Finck { 158c2c66affSColin Finck next = utf2mime(next); 159c2c66affSColin Finck if( next != -1 ) 160c2c66affSColin Finck { 161c2c66affSColin Finck next += 0x3ffffc0; 162c2c66affSColin Finck ch += (next<<6); 163c2c66affSColin Finck in++; 164c2c66affSColin Finck } 165c2c66affSColin Finck } 166c2c66affSColin Finck } 167c2c66affSColin Finck *p++ = ch; 168c2c66affSColin Finck } 169c2c66affSColin Finck ERR("Failed to encode stream name (%s)\n",debugstr_w(in)); 170c2c66affSColin Finck msi_free( out ); 171c2c66affSColin Finck return NULL; 172c2c66affSColin Finck } 173c2c66affSColin Finck 174c2c66affSColin Finck static int mime2utf(int x) 175c2c66affSColin Finck { 176c2c66affSColin Finck if( x<10 ) 177c2c66affSColin Finck return x + '0'; 178c2c66affSColin Finck if( x<(10+26)) 179c2c66affSColin Finck return x - 10 + 'A'; 180c2c66affSColin Finck if( x<(10+26+26)) 181c2c66affSColin Finck return x - 10 - 26 + 'a'; 182c2c66affSColin Finck if( x == (10+26+26) ) 183c2c66affSColin Finck return '.'; 184c2c66affSColin Finck return '_'; 185c2c66affSColin Finck } 186c2c66affSColin Finck 187c2c66affSColin Finck BOOL decode_streamname(LPCWSTR in, LPWSTR out) 188c2c66affSColin Finck { 189c2c66affSColin Finck WCHAR ch; 190c2c66affSColin Finck DWORD count = 0; 191c2c66affSColin Finck 192c2c66affSColin Finck while ( (ch = *in++) ) 193c2c66affSColin Finck { 194c2c66affSColin Finck if( (ch >= 0x3800 ) && (ch < 0x4840 ) ) 195c2c66affSColin Finck { 196c2c66affSColin Finck if( ch >= 0x4800 ) 197c2c66affSColin Finck ch = mime2utf(ch-0x4800); 198c2c66affSColin Finck else 199c2c66affSColin Finck { 200c2c66affSColin Finck ch -= 0x3800; 201c2c66affSColin Finck *out++ = mime2utf(ch&0x3f); 202c2c66affSColin Finck count++; 203c2c66affSColin Finck ch = mime2utf((ch>>6)&0x3f); 204c2c66affSColin Finck } 205c2c66affSColin Finck } 206c2c66affSColin Finck *out++ = ch; 207c2c66affSColin Finck count++; 208c2c66affSColin Finck } 209c2c66affSColin Finck *out = 0; 210c2c66affSColin Finck return count; 211c2c66affSColin Finck } 212c2c66affSColin Finck 213c2c66affSColin Finck void enum_stream_names( IStorage *stg ) 214c2c66affSColin Finck { 215c2c66affSColin Finck IEnumSTATSTG *stgenum = NULL; 216c2c66affSColin Finck HRESULT r; 217c2c66affSColin Finck STATSTG stat; 218c2c66affSColin Finck ULONG n, count; 219c2c66affSColin Finck WCHAR name[0x40]; 220c2c66affSColin Finck 221c2c66affSColin Finck r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); 222c2c66affSColin Finck if( FAILED( r ) ) 223c2c66affSColin Finck return; 224c2c66affSColin Finck 225c2c66affSColin Finck n = 0; 226c2c66affSColin Finck while( 1 ) 227c2c66affSColin Finck { 228c2c66affSColin Finck count = 0; 229c2c66affSColin Finck r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); 230c2c66affSColin Finck if( FAILED( r ) || !count ) 231c2c66affSColin Finck break; 232c2c66affSColin Finck decode_streamname( stat.pwcsName, name ); 233c2c66affSColin Finck TRACE("stream %2d -> %s %s\n", n, 234c2c66affSColin Finck debugstr_w(stat.pwcsName), debugstr_w(name) ); 235c2c66affSColin Finck CoTaskMemFree( stat.pwcsName ); 236c2c66affSColin Finck n++; 237c2c66affSColin Finck } 238c2c66affSColin Finck 239c2c66affSColin Finck IEnumSTATSTG_Release( stgenum ); 240c2c66affSColin Finck } 241c2c66affSColin Finck 242c2c66affSColin Finck UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table, 243c2c66affSColin Finck BYTE **pdata, UINT *psz ) 244c2c66affSColin Finck { 245c2c66affSColin Finck HRESULT r; 246c2c66affSColin Finck UINT ret = ERROR_FUNCTION_FAILED; 247c2c66affSColin Finck VOID *data; 248c2c66affSColin Finck ULONG sz, count; 249c2c66affSColin Finck IStream *stm = NULL; 250c2c66affSColin Finck STATSTG stat; 251c2c66affSColin Finck LPWSTR encname; 252c2c66affSColin Finck 253c2c66affSColin Finck encname = encode_streamname(table, stname); 254c2c66affSColin Finck 255c2c66affSColin Finck TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname)); 256c2c66affSColin Finck 257c2c66affSColin Finck r = IStorage_OpenStream(stg, encname, NULL, 258c2c66affSColin Finck STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); 259c2c66affSColin Finck msi_free( encname ); 260c2c66affSColin Finck if( FAILED( r ) ) 261c2c66affSColin Finck { 262c2c66affSColin Finck WARN("open stream failed r = %08x - empty table?\n", r); 263c2c66affSColin Finck return ret; 264c2c66affSColin Finck } 265c2c66affSColin Finck 266c2c66affSColin Finck r = IStream_Stat(stm, &stat, STATFLAG_NONAME ); 267c2c66affSColin Finck if( FAILED( r ) ) 268c2c66affSColin Finck { 269c2c66affSColin Finck WARN("open stream failed r = %08x!\n", r); 270c2c66affSColin Finck goto end; 271c2c66affSColin Finck } 272c2c66affSColin Finck 273c2c66affSColin Finck if( stat.cbSize.QuadPart >> 32 ) 274c2c66affSColin Finck { 275c2c66affSColin Finck WARN("Too big!\n"); 276c2c66affSColin Finck goto end; 277c2c66affSColin Finck } 278c2c66affSColin Finck 279c2c66affSColin Finck sz = stat.cbSize.QuadPart; 280c2c66affSColin Finck data = msi_alloc( sz ); 281c2c66affSColin Finck if( !data ) 282c2c66affSColin Finck { 283c2c66affSColin Finck WARN("couldn't allocate memory r=%08x!\n", r); 284c2c66affSColin Finck ret = ERROR_NOT_ENOUGH_MEMORY; 285c2c66affSColin Finck goto end; 286c2c66affSColin Finck } 287c2c66affSColin Finck 288c2c66affSColin Finck r = IStream_Read(stm, data, sz, &count ); 289c2c66affSColin Finck if( FAILED( r ) || ( count != sz ) ) 290c2c66affSColin Finck { 291c2c66affSColin Finck msi_free( data ); 292c2c66affSColin Finck WARN("read stream failed r = %08x!\n", r); 293c2c66affSColin Finck goto end; 294c2c66affSColin Finck } 295c2c66affSColin Finck 296c2c66affSColin Finck *pdata = data; 297c2c66affSColin Finck *psz = sz; 298c2c66affSColin Finck ret = ERROR_SUCCESS; 299c2c66affSColin Finck 300c2c66affSColin Finck end: 301c2c66affSColin Finck IStream_Release( stm ); 302c2c66affSColin Finck 303c2c66affSColin Finck return ret; 304c2c66affSColin Finck } 305c2c66affSColin Finck 306c2c66affSColin Finck UINT write_stream_data( IStorage *stg, LPCWSTR stname, 307c2c66affSColin Finck LPCVOID data, UINT sz, BOOL bTable ) 308c2c66affSColin Finck { 309c2c66affSColin Finck HRESULT r; 310c2c66affSColin Finck UINT ret = ERROR_FUNCTION_FAILED; 311c2c66affSColin Finck ULONG count; 312c2c66affSColin Finck IStream *stm = NULL; 313c2c66affSColin Finck ULARGE_INTEGER size; 314c2c66affSColin Finck LARGE_INTEGER pos; 315c2c66affSColin Finck LPWSTR encname; 316c2c66affSColin Finck 317c2c66affSColin Finck encname = encode_streamname(bTable, stname ); 318c2c66affSColin Finck r = IStorage_OpenStream( stg, encname, NULL, 319c2c66affSColin Finck STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); 320c2c66affSColin Finck if( FAILED(r) ) 321c2c66affSColin Finck { 322c2c66affSColin Finck r = IStorage_CreateStream( stg, encname, 323c2c66affSColin Finck STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); 324c2c66affSColin Finck } 325c2c66affSColin Finck msi_free( encname ); 326c2c66affSColin Finck if( FAILED( r ) ) 327c2c66affSColin Finck { 328c2c66affSColin Finck WARN("open stream failed r = %08x\n", r); 329c2c66affSColin Finck return ret; 330c2c66affSColin Finck } 331c2c66affSColin Finck 332c2c66affSColin Finck size.QuadPart = sz; 333c2c66affSColin Finck r = IStream_SetSize( stm, size ); 334c2c66affSColin Finck if( FAILED( r ) ) 335c2c66affSColin Finck { 336c2c66affSColin Finck WARN("Failed to SetSize\n"); 337c2c66affSColin Finck goto end; 338c2c66affSColin Finck } 339c2c66affSColin Finck 340c2c66affSColin Finck pos.QuadPart = 0; 341c2c66affSColin Finck r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL ); 342c2c66affSColin Finck if( FAILED( r ) ) 343c2c66affSColin Finck { 344c2c66affSColin Finck WARN("Failed to Seek\n"); 345c2c66affSColin Finck goto end; 346c2c66affSColin Finck } 347c2c66affSColin Finck 348c2c66affSColin Finck if (sz) 349c2c66affSColin Finck { 350c2c66affSColin Finck r = IStream_Write(stm, data, sz, &count ); 351c2c66affSColin Finck if( FAILED( r ) || ( count != sz ) ) 352c2c66affSColin Finck { 353c2c66affSColin Finck WARN("Failed to Write\n"); 354c2c66affSColin Finck goto end; 355c2c66affSColin Finck } 356c2c66affSColin Finck } 357c2c66affSColin Finck 358c2c66affSColin Finck ret = ERROR_SUCCESS; 359c2c66affSColin Finck 360c2c66affSColin Finck end: 361c2c66affSColin Finck IStream_Release( stm ); 362c2c66affSColin Finck 363c2c66affSColin Finck return ret; 364c2c66affSColin Finck } 365c2c66affSColin Finck 366c2c66affSColin Finck static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count ) 367c2c66affSColin Finck { 368c2c66affSColin Finck UINT i; 369c2c66affSColin Finck for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table ); 370c2c66affSColin Finck } 371c2c66affSColin Finck 372c2c66affSColin Finck static void free_table( MSITABLE *table ) 373c2c66affSColin Finck { 374c2c66affSColin Finck UINT i; 375c2c66affSColin Finck for( i=0; i<table->row_count; i++ ) 376c2c66affSColin Finck msi_free( table->data[i] ); 377c2c66affSColin Finck msi_free( table->data ); 378c2c66affSColin Finck msi_free( table->data_persistent ); 379c2c66affSColin Finck msi_free_colinfo( table->colinfo, table->col_count ); 380c2c66affSColin Finck msi_free( table->colinfo ); 381c2c66affSColin Finck msi_free( table ); 382c2c66affSColin Finck } 383c2c66affSColin Finck 384c2c66affSColin Finck static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref ) 385c2c66affSColin Finck { 386c2c66affSColin Finck const MSICOLUMNINFO *last_col; 387c2c66affSColin Finck 388c2c66affSColin Finck if (!count) 389c2c66affSColin Finck return 0; 390c2c66affSColin Finck 391c2c66affSColin Finck if (bytes_per_strref != LONG_STR_BYTES) 392c2c66affSColin Finck { 393c2c66affSColin Finck UINT i, size = 0; 394c2c66affSColin Finck for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref ); 395c2c66affSColin Finck return size; 396c2c66affSColin Finck } 397c2c66affSColin Finck last_col = &cols[count - 1]; 398c2c66affSColin Finck return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref ); 399c2c66affSColin Finck } 400c2c66affSColin Finck 401c2c66affSColin Finck /* add this table to the list of cached tables in the database */ 402c2c66affSColin Finck static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg ) 403c2c66affSColin Finck { 404c2c66affSColin Finck BYTE *rawdata = NULL; 405c2c66affSColin Finck UINT rawsize = 0, i, j, row_size, row_size_mem; 406c2c66affSColin Finck 407c2c66affSColin Finck TRACE("%s\n",debugstr_w(t->name)); 408c2c66affSColin Finck 409c2c66affSColin Finck row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref ); 410c2c66affSColin Finck row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES ); 411c2c66affSColin Finck 412c2c66affSColin Finck /* if we can't read the table, just assume that it's empty */ 413c2c66affSColin Finck read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize ); 414c2c66affSColin Finck if( !rawdata ) 415c2c66affSColin Finck return ERROR_SUCCESS; 416c2c66affSColin Finck 417c2c66affSColin Finck TRACE("Read %d bytes\n", rawsize ); 418c2c66affSColin Finck 419c2c66affSColin Finck if( rawsize % row_size ) 420c2c66affSColin Finck { 421c2c66affSColin Finck WARN("Table size is invalid %d/%d\n", rawsize, row_size ); 422c2c66affSColin Finck goto err; 423c2c66affSColin Finck } 424c2c66affSColin Finck 425c2c66affSColin Finck if ((t->row_count = rawsize / row_size)) 426c2c66affSColin Finck { 427c2c66affSColin Finck if (!(t->data = msi_alloc_zero( t->row_count * sizeof(USHORT *) ))) goto err; 428c2c66affSColin Finck if (!(t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL) ))) goto err; 429c2c66affSColin Finck } 430c2c66affSColin Finck 431c2c66affSColin Finck /* transpose all the data */ 432c2c66affSColin Finck TRACE("Transposing data from %d rows\n", t->row_count ); 433c2c66affSColin Finck for (i = 0; i < t->row_count; i++) 434c2c66affSColin Finck { 435c2c66affSColin Finck UINT ofs = 0, ofs_mem = 0; 436c2c66affSColin Finck 437c2c66affSColin Finck t->data[i] = msi_alloc( row_size_mem ); 438c2c66affSColin Finck if( !t->data[i] ) 439c2c66affSColin Finck goto err; 440c2c66affSColin Finck t->data_persistent[i] = TRUE; 441c2c66affSColin Finck 442c2c66affSColin Finck for (j = 0; j < t->col_count; j++) 443c2c66affSColin Finck { 444c2c66affSColin Finck UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); 445c2c66affSColin Finck UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref ); 446c2c66affSColin Finck UINT k; 447c2c66affSColin Finck 448c2c66affSColin Finck if ( n != 2 && n != 3 && n != 4 ) 449c2c66affSColin Finck { 450c2c66affSColin Finck ERR("oops - unknown column width %d\n", n); 451c2c66affSColin Finck goto err; 452c2c66affSColin Finck } 453c2c66affSColin Finck if (t->colinfo[j].type & MSITYPE_STRING && n < m) 454c2c66affSColin Finck { 455c2c66affSColin Finck for (k = 0; k < m; k++) 456c2c66affSColin Finck { 457c2c66affSColin Finck if (k < n) 458c2c66affSColin Finck t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; 459c2c66affSColin Finck else 460c2c66affSColin Finck t->data[i][ofs_mem + k] = 0; 461c2c66affSColin Finck } 462c2c66affSColin Finck } 463c2c66affSColin Finck else 464c2c66affSColin Finck { 465c2c66affSColin Finck for (k = 0; k < n; k++) 466c2c66affSColin Finck t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; 467c2c66affSColin Finck } 468c2c66affSColin Finck ofs_mem += m; 469c2c66affSColin Finck ofs += n; 470c2c66affSColin Finck } 471c2c66affSColin Finck } 472c2c66affSColin Finck 473c2c66affSColin Finck msi_free( rawdata ); 474c2c66affSColin Finck return ERROR_SUCCESS; 475c2c66affSColin Finck err: 476c2c66affSColin Finck msi_free( rawdata ); 477c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 478c2c66affSColin Finck } 479c2c66affSColin Finck 480c2c66affSColin Finck void free_cached_tables( MSIDATABASE *db ) 481c2c66affSColin Finck { 482c2c66affSColin Finck while( !list_empty( &db->tables ) ) 483c2c66affSColin Finck { 484c2c66affSColin Finck MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry ); 485c2c66affSColin Finck 486c2c66affSColin Finck list_remove( &t->entry ); 487c2c66affSColin Finck free_table( t ); 488c2c66affSColin Finck } 489c2c66affSColin Finck } 490c2c66affSColin Finck 491c2c66affSColin Finck static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name ) 492c2c66affSColin Finck { 493c2c66affSColin Finck MSITABLE *t; 494c2c66affSColin Finck 495c2c66affSColin Finck LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry ) 496c2c66affSColin Finck if( !strcmpW( name, t->name ) ) 497c2c66affSColin Finck return t; 498c2c66affSColin Finck 499c2c66affSColin Finck return NULL; 500c2c66affSColin Finck } 501c2c66affSColin Finck 502c2c66affSColin Finck static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count ) 503c2c66affSColin Finck { 504c2c66affSColin Finck DWORD i; 505c2c66affSColin Finck 506c2c66affSColin Finck for (i = 0; colinfo && i < count; i++) 507c2c66affSColin Finck { 508c2c66affSColin Finck assert( i + 1 == colinfo[i].number ); 509c2c66affSColin Finck if (i) colinfo[i].offset = colinfo[i - 1].offset + 510c2c66affSColin Finck bytes_per_column( db, &colinfo[i - 1], LONG_STR_BYTES ); 511c2c66affSColin Finck else colinfo[i].offset = 0; 512c2c66affSColin Finck 513c2c66affSColin Finck TRACE("column %d is [%s] with type %08x ofs %d\n", 514c2c66affSColin Finck colinfo[i].number, debugstr_w(colinfo[i].colname), 515c2c66affSColin Finck colinfo[i].type, colinfo[i].offset); 516c2c66affSColin Finck } 517c2c66affSColin Finck } 518c2c66affSColin Finck 519c2c66affSColin Finck static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz ) 520c2c66affSColin Finck { 521c2c66affSColin Finck const MSICOLUMNINFO *p; 522c2c66affSColin Finck DWORD i, n; 523c2c66affSColin Finck 524c2c66affSColin Finck TRACE("%s\n", debugstr_w(name)); 525c2c66affSColin Finck 526c2c66affSColin Finck if (!strcmpW( name, szTables )) 527c2c66affSColin Finck { 528c2c66affSColin Finck p = _Tables_cols; 529c2c66affSColin Finck n = 1; 530c2c66affSColin Finck } 531c2c66affSColin Finck else if (!strcmpW( name, szColumns )) 532c2c66affSColin Finck { 533c2c66affSColin Finck p = _Columns_cols; 534c2c66affSColin Finck n = 4; 535c2c66affSColin Finck } 536c2c66affSColin Finck else return ERROR_FUNCTION_FAILED; 537c2c66affSColin Finck 538c2c66affSColin Finck for (i = 0; i < n; i++) 539c2c66affSColin Finck { 540c2c66affSColin Finck if (colinfo && i < *sz) colinfo[i] = p[i]; 541c2c66affSColin Finck if (colinfo && i >= *sz) break; 542c2c66affSColin Finck } 543c2c66affSColin Finck table_calc_column_offsets( db, colinfo, n ); 544c2c66affSColin Finck *sz = n; 545c2c66affSColin Finck return ERROR_SUCCESS; 546c2c66affSColin Finck } 547c2c66affSColin Finck 548c2c66affSColin Finck static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz ); 549c2c66affSColin Finck 550c2c66affSColin Finck static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount ) 551c2c66affSColin Finck { 552c2c66affSColin Finck UINT r, column_count = 0; 553c2c66affSColin Finck MSICOLUMNINFO *columns; 554c2c66affSColin Finck 555c2c66affSColin Finck /* get the number of columns in this table */ 556c2c66affSColin Finck column_count = 0; 557c2c66affSColin Finck r = get_tablecolumns( db, name, NULL, &column_count ); 558c2c66affSColin Finck if (r != ERROR_SUCCESS) 559c2c66affSColin Finck return r; 560c2c66affSColin Finck 561c2c66affSColin Finck *pcount = column_count; 562c2c66affSColin Finck 563c2c66affSColin Finck /* if there are no columns, there's no table */ 564c2c66affSColin Finck if (!column_count) 565c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 566c2c66affSColin Finck 567c2c66affSColin Finck TRACE("table %s found\n", debugstr_w(name)); 568c2c66affSColin Finck 569c2c66affSColin Finck columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) ); 570c2c66affSColin Finck if (!columns) 571c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 572c2c66affSColin Finck 573c2c66affSColin Finck r = get_tablecolumns( db, name, columns, &column_count ); 574c2c66affSColin Finck if (r != ERROR_SUCCESS) 575c2c66affSColin Finck { 576c2c66affSColin Finck msi_free( columns ); 577c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 578c2c66affSColin Finck } 579c2c66affSColin Finck *pcols = columns; 580c2c66affSColin Finck return r; 581c2c66affSColin Finck } 582c2c66affSColin Finck 583c2c66affSColin Finck static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret ) 584c2c66affSColin Finck { 585c2c66affSColin Finck MSITABLE *table; 586c2c66affSColin Finck UINT r; 587c2c66affSColin Finck 588c2c66affSColin Finck /* first, see if the table is cached */ 589c2c66affSColin Finck table = find_cached_table( db, name ); 590c2c66affSColin Finck if (table) 591c2c66affSColin Finck { 592c2c66affSColin Finck *table_ret = table; 593c2c66affSColin Finck return ERROR_SUCCESS; 594c2c66affSColin Finck } 595c2c66affSColin Finck 596c2c66affSColin Finck /* nonexistent tables should be interpreted as empty tables */ 597c2c66affSColin Finck table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) ); 598c2c66affSColin Finck if (!table) 599c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 600c2c66affSColin Finck 601c2c66affSColin Finck table->row_count = 0; 602c2c66affSColin Finck table->data = NULL; 603c2c66affSColin Finck table->data_persistent = NULL; 604c2c66affSColin Finck table->colinfo = NULL; 605c2c66affSColin Finck table->col_count = 0; 606c2c66affSColin Finck table->persistent = MSICONDITION_TRUE; 607c2c66affSColin Finck lstrcpyW( table->name, name ); 608c2c66affSColin Finck 609c2c66affSColin Finck if (!strcmpW( name, szTables ) || !strcmpW( name, szColumns )) 610c2c66affSColin Finck table->persistent = MSICONDITION_NONE; 611c2c66affSColin Finck 612c2c66affSColin Finck r = table_get_column_info( db, name, &table->colinfo, &table->col_count ); 613c2c66affSColin Finck if (r != ERROR_SUCCESS) 614c2c66affSColin Finck { 615c2c66affSColin Finck free_table( table ); 616c2c66affSColin Finck return r; 617c2c66affSColin Finck } 618c2c66affSColin Finck r = read_table_from_storage( db, table, db->storage ); 619c2c66affSColin Finck if (r != ERROR_SUCCESS) 620c2c66affSColin Finck { 621c2c66affSColin Finck free_table( table ); 622c2c66affSColin Finck return r; 623c2c66affSColin Finck } 624c2c66affSColin Finck list_add_head( &db->tables, &table->entry ); 625c2c66affSColin Finck *table_ret = table; 626c2c66affSColin Finck return ERROR_SUCCESS; 627c2c66affSColin Finck } 628c2c66affSColin Finck 629c2c66affSColin Finck static UINT read_table_int( BYTE *const *data, UINT row, UINT col, UINT bytes ) 630c2c66affSColin Finck { 631c2c66affSColin Finck UINT ret = 0, i; 632c2c66affSColin Finck 633c2c66affSColin Finck for (i = 0; i < bytes; i++) 634c2c66affSColin Finck ret += data[row][col + i] << i * 8; 635c2c66affSColin Finck 636c2c66affSColin Finck return ret; 637c2c66affSColin Finck } 638c2c66affSColin Finck 639c2c66affSColin Finck static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz ) 640c2c66affSColin Finck { 641c2c66affSColin Finck UINT r, i, n = 0, table_id, count, maxcount = *sz; 642c2c66affSColin Finck MSITABLE *table = NULL; 643c2c66affSColin Finck 644c2c66affSColin Finck TRACE("%s\n", debugstr_w(szTableName)); 645c2c66affSColin Finck 646c2c66affSColin Finck /* first check if there is a default table with that name */ 647c2c66affSColin Finck r = get_defaulttablecolumns( db, szTableName, colinfo, sz ); 648c2c66affSColin Finck if (r == ERROR_SUCCESS && *sz) 649c2c66affSColin Finck return r; 650c2c66affSColin Finck 651c2c66affSColin Finck r = get_table( db, szColumns, &table ); 652c2c66affSColin Finck if (r != ERROR_SUCCESS) 653c2c66affSColin Finck { 654c2c66affSColin Finck ERR("couldn't load _Columns table\n"); 655c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 656c2c66affSColin Finck } 657c2c66affSColin Finck 658c2c66affSColin Finck /* convert table and column names to IDs from the string table */ 659c2c66affSColin Finck r = msi_string2id( db->strings, szTableName, -1, &table_id ); 660c2c66affSColin Finck if (r != ERROR_SUCCESS) 661c2c66affSColin Finck { 662c2c66affSColin Finck WARN("Couldn't find id for %s\n", debugstr_w(szTableName)); 663c2c66affSColin Finck return r; 664c2c66affSColin Finck } 665c2c66affSColin Finck TRACE("Table id is %d, row count is %d\n", table_id, table->row_count); 666c2c66affSColin Finck 667c2c66affSColin Finck /* Note: _Columns table doesn't have non-persistent data */ 668c2c66affSColin Finck 669c2c66affSColin Finck /* if maxcount is non-zero, assume it's exactly right for this table */ 670c2c66affSColin Finck if (colinfo) memset( colinfo, 0, maxcount * sizeof(*colinfo) ); 671c2c66affSColin Finck count = table->row_count; 672c2c66affSColin Finck for (i = 0; i < count; i++) 673c2c66affSColin Finck { 674c2c66affSColin Finck if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue; 675c2c66affSColin Finck if (colinfo) 676c2c66affSColin Finck { 677c2c66affSColin Finck UINT id = read_table_int( table->data, i, table->colinfo[2].offset, LONG_STR_BYTES ); 678c2c66affSColin Finck UINT col = read_table_int( table->data, i, table->colinfo[1].offset, sizeof(USHORT) ) - (1 << 15); 679c2c66affSColin Finck 680c2c66affSColin Finck /* check the column number is in range */ 681c2c66affSColin Finck if (col < 1 || col > maxcount) 682c2c66affSColin Finck { 683c2c66affSColin Finck ERR("column %d out of range (maxcount: %d)\n", col, maxcount); 684c2c66affSColin Finck continue; 685c2c66affSColin Finck } 686c2c66affSColin Finck /* check if this column was already set */ 687c2c66affSColin Finck if (colinfo[col - 1].number) 688c2c66affSColin Finck { 689c2c66affSColin Finck ERR("duplicate column %d\n", col); 690c2c66affSColin Finck continue; 691c2c66affSColin Finck } 692c2c66affSColin Finck colinfo[col - 1].tablename = msi_string_lookup( db->strings, table_id, NULL ); 693c2c66affSColin Finck colinfo[col - 1].number = col; 694c2c66affSColin Finck colinfo[col - 1].colname = msi_string_lookup( db->strings, id, NULL ); 695c2c66affSColin Finck colinfo[col - 1].type = read_table_int( table->data, i, table->colinfo[3].offset, 696c2c66affSColin Finck sizeof(USHORT) ) - (1 << 15); 697c2c66affSColin Finck colinfo[col - 1].offset = 0; 698c2c66affSColin Finck colinfo[col - 1].ref_count = 0; 699c2c66affSColin Finck colinfo[col - 1].hash_table = NULL; 700c2c66affSColin Finck } 701c2c66affSColin Finck n++; 702c2c66affSColin Finck } 703c2c66affSColin Finck TRACE("%s has %d columns\n", debugstr_w(szTableName), n); 704c2c66affSColin Finck 705c2c66affSColin Finck if (colinfo && n != maxcount) 706c2c66affSColin Finck { 707c2c66affSColin Finck ERR("missing column in table %s\n", debugstr_w(szTableName)); 708c2c66affSColin Finck msi_free_colinfo( colinfo, maxcount ); 709c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 710c2c66affSColin Finck } 711c2c66affSColin Finck table_calc_column_offsets( db, colinfo, n ); 712c2c66affSColin Finck *sz = n; 713c2c66affSColin Finck return ERROR_SUCCESS; 714c2c66affSColin Finck } 715c2c66affSColin Finck 716c2c66affSColin Finck UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info, 717c2c66affSColin Finck MSICONDITION persistent ) 718c2c66affSColin Finck { 719c2c66affSColin Finck UINT r, nField; 720c2c66affSColin Finck MSIVIEW *tv = NULL; 721c2c66affSColin Finck MSIRECORD *rec = NULL; 722c2c66affSColin Finck column_info *col; 723c2c66affSColin Finck MSITABLE *table; 724c2c66affSColin Finck UINT i; 725c2c66affSColin Finck 726c2c66affSColin Finck /* only add tables that don't exist already */ 727c2c66affSColin Finck if( TABLE_Exists(db, name ) ) 728c2c66affSColin Finck { 729c2c66affSColin Finck WARN("table %s exists\n", debugstr_w(name)); 730c2c66affSColin Finck return ERROR_BAD_QUERY_SYNTAX; 731c2c66affSColin Finck } 732c2c66affSColin Finck 733c2c66affSColin Finck table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) ); 734c2c66affSColin Finck if( !table ) 735c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 736c2c66affSColin Finck 737c2c66affSColin Finck table->ref_count = 1; 738c2c66affSColin Finck table->row_count = 0; 739c2c66affSColin Finck table->data = NULL; 740c2c66affSColin Finck table->data_persistent = NULL; 741c2c66affSColin Finck table->colinfo = NULL; 742c2c66affSColin Finck table->col_count = 0; 743c2c66affSColin Finck table->persistent = persistent; 744c2c66affSColin Finck lstrcpyW( table->name, name ); 745c2c66affSColin Finck 746c2c66affSColin Finck for( col = col_info; col; col = col->next ) 747c2c66affSColin Finck table->col_count++; 748c2c66affSColin Finck 749c2c66affSColin Finck table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) ); 750c2c66affSColin Finck if (!table->colinfo) 751c2c66affSColin Finck { 752c2c66affSColin Finck free_table( table ); 753c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 754c2c66affSColin Finck } 755c2c66affSColin Finck 756c2c66affSColin Finck for( i = 0, col = col_info; col; i++, col = col->next ) 757c2c66affSColin Finck { 758*211c4104Swinesync UINT table_id = msi_add_string( db->strings, col->table, -1, persistent ); 759*211c4104Swinesync UINT col_id = msi_add_string( db->strings, col->column, -1, persistent ); 760c2c66affSColin Finck 761c2c66affSColin Finck table->colinfo[ i ].tablename = msi_string_lookup( db->strings, table_id, NULL ); 762c2c66affSColin Finck table->colinfo[ i ].number = i + 1; 763c2c66affSColin Finck table->colinfo[ i ].colname = msi_string_lookup( db->strings, col_id, NULL ); 764c2c66affSColin Finck table->colinfo[ i ].type = col->type; 765c2c66affSColin Finck table->colinfo[ i ].offset = 0; 766c2c66affSColin Finck table->colinfo[ i ].ref_count = 0; 767c2c66affSColin Finck table->colinfo[ i ].hash_table = NULL; 768c2c66affSColin Finck table->colinfo[ i ].temporary = col->temporary; 769c2c66affSColin Finck } 770c2c66affSColin Finck table_calc_column_offsets( db, table->colinfo, table->col_count); 771c2c66affSColin Finck 772c2c66affSColin Finck r = TABLE_CreateView( db, szTables, &tv ); 773c2c66affSColin Finck TRACE("CreateView returned %x\n", r); 774c2c66affSColin Finck if( r ) 775c2c66affSColin Finck { 776c2c66affSColin Finck free_table( table ); 777c2c66affSColin Finck return r; 778c2c66affSColin Finck } 779c2c66affSColin Finck 780c2c66affSColin Finck r = tv->ops->execute( tv, 0 ); 781c2c66affSColin Finck TRACE("tv execute returned %x\n", r); 782c2c66affSColin Finck if( r ) 783c2c66affSColin Finck goto err; 784c2c66affSColin Finck 785c2c66affSColin Finck rec = MSI_CreateRecord( 1 ); 786c2c66affSColin Finck if( !rec ) 787c2c66affSColin Finck goto err; 788c2c66affSColin Finck 789c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 1, name ); 790c2c66affSColin Finck if( r ) 791c2c66affSColin Finck goto err; 792c2c66affSColin Finck 793c2c66affSColin Finck r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE ); 794c2c66affSColin Finck TRACE("insert_row returned %x\n", r); 795c2c66affSColin Finck if( r ) 796c2c66affSColin Finck goto err; 797c2c66affSColin Finck 798c2c66affSColin Finck tv->ops->delete( tv ); 799c2c66affSColin Finck tv = NULL; 800c2c66affSColin Finck 801c2c66affSColin Finck msiobj_release( &rec->hdr ); 802c2c66affSColin Finck rec = NULL; 803c2c66affSColin Finck 804c2c66affSColin Finck if( persistent != MSICONDITION_FALSE ) 805c2c66affSColin Finck { 806c2c66affSColin Finck /* add each column to the _Columns table */ 807c2c66affSColin Finck r = TABLE_CreateView( db, szColumns, &tv ); 808c2c66affSColin Finck if( r ) 809c2c66affSColin Finck goto err; 810c2c66affSColin Finck 811c2c66affSColin Finck r = tv->ops->execute( tv, 0 ); 812c2c66affSColin Finck TRACE("tv execute returned %x\n", r); 813c2c66affSColin Finck if( r ) 814c2c66affSColin Finck goto err; 815c2c66affSColin Finck 816c2c66affSColin Finck rec = MSI_CreateRecord( 4 ); 817c2c66affSColin Finck if( !rec ) 818c2c66affSColin Finck goto err; 819c2c66affSColin Finck 820c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 1, name ); 821c2c66affSColin Finck if( r ) 822c2c66affSColin Finck goto err; 823c2c66affSColin Finck 824c2c66affSColin Finck /* 825c2c66affSColin Finck * need to set the table, column number, col name and type 826c2c66affSColin Finck * for each column we enter in the table 827c2c66affSColin Finck */ 828c2c66affSColin Finck nField = 1; 829c2c66affSColin Finck for( col = col_info; col; col = col->next ) 830c2c66affSColin Finck { 831c2c66affSColin Finck r = MSI_RecordSetInteger( rec, 2, nField ); 832c2c66affSColin Finck if( r ) 833c2c66affSColin Finck goto err; 834c2c66affSColin Finck 835c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 3, col->column ); 836c2c66affSColin Finck if( r ) 837c2c66affSColin Finck goto err; 838c2c66affSColin Finck 839c2c66affSColin Finck r = MSI_RecordSetInteger( rec, 4, col->type ); 840c2c66affSColin Finck if( r ) 841c2c66affSColin Finck goto err; 842c2c66affSColin Finck 843c2c66affSColin Finck r = tv->ops->insert_row( tv, rec, -1, FALSE ); 844c2c66affSColin Finck if( r ) 845c2c66affSColin Finck goto err; 846c2c66affSColin Finck 847c2c66affSColin Finck nField++; 848c2c66affSColin Finck } 849c2c66affSColin Finck if( !col ) 850c2c66affSColin Finck r = ERROR_SUCCESS; 851c2c66affSColin Finck } 852c2c66affSColin Finck 853c2c66affSColin Finck err: 854c2c66affSColin Finck if (rec) 855c2c66affSColin Finck msiobj_release( &rec->hdr ); 856c2c66affSColin Finck /* FIXME: remove values from the string table on error */ 857c2c66affSColin Finck if( tv ) 858c2c66affSColin Finck tv->ops->delete( tv ); 859c2c66affSColin Finck 860c2c66affSColin Finck if (r == ERROR_SUCCESS) 861c2c66affSColin Finck list_add_head( &db->tables, &table->entry ); 862c2c66affSColin Finck else 863c2c66affSColin Finck free_table( table ); 864c2c66affSColin Finck 865c2c66affSColin Finck return r; 866c2c66affSColin Finck } 867c2c66affSColin Finck 868c2c66affSColin Finck static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref ) 869c2c66affSColin Finck { 870c2c66affSColin Finck BYTE *rawdata = NULL; 871c2c66affSColin Finck UINT rawsize, i, j, row_size, row_count; 872c2c66affSColin Finck UINT r = ERROR_FUNCTION_FAILED; 873c2c66affSColin Finck 874c2c66affSColin Finck /* Nothing to do for non-persistent tables */ 875c2c66affSColin Finck if( t->persistent == MSICONDITION_FALSE ) 876c2c66affSColin Finck return ERROR_SUCCESS; 877c2c66affSColin Finck 878c2c66affSColin Finck TRACE("Saving %s\n", debugstr_w( t->name ) ); 879c2c66affSColin Finck 880c2c66affSColin Finck row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref ); 881c2c66affSColin Finck row_count = t->row_count; 882c2c66affSColin Finck for (i = 0; i < t->row_count; i++) 883c2c66affSColin Finck { 884c2c66affSColin Finck if (!t->data_persistent[i]) 885c2c66affSColin Finck { 886c2c66affSColin Finck row_count = 1; /* yes, this is bizarre */ 887c2c66affSColin Finck break; 888c2c66affSColin Finck } 889c2c66affSColin Finck } 890c2c66affSColin Finck rawsize = row_count * row_size; 891c2c66affSColin Finck rawdata = msi_alloc_zero( rawsize ); 892c2c66affSColin Finck if( !rawdata ) 893c2c66affSColin Finck { 894c2c66affSColin Finck r = ERROR_NOT_ENOUGH_MEMORY; 895c2c66affSColin Finck goto err; 896c2c66affSColin Finck } 897c2c66affSColin Finck 898c2c66affSColin Finck rawsize = 0; 899c2c66affSColin Finck for (i = 0; i < row_count; i++) 900c2c66affSColin Finck { 901c2c66affSColin Finck UINT ofs = 0, ofs_mem = 0; 902c2c66affSColin Finck 903c2c66affSColin Finck if (!t->data_persistent[i]) break; 904c2c66affSColin Finck 905c2c66affSColin Finck for (j = 0; j < t->col_count; j++) 906c2c66affSColin Finck { 907c2c66affSColin Finck UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); 908c2c66affSColin Finck UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref ); 909c2c66affSColin Finck UINT k; 910c2c66affSColin Finck 911c2c66affSColin Finck if (n != 2 && n != 3 && n != 4) 912c2c66affSColin Finck { 913c2c66affSColin Finck ERR("oops - unknown column width %d\n", n); 914c2c66affSColin Finck goto err; 915c2c66affSColin Finck } 916c2c66affSColin Finck if (t->colinfo[j].type & MSITYPE_STRING && n < m) 917c2c66affSColin Finck { 918c2c66affSColin Finck UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES ); 919c2c66affSColin Finck if (id > 1 << bytes_per_strref * 8) 920c2c66affSColin Finck { 921c2c66affSColin Finck ERR("string id %u out of range\n", id); 922c2c66affSColin Finck goto err; 923c2c66affSColin Finck } 924c2c66affSColin Finck } 925c2c66affSColin Finck for (k = 0; k < n; k++) 926c2c66affSColin Finck { 927c2c66affSColin Finck rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k]; 928c2c66affSColin Finck } 929c2c66affSColin Finck ofs_mem += m; 930c2c66affSColin Finck ofs += n; 931c2c66affSColin Finck } 932c2c66affSColin Finck rawsize += row_size; 933c2c66affSColin Finck } 934c2c66affSColin Finck 935c2c66affSColin Finck TRACE("writing %d bytes\n", rawsize); 936c2c66affSColin Finck r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE ); 937c2c66affSColin Finck 938c2c66affSColin Finck err: 939c2c66affSColin Finck msi_free( rawdata ); 940c2c66affSColin Finck return r; 941c2c66affSColin Finck } 942c2c66affSColin Finck 943c2c66affSColin Finck static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name ) 944c2c66affSColin Finck { 945c2c66affSColin Finck MSITABLE *table; 946c2c66affSColin Finck UINT size, offset, old_count; 947c2c66affSColin Finck UINT n; 948c2c66affSColin Finck 949c2c66affSColin Finck if (!(table = find_cached_table( db, name ))) return; 950c2c66affSColin Finck old_count = table->col_count; 951c2c66affSColin Finck msi_free_colinfo( table->colinfo, table->col_count ); 952c2c66affSColin Finck msi_free( table->colinfo ); 953c2c66affSColin Finck table->colinfo = NULL; 954c2c66affSColin Finck 955c2c66affSColin Finck table_get_column_info( db, name, &table->colinfo, &table->col_count ); 956c2c66affSColin Finck if (!table->col_count) return; 957c2c66affSColin Finck 958c2c66affSColin Finck size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES ); 959c2c66affSColin Finck offset = table->colinfo[table->col_count - 1].offset; 960c2c66affSColin Finck 961c2c66affSColin Finck for ( n = 0; n < table->row_count; n++ ) 962c2c66affSColin Finck { 963c2c66affSColin Finck table->data[n] = msi_realloc( table->data[n], size ); 964c2c66affSColin Finck if (old_count < table->col_count) 965c2c66affSColin Finck memset( &table->data[n][offset], 0, size - offset ); 966c2c66affSColin Finck } 967c2c66affSColin Finck } 968c2c66affSColin Finck 969c2c66affSColin Finck /* try to find the table name in the _Tables table */ 970c2c66affSColin Finck BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ) 971c2c66affSColin Finck { 972c2c66affSColin Finck UINT r, table_id, i; 973c2c66affSColin Finck MSITABLE *table; 974c2c66affSColin Finck 975c2c66affSColin Finck if( !strcmpW( name, szTables ) || !strcmpW( name, szColumns ) || 976c2c66affSColin Finck !strcmpW( name, szStreams ) || !strcmpW( name, szStorages ) ) 977c2c66affSColin Finck return TRUE; 978c2c66affSColin Finck 979c2c66affSColin Finck r = msi_string2id( db->strings, name, -1, &table_id ); 980c2c66affSColin Finck if( r != ERROR_SUCCESS ) 981c2c66affSColin Finck { 982c2c66affSColin Finck TRACE("Couldn't find id for %s\n", debugstr_w(name)); 983c2c66affSColin Finck return FALSE; 984c2c66affSColin Finck } 985c2c66affSColin Finck 986c2c66affSColin Finck r = get_table( db, szTables, &table ); 987c2c66affSColin Finck if( r != ERROR_SUCCESS ) 988c2c66affSColin Finck { 989c2c66affSColin Finck ERR("table %s not available\n", debugstr_w(szTables)); 990c2c66affSColin Finck return FALSE; 991c2c66affSColin Finck } 992c2c66affSColin Finck 993c2c66affSColin Finck for( i = 0; i < table->row_count; i++ ) 994c2c66affSColin Finck { 995c2c66affSColin Finck if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id ) 996c2c66affSColin Finck return TRUE; 997c2c66affSColin Finck } 998c2c66affSColin Finck 999c2c66affSColin Finck return FALSE; 1000c2c66affSColin Finck } 1001c2c66affSColin Finck 1002c2c66affSColin Finck /* below is the query interface to a table */ 1003c2c66affSColin Finck 1004c2c66affSColin Finck typedef struct tagMSITABLEVIEW 1005c2c66affSColin Finck { 1006c2c66affSColin Finck MSIVIEW view; 1007c2c66affSColin Finck MSIDATABASE *db; 1008c2c66affSColin Finck MSITABLE *table; 1009c2c66affSColin Finck MSICOLUMNINFO *columns; 1010c2c66affSColin Finck UINT num_cols; 1011c2c66affSColin Finck UINT row_size; 1012c2c66affSColin Finck WCHAR name[1]; 1013c2c66affSColin Finck } MSITABLEVIEW; 1014c2c66affSColin Finck 1015c2c66affSColin Finck static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) 1016c2c66affSColin Finck { 1017c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1018c2c66affSColin Finck UINT offset, n; 1019c2c66affSColin Finck 1020c2c66affSColin Finck if( !tv->table ) 1021c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1022c2c66affSColin Finck 1023c2c66affSColin Finck if( (col==0) || (col>tv->num_cols) ) 1024c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1025c2c66affSColin Finck 1026c2c66affSColin Finck /* how many rows are there ? */ 1027c2c66affSColin Finck if( row >= tv->table->row_count ) 1028c2c66affSColin Finck return ERROR_NO_MORE_ITEMS; 1029c2c66affSColin Finck 1030c2c66affSColin Finck if( tv->columns[col-1].offset >= tv->row_size ) 1031c2c66affSColin Finck { 1032c2c66affSColin Finck ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); 1033c2c66affSColin Finck ERR("%p %p\n", tv, tv->columns ); 1034c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1035c2c66affSColin Finck } 1036c2c66affSColin Finck 1037c2c66affSColin Finck n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES ); 1038c2c66affSColin Finck if (n != 2 && n != 3 && n != 4) 1039c2c66affSColin Finck { 1040c2c66affSColin Finck ERR("oops! what is %d bytes per column?\n", n ); 1041c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1042c2c66affSColin Finck } 1043c2c66affSColin Finck 1044c2c66affSColin Finck offset = tv->columns[col-1].offset; 1045c2c66affSColin Finck *val = read_table_int(tv->table->data, row, offset, n); 1046c2c66affSColin Finck 1047c2c66affSColin Finck /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */ 1048c2c66affSColin Finck 1049c2c66affSColin Finck return ERROR_SUCCESS; 1050c2c66affSColin Finck } 1051c2c66affSColin Finck 1052c2c66affSColin Finck static UINT get_stream_name( const MSITABLEVIEW *tv, UINT row, WCHAR **pstname ) 1053c2c66affSColin Finck { 1054c2c66affSColin Finck LPWSTR p, stname = NULL; 1055c2c66affSColin Finck UINT i, r, type, ival; 1056c2c66affSColin Finck DWORD len; 1057c2c66affSColin Finck LPCWSTR sval; 1058c2c66affSColin Finck MSIVIEW *view = (MSIVIEW *) tv; 1059c2c66affSColin Finck 1060c2c66affSColin Finck TRACE("%p %d\n", tv, row); 1061c2c66affSColin Finck 1062c2c66affSColin Finck len = lstrlenW( tv->name ) + 1; 1063c2c66affSColin Finck stname = msi_alloc( len*sizeof(WCHAR) ); 1064c2c66affSColin Finck if ( !stname ) 1065c2c66affSColin Finck { 1066c2c66affSColin Finck r = ERROR_OUTOFMEMORY; 1067c2c66affSColin Finck goto err; 1068c2c66affSColin Finck } 1069c2c66affSColin Finck 1070c2c66affSColin Finck lstrcpyW( stname, tv->name ); 1071c2c66affSColin Finck 1072c2c66affSColin Finck for ( i = 0; i < tv->num_cols; i++ ) 1073c2c66affSColin Finck { 1074c2c66affSColin Finck type = tv->columns[i].type; 1075c2c66affSColin Finck if ( type & MSITYPE_KEY ) 1076c2c66affSColin Finck { 1077c2c66affSColin Finck WCHAR number[0x20]; 1078c2c66affSColin Finck 1079c2c66affSColin Finck r = TABLE_fetch_int( view, row, i+1, &ival ); 1080c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1081c2c66affSColin Finck goto err; 1082c2c66affSColin Finck 1083c2c66affSColin Finck if ( tv->columns[i].type & MSITYPE_STRING ) 1084c2c66affSColin Finck { 1085c2c66affSColin Finck sval = msi_string_lookup( tv->db->strings, ival, NULL ); 1086c2c66affSColin Finck if ( !sval ) 1087c2c66affSColin Finck { 1088c2c66affSColin Finck r = ERROR_INVALID_PARAMETER; 1089c2c66affSColin Finck goto err; 1090c2c66affSColin Finck } 1091c2c66affSColin Finck } 1092c2c66affSColin Finck else 1093c2c66affSColin Finck { 1094c2c66affSColin Finck static const WCHAR fmt[] = { '%','d',0 }; 1095c2c66affSColin Finck UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES ); 1096c2c66affSColin Finck 1097c2c66affSColin Finck switch( n ) 1098c2c66affSColin Finck { 1099c2c66affSColin Finck case 2: 1100c2c66affSColin Finck sprintfW( number, fmt, ival-0x8000 ); 1101c2c66affSColin Finck break; 1102c2c66affSColin Finck case 4: 1103c2c66affSColin Finck sprintfW( number, fmt, ival^0x80000000 ); 1104c2c66affSColin Finck break; 1105c2c66affSColin Finck default: 1106c2c66affSColin Finck ERR( "oops - unknown column width %d\n", n ); 1107c2c66affSColin Finck r = ERROR_FUNCTION_FAILED; 1108c2c66affSColin Finck goto err; 1109c2c66affSColin Finck } 1110c2c66affSColin Finck sval = number; 1111c2c66affSColin Finck } 1112c2c66affSColin Finck 1113c2c66affSColin Finck len += lstrlenW( szDot ) + lstrlenW( sval ); 1114c2c66affSColin Finck p = msi_realloc ( stname, len*sizeof(WCHAR) ); 1115c2c66affSColin Finck if ( !p ) 1116c2c66affSColin Finck { 1117c2c66affSColin Finck r = ERROR_OUTOFMEMORY; 1118c2c66affSColin Finck goto err; 1119c2c66affSColin Finck } 1120c2c66affSColin Finck stname = p; 1121c2c66affSColin Finck 1122c2c66affSColin Finck lstrcatW( stname, szDot ); 1123c2c66affSColin Finck lstrcatW( stname, sval ); 1124c2c66affSColin Finck } 1125c2c66affSColin Finck else 1126c2c66affSColin Finck continue; 1127c2c66affSColin Finck } 1128c2c66affSColin Finck 1129c2c66affSColin Finck *pstname = stname; 1130c2c66affSColin Finck return ERROR_SUCCESS; 1131c2c66affSColin Finck 1132c2c66affSColin Finck err: 1133c2c66affSColin Finck msi_free( stname ); 1134c2c66affSColin Finck *pstname = NULL; 1135c2c66affSColin Finck return r; 1136c2c66affSColin Finck } 1137c2c66affSColin Finck 1138c2c66affSColin Finck /* 1139c2c66affSColin Finck * We need a special case for streams, as we need to reference column with 1140c2c66affSColin Finck * the name of the stream in the same table, and the table name 1141c2c66affSColin Finck * which may not be available at higher levels of the query 1142c2c66affSColin Finck */ 1143c2c66affSColin Finck static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm ) 1144c2c66affSColin Finck { 1145c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1146c2c66affSColin Finck UINT r; 1147c2c66affSColin Finck WCHAR *name; 1148c2c66affSColin Finck 1149c2c66affSColin Finck if( !view->ops->fetch_int ) 1150c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1151c2c66affSColin Finck 1152c2c66affSColin Finck r = get_stream_name( tv, row, &name ); 1153c2c66affSColin Finck if (r != ERROR_SUCCESS) 1154c2c66affSColin Finck { 1155c2c66affSColin Finck ERR("fetching stream, error = %u\n", r); 1156c2c66affSColin Finck return r; 1157c2c66affSColin Finck } 1158c2c66affSColin Finck 1159c2c66affSColin Finck r = msi_get_stream( tv->db, name, stm ); 1160c2c66affSColin Finck if (r != ERROR_SUCCESS) 1161c2c66affSColin Finck ERR("fetching stream %s, error = %u\n", debugstr_w(name), r); 1162c2c66affSColin Finck 1163c2c66affSColin Finck msi_free( name ); 1164c2c66affSColin Finck return r; 1165c2c66affSColin Finck } 1166c2c66affSColin Finck 1167c2c66affSColin Finck static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val ) 1168c2c66affSColin Finck { 1169c2c66affSColin Finck UINT offset, n, i; 1170c2c66affSColin Finck 1171c2c66affSColin Finck if( !tv->table ) 1172c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1173c2c66affSColin Finck 1174c2c66affSColin Finck if( (col==0) || (col>tv->num_cols) ) 1175c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1176c2c66affSColin Finck 1177c2c66affSColin Finck if( row >= tv->table->row_count ) 1178c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1179c2c66affSColin Finck 1180c2c66affSColin Finck if( tv->columns[col-1].offset >= tv->row_size ) 1181c2c66affSColin Finck { 1182c2c66affSColin Finck ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); 1183c2c66affSColin Finck ERR("%p %p\n", tv, tv->columns ); 1184c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1185c2c66affSColin Finck } 1186c2c66affSColin Finck 1187c2c66affSColin Finck msi_free( tv->columns[col-1].hash_table ); 1188c2c66affSColin Finck tv->columns[col-1].hash_table = NULL; 1189c2c66affSColin Finck 1190c2c66affSColin Finck n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES ); 1191c2c66affSColin Finck if ( n != 2 && n != 3 && n != 4 ) 1192c2c66affSColin Finck { 1193c2c66affSColin Finck ERR("oops! what is %d bytes per column?\n", n ); 1194c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1195c2c66affSColin Finck } 1196c2c66affSColin Finck 1197c2c66affSColin Finck offset = tv->columns[col-1].offset; 1198c2c66affSColin Finck for ( i = 0; i < n; i++ ) 1199c2c66affSColin Finck tv->table->data[row][offset + i] = (val >> i * 8) & 0xff; 1200c2c66affSColin Finck 1201c2c66affSColin Finck return ERROR_SUCCESS; 1202c2c66affSColin Finck } 1203c2c66affSColin Finck 1204c2c66affSColin Finck static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) 1205c2c66affSColin Finck { 1206c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1207c2c66affSColin Finck 1208c2c66affSColin Finck if (!tv->table) 1209c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1210c2c66affSColin Finck 1211c2c66affSColin Finck return msi_view_get_row(tv->db, view, row, rec); 1212c2c66affSColin Finck } 1213c2c66affSColin Finck 1214c2c66affSColin Finck static UINT add_stream( MSIDATABASE *db, const WCHAR *name, IStream *data ) 1215c2c66affSColin Finck { 1216c2c66affSColin Finck static const WCHAR insert[] = { 1217c2c66affSColin Finck 'I','N','S','E','R','T',' ','I','N','T','O',' ', 1218c2c66affSColin Finck '`','_','S','t','r','e','a','m','s','`',' ', 1219c2c66affSColin Finck '(','`','N','a','m','e','`',',','`','D','a','t','a','`',')',' ', 1220c2c66affSColin Finck 'V','A','L','U','E','S',' ','(','?',',','?',')',0}; 1221c2c66affSColin Finck static const WCHAR update[] = { 1222c2c66affSColin Finck 'U','P','D','A','T','E',' ','`','_','S','t','r','e','a','m','s','`',' ', 1223c2c66affSColin Finck 'S','E','T',' ','`','D','a','t','a','`',' ','=',' ','?',' ', 1224c2c66affSColin Finck 'W','H','E','R','E',' ','`','N','a','m','e','`',' ','=',' ','?',0}; 1225c2c66affSColin Finck MSIQUERY *query; 1226c2c66affSColin Finck MSIRECORD *rec; 1227c2c66affSColin Finck UINT r; 1228c2c66affSColin Finck 1229c2c66affSColin Finck TRACE("%p %s %p\n", db, debugstr_w(name), data); 1230c2c66affSColin Finck 1231c2c66affSColin Finck if (!(rec = MSI_CreateRecord( 2 ))) 1232c2c66affSColin Finck return ERROR_OUTOFMEMORY; 1233c2c66affSColin Finck 1234c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 1, name ); 1235c2c66affSColin Finck if (r != ERROR_SUCCESS) 1236c2c66affSColin Finck goto done; 1237c2c66affSColin Finck 1238c2c66affSColin Finck r = MSI_RecordSetIStream( rec, 2, data ); 1239c2c66affSColin Finck if (r != ERROR_SUCCESS) 1240c2c66affSColin Finck goto done; 1241c2c66affSColin Finck 1242c2c66affSColin Finck r = MSI_DatabaseOpenViewW( db, insert, &query ); 1243c2c66affSColin Finck if (r != ERROR_SUCCESS) 1244c2c66affSColin Finck goto done; 1245c2c66affSColin Finck 1246c2c66affSColin Finck r = MSI_ViewExecute( query, rec ); 1247c2c66affSColin Finck msiobj_release( &query->hdr ); 1248c2c66affSColin Finck if (r == ERROR_SUCCESS) 1249c2c66affSColin Finck goto done; 1250c2c66affSColin Finck 1251c2c66affSColin Finck msiobj_release( &rec->hdr ); 1252c2c66affSColin Finck if (!(rec = MSI_CreateRecord( 2 ))) 1253c2c66affSColin Finck return ERROR_OUTOFMEMORY; 1254c2c66affSColin Finck 1255c2c66affSColin Finck r = MSI_RecordSetIStream( rec, 1, data ); 1256c2c66affSColin Finck if (r != ERROR_SUCCESS) 1257c2c66affSColin Finck goto done; 1258c2c66affSColin Finck 1259c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 2, name ); 1260c2c66affSColin Finck if (r != ERROR_SUCCESS) 1261c2c66affSColin Finck goto done; 1262c2c66affSColin Finck 1263c2c66affSColin Finck r = MSI_DatabaseOpenViewW( db, update, &query ); 1264c2c66affSColin Finck if (r != ERROR_SUCCESS) 1265c2c66affSColin Finck goto done; 1266c2c66affSColin Finck 1267c2c66affSColin Finck r = MSI_ViewExecute( query, rec ); 1268c2c66affSColin Finck msiobj_release( &query->hdr ); 1269c2c66affSColin Finck 1270c2c66affSColin Finck done: 1271c2c66affSColin Finck msiobj_release( &rec->hdr ); 1272c2c66affSColin Finck return r; 1273c2c66affSColin Finck } 1274c2c66affSColin Finck 1275c2c66affSColin Finck static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue ) 1276c2c66affSColin Finck { 1277c2c66affSColin Finck MSICOLUMNINFO columninfo; 1278c2c66affSColin Finck UINT r; 1279c2c66affSColin Finck int ival; 1280c2c66affSColin Finck 1281306890e8Swinesync if (!iField || iField > tv->num_cols || MSI_RecordIsNull( rec, iField )) 1282c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1283c2c66affSColin Finck 1284c2c66affSColin Finck columninfo = tv->columns[ iField - 1 ]; 1285c2c66affSColin Finck 1286c2c66affSColin Finck if ( MSITYPE_IS_BINARY(columninfo.type) ) 1287c2c66affSColin Finck { 1288c2c66affSColin Finck *pvalue = 1; /* refers to the first key column */ 1289c2c66affSColin Finck } 1290c2c66affSColin Finck else if ( columninfo.type & MSITYPE_STRING ) 1291c2c66affSColin Finck { 1292c2c66affSColin Finck int len; 1293c2c66affSColin Finck const WCHAR *sval = msi_record_get_string( rec, iField, &len ); 1294c2c66affSColin Finck if (sval) 1295c2c66affSColin Finck { 1296c2c66affSColin Finck r = msi_string2id( tv->db->strings, sval, len, pvalue ); 1297c2c66affSColin Finck if (r != ERROR_SUCCESS) 1298c2c66affSColin Finck return ERROR_NOT_FOUND; 1299c2c66affSColin Finck } 1300c2c66affSColin Finck else *pvalue = 0; 1301c2c66affSColin Finck } 1302c2c66affSColin Finck else if ( bytes_per_column( tv->db, &columninfo, LONG_STR_BYTES ) == 2 ) 1303c2c66affSColin Finck { 1304c2c66affSColin Finck ival = MSI_RecordGetInteger( rec, iField ); 1305c2c66affSColin Finck if (ival == 0x80000000) *pvalue = 0x8000; 1306c2c66affSColin Finck else 1307c2c66affSColin Finck { 1308c2c66affSColin Finck *pvalue = 0x8000 + MSI_RecordGetInteger( rec, iField ); 1309c2c66affSColin Finck if (*pvalue & 0xffff0000) 1310c2c66affSColin Finck { 1311c2c66affSColin Finck ERR("field %u value %d out of range\n", iField, *pvalue - 0x8000); 1312c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1313c2c66affSColin Finck } 1314c2c66affSColin Finck } 1315c2c66affSColin Finck } 1316c2c66affSColin Finck else 1317c2c66affSColin Finck { 1318c2c66affSColin Finck ival = MSI_RecordGetInteger( rec, iField ); 1319c2c66affSColin Finck *pvalue = ival ^ 0x80000000; 1320c2c66affSColin Finck } 1321c2c66affSColin Finck 1322c2c66affSColin Finck return ERROR_SUCCESS; 1323c2c66affSColin Finck } 1324c2c66affSColin Finck 1325c2c66affSColin Finck static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask ) 1326c2c66affSColin Finck { 1327c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1328c2c66affSColin Finck UINT i, val, r = ERROR_SUCCESS; 1329c2c66affSColin Finck 1330c2c66affSColin Finck if ( !tv->table ) 1331c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1332c2c66affSColin Finck 1333c2c66affSColin Finck /* test if any of the mask bits are invalid */ 1334c2c66affSColin Finck if ( mask >= (1<<tv->num_cols) ) 1335c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1336c2c66affSColin Finck 1337c2c66affSColin Finck for ( i = 0; i < tv->num_cols; i++ ) 1338c2c66affSColin Finck { 1339c2c66affSColin Finck BOOL persistent; 1340c2c66affSColin Finck 1341c2c66affSColin Finck /* only update the fields specified in the mask */ 1342c2c66affSColin Finck if ( !(mask&(1<<i)) ) 1343c2c66affSColin Finck continue; 1344c2c66affSColin Finck 1345c2c66affSColin Finck persistent = (tv->table->persistent != MSICONDITION_FALSE) && 1346c2c66affSColin Finck (tv->table->data_persistent[row]); 1347c2c66affSColin Finck /* FIXME: should we allow updating keys? */ 1348c2c66affSColin Finck 1349c2c66affSColin Finck val = 0; 1350c2c66affSColin Finck if ( !MSI_RecordIsNull( rec, i + 1 ) ) 1351c2c66affSColin Finck { 1352c2c66affSColin Finck r = get_table_value_from_record (tv, rec, i + 1, &val); 1353c2c66affSColin Finck if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) ) 1354c2c66affSColin Finck { 1355c2c66affSColin Finck IStream *stm; 1356c2c66affSColin Finck LPWSTR stname; 1357c2c66affSColin Finck 1358c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1359c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1360c2c66affSColin Finck 1361c2c66affSColin Finck r = MSI_RecordGetIStream( rec, i + 1, &stm ); 1362c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1363c2c66affSColin Finck return r; 1364c2c66affSColin Finck 1365c2c66affSColin Finck r = get_stream_name( tv, row, &stname ); 1366c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1367c2c66affSColin Finck { 1368c2c66affSColin Finck IStream_Release( stm ); 1369c2c66affSColin Finck return r; 1370c2c66affSColin Finck } 1371c2c66affSColin Finck 1372c2c66affSColin Finck r = add_stream( tv->db, stname, stm ); 1373c2c66affSColin Finck IStream_Release( stm ); 1374c2c66affSColin Finck msi_free ( stname ); 1375c2c66affSColin Finck 1376c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1377c2c66affSColin Finck return r; 1378c2c66affSColin Finck } 1379c2c66affSColin Finck else if ( tv->columns[i].type & MSITYPE_STRING ) 1380c2c66affSColin Finck { 1381c2c66affSColin Finck UINT x; 1382c2c66affSColin Finck 1383c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1384c2c66affSColin Finck { 1385c2c66affSColin Finck int len; 1386c2c66affSColin Finck const WCHAR *sval = msi_record_get_string( rec, i + 1, &len ); 1387*211c4104Swinesync val = msi_add_string( tv->db->strings, sval, len, persistent ); 1388c2c66affSColin Finck } 1389c2c66affSColin Finck else 1390c2c66affSColin Finck { 1391c2c66affSColin Finck TABLE_fetch_int(&tv->view, row, i + 1, &x); 1392c2c66affSColin Finck if (val == x) 1393c2c66affSColin Finck continue; 1394c2c66affSColin Finck } 1395c2c66affSColin Finck } 1396c2c66affSColin Finck else 1397c2c66affSColin Finck { 1398c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1399c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1400c2c66affSColin Finck } 1401c2c66affSColin Finck } 1402c2c66affSColin Finck 1403c2c66affSColin Finck r = TABLE_set_int( tv, row, i+1, val ); 1404c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1405c2c66affSColin Finck break; 1406c2c66affSColin Finck } 1407c2c66affSColin Finck return r; 1408c2c66affSColin Finck } 1409c2c66affSColin Finck 1410c2c66affSColin Finck static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary ) 1411c2c66affSColin Finck { 1412c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1413c2c66affSColin Finck BYTE **p, *row; 1414c2c66affSColin Finck BOOL *b; 1415c2c66affSColin Finck UINT sz; 1416c2c66affSColin Finck BYTE ***data_ptr; 1417c2c66affSColin Finck BOOL **data_persist_ptr; 1418c2c66affSColin Finck UINT *row_count; 1419c2c66affSColin Finck 1420c2c66affSColin Finck TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE"); 1421c2c66affSColin Finck 1422c2c66affSColin Finck if( !tv->table ) 1423c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1424c2c66affSColin Finck 1425c2c66affSColin Finck row = msi_alloc_zero( tv->row_size ); 1426c2c66affSColin Finck if( !row ) 1427c2c66affSColin Finck return ERROR_NOT_ENOUGH_MEMORY; 1428c2c66affSColin Finck 1429c2c66affSColin Finck row_count = &tv->table->row_count; 1430c2c66affSColin Finck data_ptr = &tv->table->data; 1431c2c66affSColin Finck data_persist_ptr = &tv->table->data_persistent; 1432c2c66affSColin Finck if (*num == -1) 1433c2c66affSColin Finck *num = tv->table->row_count; 1434c2c66affSColin Finck 1435c2c66affSColin Finck sz = (*row_count + 1) * sizeof (BYTE*); 1436c2c66affSColin Finck if( *data_ptr ) 1437c2c66affSColin Finck p = msi_realloc( *data_ptr, sz ); 1438c2c66affSColin Finck else 1439c2c66affSColin Finck p = msi_alloc( sz ); 1440c2c66affSColin Finck if( !p ) 1441c2c66affSColin Finck { 1442c2c66affSColin Finck msi_free( row ); 1443c2c66affSColin Finck return ERROR_NOT_ENOUGH_MEMORY; 1444c2c66affSColin Finck } 1445c2c66affSColin Finck 1446c2c66affSColin Finck sz = (*row_count + 1) * sizeof (BOOL); 1447c2c66affSColin Finck if( *data_persist_ptr ) 1448c2c66affSColin Finck b = msi_realloc( *data_persist_ptr, sz ); 1449c2c66affSColin Finck else 1450c2c66affSColin Finck b = msi_alloc( sz ); 1451c2c66affSColin Finck if( !b ) 1452c2c66affSColin Finck { 1453c2c66affSColin Finck msi_free( row ); 1454c2c66affSColin Finck msi_free( p ); 1455c2c66affSColin Finck return ERROR_NOT_ENOUGH_MEMORY; 1456c2c66affSColin Finck } 1457c2c66affSColin Finck 1458c2c66affSColin Finck *data_ptr = p; 1459c2c66affSColin Finck (*data_ptr)[*row_count] = row; 1460c2c66affSColin Finck 1461c2c66affSColin Finck *data_persist_ptr = b; 1462c2c66affSColin Finck (*data_persist_ptr)[*row_count] = !temporary; 1463c2c66affSColin Finck 1464c2c66affSColin Finck (*row_count)++; 1465c2c66affSColin Finck 1466c2c66affSColin Finck return ERROR_SUCCESS; 1467c2c66affSColin Finck } 1468c2c66affSColin Finck 1469c2c66affSColin Finck static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) 1470c2c66affSColin Finck { 1471c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1472c2c66affSColin Finck 1473c2c66affSColin Finck TRACE("%p %p\n", tv, record); 1474c2c66affSColin Finck 1475c2c66affSColin Finck TRACE("There are %d columns\n", tv->num_cols ); 1476c2c66affSColin Finck 1477c2c66affSColin Finck return ERROR_SUCCESS; 1478c2c66affSColin Finck } 1479c2c66affSColin Finck 1480c2c66affSColin Finck static UINT TABLE_close( struct tagMSIVIEW *view ) 1481c2c66affSColin Finck { 1482c2c66affSColin Finck TRACE("%p\n", view ); 1483c2c66affSColin Finck 1484c2c66affSColin Finck return ERROR_SUCCESS; 1485c2c66affSColin Finck } 1486c2c66affSColin Finck 1487c2c66affSColin Finck static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols) 1488c2c66affSColin Finck { 1489c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1490c2c66affSColin Finck 1491c2c66affSColin Finck TRACE("%p %p %p\n", view, rows, cols ); 1492c2c66affSColin Finck 1493c2c66affSColin Finck if( cols ) 1494c2c66affSColin Finck *cols = tv->num_cols; 1495c2c66affSColin Finck if( rows ) 1496c2c66affSColin Finck { 1497c2c66affSColin Finck if( !tv->table ) 1498c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1499c2c66affSColin Finck *rows = tv->table->row_count; 1500c2c66affSColin Finck } 1501c2c66affSColin Finck 1502c2c66affSColin Finck return ERROR_SUCCESS; 1503c2c66affSColin Finck } 1504c2c66affSColin Finck 1505c2c66affSColin Finck static UINT TABLE_get_column_info( struct tagMSIVIEW *view, 1506c2c66affSColin Finck UINT n, LPCWSTR *name, UINT *type, BOOL *temporary, 1507c2c66affSColin Finck LPCWSTR *table_name ) 1508c2c66affSColin Finck { 1509c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1510c2c66affSColin Finck 1511c2c66affSColin Finck TRACE("%p %d %p %p\n", tv, n, name, type ); 1512c2c66affSColin Finck 1513c2c66affSColin Finck if( ( n == 0 ) || ( n > tv->num_cols ) ) 1514c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1515c2c66affSColin Finck 1516c2c66affSColin Finck if( name ) 1517c2c66affSColin Finck { 1518c2c66affSColin Finck *name = tv->columns[n-1].colname; 1519c2c66affSColin Finck if( !*name ) 1520c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1521c2c66affSColin Finck } 1522c2c66affSColin Finck 1523c2c66affSColin Finck if( table_name ) 1524c2c66affSColin Finck { 1525c2c66affSColin Finck *table_name = tv->columns[n-1].tablename; 1526c2c66affSColin Finck if( !*table_name ) 1527c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1528c2c66affSColin Finck } 1529c2c66affSColin Finck 1530c2c66affSColin Finck if( type ) 1531c2c66affSColin Finck *type = tv->columns[n-1].type; 1532c2c66affSColin Finck 1533c2c66affSColin Finck if( temporary ) 1534c2c66affSColin Finck *temporary = tv->columns[n-1].temporary; 1535c2c66affSColin Finck 1536c2c66affSColin Finck return ERROR_SUCCESS; 1537c2c66affSColin Finck } 1538c2c66affSColin Finck 1539c2c66affSColin Finck static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column ); 1540c2c66affSColin Finck 1541c2c66affSColin Finck static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column ) 1542c2c66affSColin Finck { 1543c2c66affSColin Finck UINT r, row, i; 1544c2c66affSColin Finck 1545c2c66affSColin Finck /* check there are no null values where they're not allowed */ 1546c2c66affSColin Finck for( i = 0; i < tv->num_cols; i++ ) 1547c2c66affSColin Finck { 1548c2c66affSColin Finck if ( tv->columns[i].type & MSITYPE_NULLABLE ) 1549c2c66affSColin Finck continue; 1550c2c66affSColin Finck 1551c2c66affSColin Finck if ( MSITYPE_IS_BINARY(tv->columns[i].type) ) 1552c2c66affSColin Finck TRACE("skipping binary column\n"); 1553c2c66affSColin Finck else if ( tv->columns[i].type & MSITYPE_STRING ) 1554c2c66affSColin Finck { 1555c2c66affSColin Finck int len; 1556c2c66affSColin Finck const WCHAR *str = msi_record_get_string( rec, i+1, &len ); 1557c2c66affSColin Finck 1558c2c66affSColin Finck if (!str || (!str[0] && !len)) 1559c2c66affSColin Finck { 1560c2c66affSColin Finck if (column) *column = i; 1561c2c66affSColin Finck return ERROR_INVALID_DATA; 1562c2c66affSColin Finck } 1563c2c66affSColin Finck } 1564c2c66affSColin Finck else 1565c2c66affSColin Finck { 1566c2c66affSColin Finck UINT n; 1567c2c66affSColin Finck 1568c2c66affSColin Finck n = MSI_RecordGetInteger( rec, i+1 ); 1569c2c66affSColin Finck if (n == MSI_NULL_INTEGER) 1570c2c66affSColin Finck { 1571c2c66affSColin Finck if (column) *column = i; 1572c2c66affSColin Finck return ERROR_INVALID_DATA; 1573c2c66affSColin Finck } 1574c2c66affSColin Finck } 1575c2c66affSColin Finck } 1576c2c66affSColin Finck 1577c2c66affSColin Finck /* check there are no duplicate keys */ 1578c2c66affSColin Finck r = msi_table_find_row( tv, rec, &row, column ); 1579c2c66affSColin Finck if (r == ERROR_SUCCESS) 1580c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1581c2c66affSColin Finck 1582c2c66affSColin Finck return ERROR_SUCCESS; 1583c2c66affSColin Finck } 1584c2c66affSColin Finck 1585c2c66affSColin Finck static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec ) 1586c2c66affSColin Finck { 1587c2c66affSColin Finck UINT r, i, ivalue, x; 1588c2c66affSColin Finck 1589c2c66affSColin Finck for (i = 0; i < tv->num_cols; i++ ) 1590c2c66affSColin Finck { 1591c2c66affSColin Finck if (!(tv->columns[i].type & MSITYPE_KEY)) continue; 1592c2c66affSColin Finck 1593c2c66affSColin Finck r = get_table_value_from_record( tv, rec, i + 1, &ivalue ); 1594c2c66affSColin Finck if (r != ERROR_SUCCESS) 1595c2c66affSColin Finck return 1; 1596c2c66affSColin Finck 1597c2c66affSColin Finck r = TABLE_fetch_int( &tv->view, row, i + 1, &x ); 1598c2c66affSColin Finck if (r != ERROR_SUCCESS) 1599c2c66affSColin Finck { 1600c2c66affSColin Finck WARN("TABLE_fetch_int should not fail here %u\n", r); 1601c2c66affSColin Finck return -1; 1602c2c66affSColin Finck } 1603c2c66affSColin Finck if (ivalue > x) 1604c2c66affSColin Finck { 1605c2c66affSColin Finck return 1; 1606c2c66affSColin Finck } 1607c2c66affSColin Finck else if (ivalue == x) 1608c2c66affSColin Finck { 1609c2c66affSColin Finck if (i < tv->num_cols - 1) continue; 1610c2c66affSColin Finck return 0; 1611c2c66affSColin Finck } 1612c2c66affSColin Finck else 1613c2c66affSColin Finck return -1; 1614c2c66affSColin Finck } 1615c2c66affSColin Finck return 1; 1616c2c66affSColin Finck } 1617c2c66affSColin Finck 1618c2c66affSColin Finck static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec ) 1619c2c66affSColin Finck { 1620c2c66affSColin Finck int idx, c, low = 0, high = tv->table->row_count - 1; 1621c2c66affSColin Finck 1622c2c66affSColin Finck TRACE("%p %p\n", tv, rec); 1623c2c66affSColin Finck 1624c2c66affSColin Finck while (low <= high) 1625c2c66affSColin Finck { 1626c2c66affSColin Finck idx = (low + high) / 2; 1627c2c66affSColin Finck c = compare_record( tv, idx, rec ); 1628c2c66affSColin Finck 1629c2c66affSColin Finck if (c < 0) 1630c2c66affSColin Finck high = idx - 1; 1631c2c66affSColin Finck else if (c > 0) 1632c2c66affSColin Finck low = idx + 1; 1633c2c66affSColin Finck else 1634c2c66affSColin Finck { 1635c2c66affSColin Finck TRACE("found %u\n", idx); 1636c2c66affSColin Finck return idx; 1637c2c66affSColin Finck } 1638c2c66affSColin Finck } 1639c2c66affSColin Finck TRACE("found %u\n", high + 1); 1640c2c66affSColin Finck return high + 1; 1641c2c66affSColin Finck } 1642c2c66affSColin Finck 1643c2c66affSColin Finck static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary ) 1644c2c66affSColin Finck { 1645c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1646c2c66affSColin Finck UINT i, r; 1647c2c66affSColin Finck 1648c2c66affSColin Finck TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" ); 1649c2c66affSColin Finck 1650c2c66affSColin Finck /* check that the key is unique - can we find a matching row? */ 1651c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1652c2c66affSColin Finck if( r != ERROR_SUCCESS ) 1653c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1654c2c66affSColin Finck 1655c2c66affSColin Finck if (row == -1) 1656c2c66affSColin Finck row = find_insert_index( tv, rec ); 1657c2c66affSColin Finck 1658c2c66affSColin Finck r = table_create_new_row( view, &row, temporary ); 1659c2c66affSColin Finck TRACE("insert_row returned %08x\n", r); 1660c2c66affSColin Finck if( r != ERROR_SUCCESS ) 1661c2c66affSColin Finck return r; 1662c2c66affSColin Finck 1663c2c66affSColin Finck /* shift the rows to make room for the new row */ 1664c2c66affSColin Finck for (i = tv->table->row_count - 1; i > row; i--) 1665c2c66affSColin Finck { 1666c2c66affSColin Finck memmove(&(tv->table->data[i][0]), 1667c2c66affSColin Finck &(tv->table->data[i - 1][0]), tv->row_size); 1668c2c66affSColin Finck tv->table->data_persistent[i] = tv->table->data_persistent[i - 1]; 1669c2c66affSColin Finck } 1670c2c66affSColin Finck 1671c2c66affSColin Finck /* Re-set the persistence flag */ 1672c2c66affSColin Finck tv->table->data_persistent[row] = !temporary; 1673c2c66affSColin Finck return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 ); 1674c2c66affSColin Finck } 1675c2c66affSColin Finck 1676c2c66affSColin Finck static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row ) 1677c2c66affSColin Finck { 1678c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1679c2c66affSColin Finck UINT r, num_rows, num_cols, i; 1680c2c66affSColin Finck 1681c2c66affSColin Finck TRACE("%p %d\n", tv, row); 1682c2c66affSColin Finck 1683c2c66affSColin Finck if ( !tv->table ) 1684c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1685c2c66affSColin Finck 1686c2c66affSColin Finck r = TABLE_get_dimensions( view, &num_rows, &num_cols ); 1687c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1688c2c66affSColin Finck return r; 1689c2c66affSColin Finck 1690c2c66affSColin Finck if ( row >= num_rows ) 1691c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1692c2c66affSColin Finck 1693c2c66affSColin Finck num_rows = tv->table->row_count; 1694c2c66affSColin Finck tv->table->row_count--; 1695c2c66affSColin Finck 1696c2c66affSColin Finck /* reset the hash tables */ 1697c2c66affSColin Finck for (i = 0; i < tv->num_cols; i++) 1698c2c66affSColin Finck { 1699c2c66affSColin Finck msi_free( tv->columns[i].hash_table ); 1700c2c66affSColin Finck tv->columns[i].hash_table = NULL; 1701c2c66affSColin Finck } 1702c2c66affSColin Finck 1703c2c66affSColin Finck for (i = row + 1; i < num_rows; i++) 1704c2c66affSColin Finck { 1705c2c66affSColin Finck memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size); 1706c2c66affSColin Finck tv->table->data_persistent[i - 1] = tv->table->data_persistent[i]; 1707c2c66affSColin Finck } 1708c2c66affSColin Finck 1709c2c66affSColin Finck msi_free(tv->table->data[num_rows - 1]); 1710c2c66affSColin Finck 1711c2c66affSColin Finck return ERROR_SUCCESS; 1712c2c66affSColin Finck } 1713c2c66affSColin Finck 1714c2c66affSColin Finck static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row) 1715c2c66affSColin Finck { 1716c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1717c2c66affSColin Finck UINT r, new_row; 1718c2c66affSColin Finck 1719c2c66affSColin Finck /* FIXME: MsiViewFetch should set rec index 0 to some ID that 1720c2c66affSColin Finck * sets the fetched record apart from other records 1721c2c66affSColin Finck */ 1722c2c66affSColin Finck 1723c2c66affSColin Finck if (!tv->table) 1724c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1725c2c66affSColin Finck 1726c2c66affSColin Finck r = msi_table_find_row(tv, rec, &new_row, NULL); 1727c2c66affSColin Finck if (r != ERROR_SUCCESS) 1728c2c66affSColin Finck { 1729c2c66affSColin Finck ERR("can't find row to modify\n"); 1730c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1731c2c66affSColin Finck } 1732c2c66affSColin Finck 1733c2c66affSColin Finck /* the row cannot be changed */ 1734c2c66affSColin Finck if (row != new_row + 1) 1735c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1736c2c66affSColin Finck 1737c2c66affSColin Finck return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1); 1738c2c66affSColin Finck } 1739c2c66affSColin Finck 1740c2c66affSColin Finck static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec) 1741c2c66affSColin Finck { 1742c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1743c2c66affSColin Finck UINT r, row; 1744c2c66affSColin Finck 1745c2c66affSColin Finck if (!tv->table) 1746c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1747c2c66affSColin Finck 1748c2c66affSColin Finck r = msi_table_find_row(tv, rec, &row, NULL); 1749c2c66affSColin Finck if (r == ERROR_SUCCESS) 1750c2c66affSColin Finck return TABLE_set_row(view, row, rec, (1 << tv->num_cols) - 1); 1751c2c66affSColin Finck else 1752c2c66affSColin Finck return TABLE_insert_row( view, rec, -1, FALSE ); 1753c2c66affSColin Finck } 1754c2c66affSColin Finck 1755c2c66affSColin Finck static UINT modify_delete_row( struct tagMSIVIEW *view, MSIRECORD *rec ) 1756c2c66affSColin Finck { 1757c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1758c2c66affSColin Finck UINT row, r; 1759c2c66affSColin Finck 1760c2c66affSColin Finck r = msi_table_find_row(tv, rec, &row, NULL); 1761c2c66affSColin Finck if (r != ERROR_SUCCESS) 1762c2c66affSColin Finck return r; 1763c2c66affSColin Finck 1764c2c66affSColin Finck return TABLE_delete_row(view, row); 1765c2c66affSColin Finck } 1766c2c66affSColin Finck 1767c2c66affSColin Finck static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row ) 1768c2c66affSColin Finck { 1769c2c66affSColin Finck MSIRECORD *curr; 1770c2c66affSColin Finck UINT r, i, count; 1771c2c66affSColin Finck 1772c2c66affSColin Finck r = TABLE_get_row(view, row - 1, &curr); 1773c2c66affSColin Finck if (r != ERROR_SUCCESS) 1774c2c66affSColin Finck return r; 1775c2c66affSColin Finck 1776c2c66affSColin Finck /* Close the original record */ 1777c2c66affSColin Finck MSI_CloseRecord(&rec->hdr); 1778c2c66affSColin Finck 1779c2c66affSColin Finck count = MSI_RecordGetFieldCount(rec); 1780c2c66affSColin Finck for (i = 0; i < count; i++) 1781c2c66affSColin Finck MSI_RecordCopyField(curr, i + 1, rec, i + 1); 1782c2c66affSColin Finck 1783c2c66affSColin Finck msiobj_release(&curr->hdr); 1784c2c66affSColin Finck return ERROR_SUCCESS; 1785c2c66affSColin Finck } 1786c2c66affSColin Finck 1787c2c66affSColin Finck static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, 1788c2c66affSColin Finck MSIRECORD *rec, UINT row) 1789c2c66affSColin Finck { 1790c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1791c2c66affSColin Finck UINT r, frow, column; 1792c2c66affSColin Finck 1793c2c66affSColin Finck TRACE("%p %d %p\n", view, eModifyMode, rec ); 1794c2c66affSColin Finck 1795c2c66affSColin Finck switch (eModifyMode) 1796c2c66affSColin Finck { 1797c2c66affSColin Finck case MSIMODIFY_DELETE: 1798c2c66affSColin Finck r = modify_delete_row( view, rec ); 1799c2c66affSColin Finck break; 1800c2c66affSColin Finck case MSIMODIFY_VALIDATE_NEW: 1801c2c66affSColin Finck r = table_validate_new( tv, rec, &column ); 1802c2c66affSColin Finck if (r != ERROR_SUCCESS) 1803c2c66affSColin Finck { 1804c2c66affSColin Finck tv->view.error = MSIDBERROR_DUPLICATEKEY; 1805c2c66affSColin Finck tv->view.error_column = tv->columns[column].colname; 1806c2c66affSColin Finck r = ERROR_INVALID_DATA; 1807c2c66affSColin Finck } 1808c2c66affSColin Finck break; 1809c2c66affSColin Finck 1810c2c66affSColin Finck case MSIMODIFY_INSERT: 1811c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1812c2c66affSColin Finck if (r != ERROR_SUCCESS) 1813c2c66affSColin Finck break; 1814c2c66affSColin Finck r = TABLE_insert_row( view, rec, -1, FALSE ); 1815c2c66affSColin Finck break; 1816c2c66affSColin Finck 1817c2c66affSColin Finck case MSIMODIFY_INSERT_TEMPORARY: 1818c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1819c2c66affSColin Finck if (r != ERROR_SUCCESS) 1820c2c66affSColin Finck break; 1821c2c66affSColin Finck r = TABLE_insert_row( view, rec, -1, TRUE ); 1822c2c66affSColin Finck break; 1823c2c66affSColin Finck 1824c2c66affSColin Finck case MSIMODIFY_REFRESH: 1825c2c66affSColin Finck r = msi_refresh_record( view, rec, row ); 1826c2c66affSColin Finck break; 1827c2c66affSColin Finck 1828c2c66affSColin Finck case MSIMODIFY_UPDATE: 1829c2c66affSColin Finck r = msi_table_update( view, rec, row ); 1830c2c66affSColin Finck break; 1831c2c66affSColin Finck 1832c2c66affSColin Finck case MSIMODIFY_ASSIGN: 1833c2c66affSColin Finck r = msi_table_assign( view, rec ); 1834c2c66affSColin Finck break; 1835c2c66affSColin Finck 1836c2c66affSColin Finck case MSIMODIFY_MERGE: 1837c2c66affSColin Finck /* check row that matches this record */ 1838c2c66affSColin Finck r = msi_table_find_row( tv, rec, &frow, &column ); 1839c2c66affSColin Finck if (r != ERROR_SUCCESS) 1840c2c66affSColin Finck { 1841c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1842c2c66affSColin Finck if (r == ERROR_SUCCESS) 1843c2c66affSColin Finck r = TABLE_insert_row( view, rec, -1, FALSE ); 1844c2c66affSColin Finck } 1845c2c66affSColin Finck break; 1846c2c66affSColin Finck 1847c2c66affSColin Finck case MSIMODIFY_REPLACE: 1848c2c66affSColin Finck case MSIMODIFY_VALIDATE: 1849c2c66affSColin Finck case MSIMODIFY_VALIDATE_FIELD: 1850c2c66affSColin Finck case MSIMODIFY_VALIDATE_DELETE: 1851c2c66affSColin Finck FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); 1852c2c66affSColin Finck r = ERROR_CALL_NOT_IMPLEMENTED; 1853c2c66affSColin Finck break; 1854c2c66affSColin Finck 1855c2c66affSColin Finck default: 1856c2c66affSColin Finck r = ERROR_INVALID_DATA; 1857c2c66affSColin Finck } 1858c2c66affSColin Finck 1859c2c66affSColin Finck return r; 1860c2c66affSColin Finck } 1861c2c66affSColin Finck 1862c2c66affSColin Finck static UINT TABLE_delete( struct tagMSIVIEW *view ) 1863c2c66affSColin Finck { 1864c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1865c2c66affSColin Finck 1866c2c66affSColin Finck TRACE("%p\n", view ); 1867c2c66affSColin Finck 1868c2c66affSColin Finck tv->table = NULL; 1869c2c66affSColin Finck tv->columns = NULL; 1870c2c66affSColin Finck 1871c2c66affSColin Finck msi_free( tv ); 1872c2c66affSColin Finck 1873c2c66affSColin Finck return ERROR_SUCCESS; 1874c2c66affSColin Finck } 1875c2c66affSColin Finck 1876c2c66affSColin Finck static UINT TABLE_add_ref(struct tagMSIVIEW *view) 1877c2c66affSColin Finck { 1878c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1879c2c66affSColin Finck UINT i; 1880c2c66affSColin Finck 1881c2c66affSColin Finck TRACE("%p %d\n", view, tv->table->ref_count); 1882c2c66affSColin Finck 1883c2c66affSColin Finck for (i = 0; i < tv->table->col_count; i++) 1884c2c66affSColin Finck { 1885c2c66affSColin Finck if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY) 1886c2c66affSColin Finck InterlockedIncrement(&tv->table->colinfo[i].ref_count); 1887c2c66affSColin Finck } 1888c2c66affSColin Finck 1889c2c66affSColin Finck return InterlockedIncrement(&tv->table->ref_count); 1890c2c66affSColin Finck } 1891c2c66affSColin Finck 1892c2c66affSColin Finck static UINT TABLE_remove_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number) 1893c2c66affSColin Finck { 1894c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1895c2c66affSColin Finck MSIRECORD *rec = NULL; 1896c2c66affSColin Finck MSIVIEW *columns = NULL; 1897c2c66affSColin Finck UINT row, r; 1898c2c66affSColin Finck 1899c2c66affSColin Finck rec = MSI_CreateRecord(2); 1900c2c66affSColin Finck if (!rec) 1901c2c66affSColin Finck return ERROR_OUTOFMEMORY; 1902c2c66affSColin Finck 1903c2c66affSColin Finck MSI_RecordSetStringW(rec, 1, table); 1904c2c66affSColin Finck MSI_RecordSetInteger(rec, 2, number); 1905c2c66affSColin Finck 1906c2c66affSColin Finck r = TABLE_CreateView(tv->db, szColumns, &columns); 1907c2c66affSColin Finck if (r != ERROR_SUCCESS) 1908c2c66affSColin Finck { 1909c2c66affSColin Finck msiobj_release(&rec->hdr); 1910c2c66affSColin Finck return r; 1911c2c66affSColin Finck } 1912c2c66affSColin Finck 1913c2c66affSColin Finck r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL); 1914c2c66affSColin Finck if (r != ERROR_SUCCESS) 1915c2c66affSColin Finck goto done; 1916c2c66affSColin Finck 1917c2c66affSColin Finck r = TABLE_delete_row(columns, row); 1918c2c66affSColin Finck if (r != ERROR_SUCCESS) 1919c2c66affSColin Finck goto done; 1920c2c66affSColin Finck 1921c2c66affSColin Finck msi_update_table_columns(tv->db, table); 1922c2c66affSColin Finck 1923c2c66affSColin Finck done: 1924c2c66affSColin Finck msiobj_release(&rec->hdr); 1925c2c66affSColin Finck columns->ops->delete(columns); 1926c2c66affSColin Finck return r; 1927c2c66affSColin Finck } 1928c2c66affSColin Finck 1929c2c66affSColin Finck static UINT TABLE_release(struct tagMSIVIEW *view) 1930c2c66affSColin Finck { 1931c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1932c2c66affSColin Finck INT ref = tv->table->ref_count; 1933c2c66affSColin Finck UINT i, r; 1934c2c66affSColin Finck 1935c2c66affSColin Finck TRACE("%p %d\n", view, ref); 1936c2c66affSColin Finck 1937c2c66affSColin Finck for (i = 0; i < tv->table->col_count; i++) 1938c2c66affSColin Finck { 1939c2c66affSColin Finck if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY) 1940c2c66affSColin Finck { 1941c2c66affSColin Finck ref = InterlockedDecrement(&tv->table->colinfo[i].ref_count); 1942c2c66affSColin Finck if (ref == 0) 1943c2c66affSColin Finck { 1944c2c66affSColin Finck r = TABLE_remove_column(view, tv->table->colinfo[i].tablename, 1945c2c66affSColin Finck tv->table->colinfo[i].number); 1946c2c66affSColin Finck if (r != ERROR_SUCCESS) 1947c2c66affSColin Finck break; 1948c2c66affSColin Finck } 1949c2c66affSColin Finck } 1950c2c66affSColin Finck } 1951c2c66affSColin Finck 1952c2c66affSColin Finck ref = InterlockedDecrement(&tv->table->ref_count); 1953c2c66affSColin Finck if (ref == 0) 1954c2c66affSColin Finck { 1955c2c66affSColin Finck if (!tv->table->row_count) 1956c2c66affSColin Finck { 1957c2c66affSColin Finck list_remove(&tv->table->entry); 1958c2c66affSColin Finck free_table(tv->table); 1959c2c66affSColin Finck TABLE_delete(view); 1960c2c66affSColin Finck } 1961c2c66affSColin Finck } 1962c2c66affSColin Finck 1963c2c66affSColin Finck return ref; 1964c2c66affSColin Finck } 1965c2c66affSColin Finck 1966c2c66affSColin Finck static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number, 1967c2c66affSColin Finck LPCWSTR column, UINT type, BOOL hold) 1968c2c66affSColin Finck { 1969c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1970c2c66affSColin Finck MSITABLE *msitable; 1971c2c66affSColin Finck MSIRECORD *rec; 1972c2c66affSColin Finck UINT r, i; 1973c2c66affSColin Finck 1974c2c66affSColin Finck rec = MSI_CreateRecord(4); 1975c2c66affSColin Finck if (!rec) 1976c2c66affSColin Finck return ERROR_OUTOFMEMORY; 1977c2c66affSColin Finck 1978c2c66affSColin Finck MSI_RecordSetStringW(rec, 1, table); 1979c2c66affSColin Finck MSI_RecordSetInteger(rec, 2, number); 1980c2c66affSColin Finck MSI_RecordSetStringW(rec, 3, column); 1981c2c66affSColin Finck MSI_RecordSetInteger(rec, 4, type); 1982c2c66affSColin Finck 1983c2c66affSColin Finck r = TABLE_insert_row(&tv->view, rec, -1, FALSE); 1984c2c66affSColin Finck if (r != ERROR_SUCCESS) 1985c2c66affSColin Finck goto done; 1986c2c66affSColin Finck 1987c2c66affSColin Finck msi_update_table_columns(tv->db, table); 1988c2c66affSColin Finck 1989c2c66affSColin Finck if (!hold) 1990c2c66affSColin Finck goto done; 1991c2c66affSColin Finck 1992c2c66affSColin Finck msitable = find_cached_table(tv->db, table); 1993c2c66affSColin Finck for (i = 0; i < msitable->col_count; i++) 1994c2c66affSColin Finck { 1995c2c66affSColin Finck if (!strcmpW( msitable->colinfo[i].colname, column )) 1996c2c66affSColin Finck { 1997c2c66affSColin Finck InterlockedIncrement(&msitable->colinfo[i].ref_count); 1998c2c66affSColin Finck break; 1999c2c66affSColin Finck } 2000c2c66affSColin Finck } 2001c2c66affSColin Finck 2002c2c66affSColin Finck done: 2003c2c66affSColin Finck msiobj_release(&rec->hdr); 2004c2c66affSColin Finck return r; 2005c2c66affSColin Finck } 2006c2c66affSColin Finck 2007c2c66affSColin Finck static UINT TABLE_drop(struct tagMSIVIEW *view) 2008c2c66affSColin Finck { 2009c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 2010c2c66affSColin Finck MSIVIEW *tables = NULL; 2011c2c66affSColin Finck MSIRECORD *rec = NULL; 2012c2c66affSColin Finck UINT r, row; 2013c2c66affSColin Finck INT i; 2014c2c66affSColin Finck 2015c2c66affSColin Finck TRACE("dropping table %s\n", debugstr_w(tv->name)); 2016c2c66affSColin Finck 2017c2c66affSColin Finck for (i = tv->table->col_count - 1; i >= 0; i--) 2018c2c66affSColin Finck { 2019c2c66affSColin Finck r = TABLE_remove_column(view, tv->table->colinfo[i].tablename, 2020c2c66affSColin Finck tv->table->colinfo[i].number); 2021c2c66affSColin Finck if (r != ERROR_SUCCESS) 2022c2c66affSColin Finck return r; 2023c2c66affSColin Finck } 2024c2c66affSColin Finck 2025c2c66affSColin Finck rec = MSI_CreateRecord(1); 2026c2c66affSColin Finck if (!rec) 2027c2c66affSColin Finck return ERROR_OUTOFMEMORY; 2028c2c66affSColin Finck 2029c2c66affSColin Finck MSI_RecordSetStringW(rec, 1, tv->name); 2030c2c66affSColin Finck 2031c2c66affSColin Finck r = TABLE_CreateView(tv->db, szTables, &tables); 2032c2c66affSColin Finck if (r != ERROR_SUCCESS) 2033c2c66affSColin Finck { 2034c2c66affSColin Finck msiobj_release(&rec->hdr); 2035c2c66affSColin Finck return r; 2036c2c66affSColin Finck } 2037c2c66affSColin Finck 2038c2c66affSColin Finck r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row, NULL); 2039c2c66affSColin Finck if (r != ERROR_SUCCESS) 2040c2c66affSColin Finck goto done; 2041c2c66affSColin Finck 2042c2c66affSColin Finck r = TABLE_delete_row(tables, row); 2043c2c66affSColin Finck if (r != ERROR_SUCCESS) 2044c2c66affSColin Finck goto done; 2045c2c66affSColin Finck 2046c2c66affSColin Finck list_remove(&tv->table->entry); 2047c2c66affSColin Finck free_table(tv->table); 2048c2c66affSColin Finck 2049c2c66affSColin Finck done: 2050c2c66affSColin Finck msiobj_release(&rec->hdr); 2051c2c66affSColin Finck tables->ops->delete(tables); 2052c2c66affSColin Finck 2053c2c66affSColin Finck return r; 2054c2c66affSColin Finck } 2055c2c66affSColin Finck 2056c2c66affSColin Finck static const MSIVIEWOPS table_ops = 2057c2c66affSColin Finck { 2058c2c66affSColin Finck TABLE_fetch_int, 2059c2c66affSColin Finck TABLE_fetch_stream, 2060c2c66affSColin Finck TABLE_get_row, 2061c2c66affSColin Finck TABLE_set_row, 2062c2c66affSColin Finck TABLE_insert_row, 2063c2c66affSColin Finck TABLE_delete_row, 2064c2c66affSColin Finck TABLE_execute, 2065c2c66affSColin Finck TABLE_close, 2066c2c66affSColin Finck TABLE_get_dimensions, 2067c2c66affSColin Finck TABLE_get_column_info, 2068c2c66affSColin Finck TABLE_modify, 2069c2c66affSColin Finck TABLE_delete, 2070c2c66affSColin Finck TABLE_add_ref, 2071c2c66affSColin Finck TABLE_release, 2072c2c66affSColin Finck TABLE_add_column, 2073c2c66affSColin Finck NULL, 2074c2c66affSColin Finck TABLE_drop, 2075c2c66affSColin Finck }; 2076c2c66affSColin Finck 2077c2c66affSColin Finck UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) 2078c2c66affSColin Finck { 2079c2c66affSColin Finck MSITABLEVIEW *tv ; 2080c2c66affSColin Finck UINT r, sz; 2081c2c66affSColin Finck 2082c2c66affSColin Finck TRACE("%p %s %p\n", db, debugstr_w(name), view ); 2083c2c66affSColin Finck 2084c2c66affSColin Finck if ( !strcmpW( name, szStreams ) ) 2085c2c66affSColin Finck return STREAMS_CreateView( db, view ); 2086c2c66affSColin Finck else if ( !strcmpW( name, szStorages ) ) 2087c2c66affSColin Finck return STORAGES_CreateView( db, view ); 2088c2c66affSColin Finck 2089c2c66affSColin Finck sz = FIELD_OFFSET( MSITABLEVIEW, name[lstrlenW( name ) + 1] ); 2090c2c66affSColin Finck tv = msi_alloc_zero( sz ); 2091c2c66affSColin Finck if( !tv ) 2092c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 2093c2c66affSColin Finck 2094c2c66affSColin Finck r = get_table( db, name, &tv->table ); 2095c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2096c2c66affSColin Finck { 2097c2c66affSColin Finck msi_free( tv ); 2098c2c66affSColin Finck WARN("table not found\n"); 2099c2c66affSColin Finck return r; 2100c2c66affSColin Finck } 2101c2c66affSColin Finck 2102c2c66affSColin Finck TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count); 2103c2c66affSColin Finck 2104c2c66affSColin Finck /* fill the structure */ 2105c2c66affSColin Finck tv->view.ops = &table_ops; 2106c2c66affSColin Finck tv->db = db; 2107c2c66affSColin Finck tv->columns = tv->table->colinfo; 2108c2c66affSColin Finck tv->num_cols = tv->table->col_count; 2109c2c66affSColin Finck tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES ); 2110c2c66affSColin Finck 2111c2c66affSColin Finck TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size ); 2112c2c66affSColin Finck 2113c2c66affSColin Finck *view = (MSIVIEW*) tv; 2114c2c66affSColin Finck lstrcpyW( tv->name, name ); 2115c2c66affSColin Finck 2116c2c66affSColin Finck return ERROR_SUCCESS; 2117c2c66affSColin Finck } 2118c2c66affSColin Finck 2119c2c66affSColin Finck UINT MSI_CommitTables( MSIDATABASE *db ) 2120c2c66affSColin Finck { 2121c2c66affSColin Finck UINT r, bytes_per_strref; 2122c2c66affSColin Finck HRESULT hr; 2123c2c66affSColin Finck MSITABLE *table = NULL; 2124c2c66affSColin Finck 2125c2c66affSColin Finck TRACE("%p\n",db); 2126c2c66affSColin Finck 2127c2c66affSColin Finck r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref ); 2128c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2129c2c66affSColin Finck { 2130c2c66affSColin Finck WARN("failed to save string table r=%08x\n",r); 2131c2c66affSColin Finck return r; 2132c2c66affSColin Finck } 2133c2c66affSColin Finck 2134c2c66affSColin Finck LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry ) 2135c2c66affSColin Finck { 2136c2c66affSColin Finck r = save_table( db, table, bytes_per_strref ); 2137c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2138c2c66affSColin Finck { 2139c2c66affSColin Finck WARN("failed to save table %s (r=%08x)\n", 2140c2c66affSColin Finck debugstr_w(table->name), r); 2141c2c66affSColin Finck return r; 2142c2c66affSColin Finck } 2143c2c66affSColin Finck } 2144c2c66affSColin Finck 2145c2c66affSColin Finck hr = IStorage_Commit( db->storage, 0 ); 2146c2c66affSColin Finck if (FAILED( hr )) 2147c2c66affSColin Finck { 2148c2c66affSColin Finck WARN("failed to commit changes 0x%08x\n", hr); 2149c2c66affSColin Finck r = ERROR_FUNCTION_FAILED; 2150c2c66affSColin Finck } 2151c2c66affSColin Finck return r; 2152c2c66affSColin Finck } 2153c2c66affSColin Finck 2154c2c66affSColin Finck MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table ) 2155c2c66affSColin Finck { 2156c2c66affSColin Finck MSITABLE *t; 2157c2c66affSColin Finck UINT r; 2158c2c66affSColin Finck 2159c2c66affSColin Finck TRACE("%p %s\n", db, debugstr_w(table)); 2160c2c66affSColin Finck 2161c2c66affSColin Finck if (!table) 2162c2c66affSColin Finck return MSICONDITION_ERROR; 2163c2c66affSColin Finck 2164c2c66affSColin Finck r = get_table( db, table, &t ); 2165c2c66affSColin Finck if (r != ERROR_SUCCESS) 2166c2c66affSColin Finck return MSICONDITION_NONE; 2167c2c66affSColin Finck 2168c2c66affSColin Finck return t->persistent; 2169c2c66affSColin Finck } 2170c2c66affSColin Finck 2171c2c66affSColin Finck static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes) 2172c2c66affSColin Finck { 2173c2c66affSColin Finck UINT ret = 0, i; 2174c2c66affSColin Finck 2175c2c66affSColin Finck for (i = 0; i < bytes; i++) 2176c2c66affSColin Finck ret += (data[col + i] << i * 8); 2177c2c66affSColin Finck 2178c2c66affSColin Finck return ret; 2179c2c66affSColin Finck } 2180c2c66affSColin Finck 2181c2c66affSColin Finck static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname ) 2182c2c66affSColin Finck { 2183c2c66affSColin Finck LPWSTR stname = NULL, sval, p; 2184c2c66affSColin Finck DWORD len; 2185c2c66affSColin Finck UINT i, r; 2186c2c66affSColin Finck 2187c2c66affSColin Finck TRACE("%p %p\n", tv, rec); 2188c2c66affSColin Finck 2189c2c66affSColin Finck len = lstrlenW( tv->name ) + 1; 2190c2c66affSColin Finck stname = msi_alloc( len*sizeof(WCHAR) ); 2191c2c66affSColin Finck if ( !stname ) 2192c2c66affSColin Finck { 2193c2c66affSColin Finck r = ERROR_OUTOFMEMORY; 2194c2c66affSColin Finck goto err; 2195c2c66affSColin Finck } 2196c2c66affSColin Finck 2197c2c66affSColin Finck lstrcpyW( stname, tv->name ); 2198c2c66affSColin Finck 2199c2c66affSColin Finck for ( i = 0; i < tv->num_cols; i++ ) 2200c2c66affSColin Finck { 2201c2c66affSColin Finck if ( tv->columns[i].type & MSITYPE_KEY ) 2202c2c66affSColin Finck { 2203c2c66affSColin Finck sval = msi_dup_record_field( rec, i + 1 ); 2204c2c66affSColin Finck if ( !sval ) 2205c2c66affSColin Finck { 2206c2c66affSColin Finck r = ERROR_OUTOFMEMORY; 2207c2c66affSColin Finck goto err; 2208c2c66affSColin Finck } 2209c2c66affSColin Finck 2210c2c66affSColin Finck len += lstrlenW( szDot ) + lstrlenW ( sval ); 2211c2c66affSColin Finck p = msi_realloc ( stname, len*sizeof(WCHAR) ); 2212c2c66affSColin Finck if ( !p ) 2213c2c66affSColin Finck { 2214c2c66affSColin Finck r = ERROR_OUTOFMEMORY; 2215c2c66affSColin Finck msi_free(sval); 2216c2c66affSColin Finck goto err; 2217c2c66affSColin Finck } 2218c2c66affSColin Finck stname = p; 2219c2c66affSColin Finck 2220c2c66affSColin Finck lstrcatW( stname, szDot ); 2221c2c66affSColin Finck lstrcatW( stname, sval ); 2222c2c66affSColin Finck 2223c2c66affSColin Finck msi_free( sval ); 2224c2c66affSColin Finck } 2225c2c66affSColin Finck else 2226c2c66affSColin Finck continue; 2227c2c66affSColin Finck } 2228c2c66affSColin Finck 2229c2c66affSColin Finck *pstname = encode_streamname( FALSE, stname ); 2230c2c66affSColin Finck msi_free( stname ); 2231c2c66affSColin Finck 2232c2c66affSColin Finck return ERROR_SUCCESS; 2233c2c66affSColin Finck 2234c2c66affSColin Finck err: 2235c2c66affSColin Finck msi_free ( stname ); 2236c2c66affSColin Finck *pstname = NULL; 2237c2c66affSColin Finck return r; 2238c2c66affSColin Finck } 2239c2c66affSColin Finck 2240c2c66affSColin Finck static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st, 2241c2c66affSColin Finck IStorage *stg, const BYTE *rawdata, UINT bytes_per_strref ) 2242c2c66affSColin Finck { 2243c2c66affSColin Finck UINT i, val, ofs = 0; 2244c2c66affSColin Finck USHORT mask; 2245c2c66affSColin Finck MSICOLUMNINFO *columns = tv->columns; 2246c2c66affSColin Finck MSIRECORD *rec; 2247c2c66affSColin Finck 2248c2c66affSColin Finck mask = rawdata[0] | (rawdata[1] << 8); 2249c2c66affSColin Finck rawdata += 2; 2250c2c66affSColin Finck 2251c2c66affSColin Finck rec = MSI_CreateRecord( tv->num_cols ); 2252c2c66affSColin Finck if( !rec ) 2253c2c66affSColin Finck return rec; 2254c2c66affSColin Finck 2255c2c66affSColin Finck TRACE("row ->\n"); 2256c2c66affSColin Finck for( i=0; i<tv->num_cols; i++ ) 2257c2c66affSColin Finck { 2258c2c66affSColin Finck if ( (mask&1) && (i>=(mask>>8)) ) 2259c2c66affSColin Finck break; 2260c2c66affSColin Finck /* all keys must be present */ 2261c2c66affSColin Finck if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) ) 2262c2c66affSColin Finck continue; 2263c2c66affSColin Finck 2264c2c66affSColin Finck if( MSITYPE_IS_BINARY(tv->columns[i].type) ) 2265c2c66affSColin Finck { 2266c2c66affSColin Finck LPWSTR encname; 2267c2c66affSColin Finck IStream *stm = NULL; 2268c2c66affSColin Finck UINT r; 2269c2c66affSColin Finck 2270c2c66affSColin Finck ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref ); 2271c2c66affSColin Finck 2272c2c66affSColin Finck r = msi_record_encoded_stream_name( tv, rec, &encname ); 2273c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 2274c2c66affSColin Finck { 2275c2c66affSColin Finck msiobj_release( &rec->hdr ); 2276c2c66affSColin Finck return NULL; 2277c2c66affSColin Finck } 2278c2c66affSColin Finck r = IStorage_OpenStream( stg, encname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); 2279c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 2280c2c66affSColin Finck { 2281c2c66affSColin Finck msiobj_release( &rec->hdr ); 2282c2c66affSColin Finck msi_free( encname ); 2283c2c66affSColin Finck return NULL; 2284c2c66affSColin Finck } 2285c2c66affSColin Finck 2286c2c66affSColin Finck MSI_RecordSetStream( rec, i+1, stm ); 2287c2c66affSColin Finck TRACE(" field %d [%s]\n", i+1, debugstr_w(encname)); 2288c2c66affSColin Finck msi_free( encname ); 2289c2c66affSColin Finck } 2290c2c66affSColin Finck else if( columns[i].type & MSITYPE_STRING ) 2291c2c66affSColin Finck { 2292c2c66affSColin Finck int len; 2293c2c66affSColin Finck const WCHAR *sval; 2294c2c66affSColin Finck 2295c2c66affSColin Finck val = read_raw_int(rawdata, ofs, bytes_per_strref); 2296c2c66affSColin Finck sval = msi_string_lookup( st, val, &len ); 2297c2c66affSColin Finck msi_record_set_string( rec, i+1, sval, len ); 2298c2c66affSColin Finck TRACE(" field %d [%s]\n", i+1, debugstr_wn(sval, len)); 2299c2c66affSColin Finck ofs += bytes_per_strref; 2300c2c66affSColin Finck } 2301c2c66affSColin Finck else 2302c2c66affSColin Finck { 2303c2c66affSColin Finck UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref ); 2304c2c66affSColin Finck switch( n ) 2305c2c66affSColin Finck { 2306c2c66affSColin Finck case 2: 2307c2c66affSColin Finck val = read_raw_int(rawdata, ofs, n); 2308c2c66affSColin Finck if (val) 2309c2c66affSColin Finck MSI_RecordSetInteger( rec, i+1, val-0x8000 ); 2310c2c66affSColin Finck TRACE(" field %d [0x%04x]\n", i+1, val ); 2311c2c66affSColin Finck break; 2312c2c66affSColin Finck case 4: 2313c2c66affSColin Finck val = read_raw_int(rawdata, ofs, n); 2314c2c66affSColin Finck if (val) 2315c2c66affSColin Finck MSI_RecordSetInteger( rec, i+1, val^0x80000000 ); 2316c2c66affSColin Finck TRACE(" field %d [0x%08x]\n", i+1, val ); 2317c2c66affSColin Finck break; 2318c2c66affSColin Finck default: 2319c2c66affSColin Finck ERR("oops - unknown column width %d\n", n); 2320c2c66affSColin Finck break; 2321c2c66affSColin Finck } 2322c2c66affSColin Finck ofs += n; 2323c2c66affSColin Finck } 2324c2c66affSColin Finck } 2325c2c66affSColin Finck return rec; 2326c2c66affSColin Finck } 2327c2c66affSColin Finck 2328c2c66affSColin Finck static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize ) 2329c2c66affSColin Finck { 2330c2c66affSColin Finck UINT i; 2331c2c66affSColin Finck for (i = 0; i < rawsize / 2; i++) 2332c2c66affSColin Finck { 2333c2c66affSColin Finck int len; 2334c2c66affSColin Finck const WCHAR *sval = msi_string_lookup( st, rawdata[i], &len ); 2335c2c66affSColin Finck MESSAGE(" %04x %s\n", rawdata[i], debugstr_wn(sval, len) ); 2336c2c66affSColin Finck } 2337c2c66affSColin Finck } 2338c2c66affSColin Finck 2339c2c66affSColin Finck static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec ) 2340c2c66affSColin Finck { 2341c2c66affSColin Finck UINT i, r, *data; 2342c2c66affSColin Finck 2343c2c66affSColin Finck data = msi_alloc( tv->num_cols *sizeof (UINT) ); 2344c2c66affSColin Finck for( i=0; i<tv->num_cols; i++ ) 2345c2c66affSColin Finck { 2346c2c66affSColin Finck data[i] = 0; 2347c2c66affSColin Finck 2348c2c66affSColin Finck if ( ~tv->columns[i].type & MSITYPE_KEY ) 2349c2c66affSColin Finck continue; 2350c2c66affSColin Finck 2351c2c66affSColin Finck /* turn the transform column value into a row value */ 2352c2c66affSColin Finck if ( ( tv->columns[i].type & MSITYPE_STRING ) && 2353c2c66affSColin Finck ! MSITYPE_IS_BINARY(tv->columns[i].type) ) 2354c2c66affSColin Finck { 2355c2c66affSColin Finck int len; 2356c2c66affSColin Finck const WCHAR *str = msi_record_get_string( rec, i+1, &len ); 2357c2c66affSColin Finck if (str) 2358c2c66affSColin Finck { 2359c2c66affSColin Finck r = msi_string2id( tv->db->strings, str, len, &data[i] ); 2360c2c66affSColin Finck 2361c2c66affSColin Finck /* if there's no matching string in the string table, 2362c2c66affSColin Finck these keys can't match any record, so fail now. */ 2363c2c66affSColin Finck if (r != ERROR_SUCCESS) 2364c2c66affSColin Finck { 2365c2c66affSColin Finck msi_free( data ); 2366c2c66affSColin Finck return NULL; 2367c2c66affSColin Finck } 2368c2c66affSColin Finck } 2369c2c66affSColin Finck else data[i] = 0; 2370c2c66affSColin Finck } 2371c2c66affSColin Finck else 2372c2c66affSColin Finck { 2373c2c66affSColin Finck data[i] = MSI_RecordGetInteger( rec, i+1 ); 2374c2c66affSColin Finck 2375c2c66affSColin Finck if (data[i] == MSI_NULL_INTEGER) 2376c2c66affSColin Finck data[i] = 0; 2377c2c66affSColin Finck else if ((tv->columns[i].type&0xff) == 2) 2378c2c66affSColin Finck data[i] += 0x8000; 2379c2c66affSColin Finck else 2380c2c66affSColin Finck data[i] += 0x80000000; 2381c2c66affSColin Finck } 2382c2c66affSColin Finck } 2383c2c66affSColin Finck return data; 2384c2c66affSColin Finck } 2385c2c66affSColin Finck 2386c2c66affSColin Finck static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column ) 2387c2c66affSColin Finck { 2388c2c66affSColin Finck UINT i, r, x, ret = ERROR_FUNCTION_FAILED; 2389c2c66affSColin Finck 2390c2c66affSColin Finck for( i=0; i<tv->num_cols; i++ ) 2391c2c66affSColin Finck { 2392c2c66affSColin Finck if ( ~tv->columns[i].type & MSITYPE_KEY ) 2393c2c66affSColin Finck continue; 2394c2c66affSColin Finck 2395c2c66affSColin Finck /* turn the transform column value into a row value */ 2396c2c66affSColin Finck r = TABLE_fetch_int( &tv->view, row, i+1, &x ); 2397c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 2398c2c66affSColin Finck { 2399c2c66affSColin Finck ERR("TABLE_fetch_int shouldn't fail here\n"); 2400c2c66affSColin Finck break; 2401c2c66affSColin Finck } 2402c2c66affSColin Finck 2403c2c66affSColin Finck /* if this key matches, move to the next column */ 2404c2c66affSColin Finck if ( x != data[i] ) 2405c2c66affSColin Finck { 2406c2c66affSColin Finck ret = ERROR_FUNCTION_FAILED; 2407c2c66affSColin Finck break; 2408c2c66affSColin Finck } 2409c2c66affSColin Finck if (column) *column = i; 2410c2c66affSColin Finck ret = ERROR_SUCCESS; 2411c2c66affSColin Finck } 2412c2c66affSColin Finck return ret; 2413c2c66affSColin Finck } 2414c2c66affSColin Finck 2415c2c66affSColin Finck static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column ) 2416c2c66affSColin Finck { 2417c2c66affSColin Finck UINT i, r = ERROR_FUNCTION_FAILED, *data; 2418c2c66affSColin Finck 2419c2c66affSColin Finck data = msi_record_to_row( tv, rec ); 2420c2c66affSColin Finck if( !data ) 2421c2c66affSColin Finck return r; 2422c2c66affSColin Finck for( i = 0; i < tv->table->row_count; i++ ) 2423c2c66affSColin Finck { 2424c2c66affSColin Finck r = msi_row_matches( tv, i, data, column ); 2425c2c66affSColin Finck if( r == ERROR_SUCCESS ) 2426c2c66affSColin Finck { 2427c2c66affSColin Finck *row = i; 2428c2c66affSColin Finck break; 2429c2c66affSColin Finck } 2430c2c66affSColin Finck } 2431c2c66affSColin Finck msi_free( data ); 2432c2c66affSColin Finck return r; 2433c2c66affSColin Finck } 2434c2c66affSColin Finck 2435c2c66affSColin Finck typedef struct 2436c2c66affSColin Finck { 2437c2c66affSColin Finck struct list entry; 2438c2c66affSColin Finck LPWSTR name; 2439c2c66affSColin Finck } TRANSFORMDATA; 2440c2c66affSColin Finck 2441c2c66affSColin Finck static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, 2442c2c66affSColin Finck string_table *st, TRANSFORMDATA *transform, 2443c2c66affSColin Finck UINT bytes_per_strref ) 2444c2c66affSColin Finck { 2445c2c66affSColin Finck BYTE *rawdata = NULL; 2446c2c66affSColin Finck MSITABLEVIEW *tv = NULL; 2447c2c66affSColin Finck UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0; 2448c2c66affSColin Finck MSIRECORD *rec = NULL; 2449c2c66affSColin Finck WCHAR coltable[32]; 2450c2c66affSColin Finck const WCHAR *name; 2451c2c66affSColin Finck 2452c2c66affSColin Finck if (!transform) 2453c2c66affSColin Finck return ERROR_SUCCESS; 2454c2c66affSColin Finck 2455c2c66affSColin Finck name = transform->name; 2456c2c66affSColin Finck 2457c2c66affSColin Finck coltable[0] = 0; 2458c2c66affSColin Finck TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) ); 2459c2c66affSColin Finck 2460c2c66affSColin Finck /* read the transform data */ 2461c2c66affSColin Finck read_stream_data( stg, name, TRUE, &rawdata, &rawsize ); 2462c2c66affSColin Finck if ( !rawdata ) 2463c2c66affSColin Finck { 2464c2c66affSColin Finck TRACE("table %s empty\n", debugstr_w(name) ); 2465c2c66affSColin Finck return ERROR_INVALID_TABLE; 2466c2c66affSColin Finck } 2467c2c66affSColin Finck 2468c2c66affSColin Finck /* create a table view */ 2469c2c66affSColin Finck r = TABLE_CreateView( db, name, (MSIVIEW**) &tv ); 2470c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2471c2c66affSColin Finck goto err; 2472c2c66affSColin Finck 2473c2c66affSColin Finck r = tv->view.ops->execute( &tv->view, NULL ); 2474c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2475c2c66affSColin Finck goto err; 2476c2c66affSColin Finck 2477c2c66affSColin Finck TRACE("name = %s columns = %u row_size = %u raw size = %u\n", 2478c2c66affSColin Finck debugstr_w(name), tv->num_cols, tv->row_size, rawsize ); 2479c2c66affSColin Finck 2480c2c66affSColin Finck /* interpret the data */ 2481c2c66affSColin Finck for (n = 0; n < rawsize;) 2482c2c66affSColin Finck { 2483c2c66affSColin Finck mask = rawdata[n] | (rawdata[n + 1] << 8); 2484c2c66affSColin Finck if (mask & 1) 2485c2c66affSColin Finck { 2486c2c66affSColin Finck /* 2487c2c66affSColin Finck * if the low bit is set, columns are continuous and 2488c2c66affSColin Finck * the number of columns is specified in the high byte 2489c2c66affSColin Finck */ 2490c2c66affSColin Finck sz = 2; 2491c2c66affSColin Finck num_cols = mask >> 8; 2492c2c66affSColin Finck if (num_cols > tv->num_cols) 2493c2c66affSColin Finck { 2494c2c66affSColin Finck ERR("excess columns in transform: %u > %u\n", num_cols, tv->num_cols); 2495c2c66affSColin Finck break; 2496c2c66affSColin Finck } 2497c2c66affSColin Finck 2498c2c66affSColin Finck for (i = 0; i < num_cols; i++) 2499c2c66affSColin Finck { 2500c2c66affSColin Finck if( (tv->columns[i].type & MSITYPE_STRING) && 2501c2c66affSColin Finck ! MSITYPE_IS_BINARY(tv->columns[i].type) ) 2502c2c66affSColin Finck sz += bytes_per_strref; 2503c2c66affSColin Finck else 2504c2c66affSColin Finck sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); 2505c2c66affSColin Finck } 2506c2c66affSColin Finck } 2507c2c66affSColin Finck else 2508c2c66affSColin Finck { 2509c2c66affSColin Finck /* 2510c2c66affSColin Finck * If the low bit is not set, mask is a bitmask. 2511c2c66affSColin Finck * Excepting for key fields, which are always present, 2512c2c66affSColin Finck * each bit indicates that a field is present in the transform record. 2513c2c66affSColin Finck * 2514c2c66affSColin Finck * mask == 0 is a special case ... only the keys will be present 2515c2c66affSColin Finck * and it means that this row should be deleted. 2516c2c66affSColin Finck */ 2517c2c66affSColin Finck sz = 2; 2518c2c66affSColin Finck num_cols = tv->num_cols; 2519c2c66affSColin Finck for (i = 0; i < num_cols; i++) 2520c2c66affSColin Finck { 2521c2c66affSColin Finck if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask)) 2522c2c66affSColin Finck { 2523c2c66affSColin Finck if ((tv->columns[i].type & MSITYPE_STRING) && 2524c2c66affSColin Finck !MSITYPE_IS_BINARY(tv->columns[i].type)) 2525c2c66affSColin Finck sz += bytes_per_strref; 2526c2c66affSColin Finck else 2527c2c66affSColin Finck sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); 2528c2c66affSColin Finck } 2529c2c66affSColin Finck } 2530c2c66affSColin Finck } 2531c2c66affSColin Finck 2532c2c66affSColin Finck /* check we didn't run of the end of the table */ 2533c2c66affSColin Finck if (n + sz > rawsize) 2534c2c66affSColin Finck { 2535c2c66affSColin Finck ERR("borked.\n"); 2536c2c66affSColin Finck dump_table( st, (USHORT *)rawdata, rawsize ); 2537c2c66affSColin Finck break; 2538c2c66affSColin Finck } 2539c2c66affSColin Finck 2540c2c66affSColin Finck rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref ); 2541c2c66affSColin Finck if (rec) 2542c2c66affSColin Finck { 2543c2c66affSColin Finck WCHAR table[32]; 2544c2c66affSColin Finck DWORD sz = 32; 2545c2c66affSColin Finck UINT number = MSI_NULL_INTEGER; 2546c2c66affSColin Finck UINT row = 0; 2547c2c66affSColin Finck 2548c2c66affSColin Finck if (!strcmpW( name, szColumns )) 2549c2c66affSColin Finck { 2550c2c66affSColin Finck MSI_RecordGetStringW( rec, 1, table, &sz ); 2551c2c66affSColin Finck number = MSI_RecordGetInteger( rec, 2 ); 2552c2c66affSColin Finck 2553c2c66affSColin Finck /* 2554c2c66affSColin Finck * Native msi seems writes nul into the Number (2nd) column of 2555c2c66affSColin Finck * the _Columns table when there are new columns 2556c2c66affSColin Finck */ 2557c2c66affSColin Finck if ( number == MSI_NULL_INTEGER ) 2558c2c66affSColin Finck { 2559c2c66affSColin Finck /* reset the column number on a new table */ 2560c2c66affSColin Finck if (strcmpW( coltable, table )) 2561c2c66affSColin Finck { 2562c2c66affSColin Finck colcol = 0; 2563c2c66affSColin Finck lstrcpyW( coltable, table ); 2564c2c66affSColin Finck } 2565c2c66affSColin Finck 2566c2c66affSColin Finck /* fix nul column numbers */ 2567c2c66affSColin Finck MSI_RecordSetInteger( rec, 2, ++colcol ); 2568c2c66affSColin Finck } 2569c2c66affSColin Finck } 2570c2c66affSColin Finck 2571c2c66affSColin Finck if (TRACE_ON(msidb)) dump_record( rec ); 2572c2c66affSColin Finck 2573c2c66affSColin Finck r = msi_table_find_row( tv, rec, &row, NULL ); 2574c2c66affSColin Finck if (r == ERROR_SUCCESS) 2575c2c66affSColin Finck { 2576c2c66affSColin Finck if (!mask) 2577c2c66affSColin Finck { 2578c2c66affSColin Finck TRACE("deleting row [%d]:\n", row); 2579c2c66affSColin Finck r = TABLE_delete_row( &tv->view, row ); 2580c2c66affSColin Finck if (r != ERROR_SUCCESS) 2581c2c66affSColin Finck WARN("failed to delete row %u\n", r); 2582c2c66affSColin Finck } 2583c2c66affSColin Finck else if (mask & 1) 2584c2c66affSColin Finck { 2585c2c66affSColin Finck TRACE("modifying full row [%d]:\n", row); 2586c2c66affSColin Finck r = TABLE_set_row( &tv->view, row, rec, (1 << tv->num_cols) - 1 ); 2587c2c66affSColin Finck if (r != ERROR_SUCCESS) 2588c2c66affSColin Finck WARN("failed to modify row %u\n", r); 2589c2c66affSColin Finck } 2590c2c66affSColin Finck else 2591c2c66affSColin Finck { 2592c2c66affSColin Finck TRACE("modifying masked row [%d]:\n", row); 2593c2c66affSColin Finck r = TABLE_set_row( &tv->view, row, rec, mask ); 2594c2c66affSColin Finck if (r != ERROR_SUCCESS) 2595c2c66affSColin Finck WARN("failed to modify row %u\n", r); 2596c2c66affSColin Finck } 2597c2c66affSColin Finck } 2598c2c66affSColin Finck else 2599c2c66affSColin Finck { 2600c2c66affSColin Finck TRACE("inserting row\n"); 2601c2c66affSColin Finck r = TABLE_insert_row( &tv->view, rec, -1, FALSE ); 2602c2c66affSColin Finck if (r != ERROR_SUCCESS) 2603c2c66affSColin Finck WARN("failed to insert row %u\n", r); 2604c2c66affSColin Finck } 2605c2c66affSColin Finck 2606c2c66affSColin Finck if (!strcmpW( name, szColumns )) 2607c2c66affSColin Finck msi_update_table_columns( db, table ); 2608c2c66affSColin Finck 2609c2c66affSColin Finck msiobj_release( &rec->hdr ); 2610c2c66affSColin Finck } 2611c2c66affSColin Finck 2612c2c66affSColin Finck n += sz; 2613c2c66affSColin Finck } 2614c2c66affSColin Finck 2615c2c66affSColin Finck err: 2616c2c66affSColin Finck /* no need to free the table, it's associated with the database */ 2617c2c66affSColin Finck msi_free( rawdata ); 2618c2c66affSColin Finck if( tv ) 2619c2c66affSColin Finck tv->view.ops->delete( &tv->view ); 2620c2c66affSColin Finck 2621c2c66affSColin Finck return ERROR_SUCCESS; 2622c2c66affSColin Finck } 2623c2c66affSColin Finck 2624c2c66affSColin Finck /* 2625c2c66affSColin Finck * msi_table_apply_transform 2626c2c66affSColin Finck * 2627c2c66affSColin Finck * Enumerate the table transforms in a transform storage and apply each one. 2628c2c66affSColin Finck */ 2629c2c66affSColin Finck UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) 2630c2c66affSColin Finck { 2631c2c66affSColin Finck struct list transforms; 2632c2c66affSColin Finck IEnumSTATSTG *stgenum = NULL; 2633c2c66affSColin Finck TRANSFORMDATA *transform; 2634c2c66affSColin Finck TRANSFORMDATA *tables = NULL, *columns = NULL; 2635c2c66affSColin Finck HRESULT hr; 2636c2c66affSColin Finck STATSTG stat; 2637c2c66affSColin Finck string_table *strings; 2638c2c66affSColin Finck UINT ret = ERROR_FUNCTION_FAILED; 2639c2c66affSColin Finck UINT bytes_per_strref; 2640c2c66affSColin Finck BOOL property_update = FALSE; 2641c2c66affSColin Finck 2642c2c66affSColin Finck TRACE("%p %p\n", db, stg ); 2643c2c66affSColin Finck 2644c2c66affSColin Finck strings = msi_load_string_table( stg, &bytes_per_strref ); 2645c2c66affSColin Finck if( !strings ) 2646c2c66affSColin Finck goto end; 2647c2c66affSColin Finck 2648c2c66affSColin Finck hr = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); 2649c2c66affSColin Finck if (FAILED( hr )) 2650c2c66affSColin Finck goto end; 2651c2c66affSColin Finck 2652c2c66affSColin Finck list_init(&transforms); 2653c2c66affSColin Finck 2654c2c66affSColin Finck while ( TRUE ) 2655c2c66affSColin Finck { 2656c2c66affSColin Finck MSITABLEVIEW *tv = NULL; 2657c2c66affSColin Finck WCHAR name[0x40]; 2658c2c66affSColin Finck ULONG count = 0; 2659c2c66affSColin Finck 2660c2c66affSColin Finck hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); 2661c2c66affSColin Finck if (FAILED( hr ) || !count) 2662c2c66affSColin Finck break; 2663c2c66affSColin Finck 2664c2c66affSColin Finck decode_streamname( stat.pwcsName, name ); 2665c2c66affSColin Finck CoTaskMemFree( stat.pwcsName ); 2666c2c66affSColin Finck if ( name[0] != 0x4840 ) 2667c2c66affSColin Finck continue; 2668c2c66affSColin Finck 2669c2c66affSColin Finck if ( !strcmpW( name+1, szStringPool ) || 2670c2c66affSColin Finck !strcmpW( name+1, szStringData ) ) 2671c2c66affSColin Finck continue; 2672c2c66affSColin Finck 2673c2c66affSColin Finck transform = msi_alloc_zero( sizeof(TRANSFORMDATA) ); 2674c2c66affSColin Finck if ( !transform ) 2675c2c66affSColin Finck break; 2676c2c66affSColin Finck 2677c2c66affSColin Finck list_add_tail( &transforms, &transform->entry ); 2678c2c66affSColin Finck 2679c2c66affSColin Finck transform->name = strdupW( name + 1 ); 2680c2c66affSColin Finck 2681c2c66affSColin Finck if ( !strcmpW( transform->name, szTables ) ) 2682c2c66affSColin Finck tables = transform; 2683c2c66affSColin Finck else if (!strcmpW( transform->name, szColumns ) ) 2684c2c66affSColin Finck columns = transform; 2685c2c66affSColin Finck else if (!strcmpW( transform->name, szProperty )) 2686c2c66affSColin Finck property_update = TRUE; 2687c2c66affSColin Finck 2688c2c66affSColin Finck TRACE("transform contains stream %s\n", debugstr_w(name)); 2689c2c66affSColin Finck 2690c2c66affSColin Finck /* load the table */ 2691c2c66affSColin Finck if (TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv ) != ERROR_SUCCESS) 2692c2c66affSColin Finck continue; 2693c2c66affSColin Finck 2694c2c66affSColin Finck if (tv->view.ops->execute( &tv->view, NULL ) != ERROR_SUCCESS) 2695c2c66affSColin Finck { 2696c2c66affSColin Finck tv->view.ops->delete( &tv->view ); 2697c2c66affSColin Finck continue; 2698c2c66affSColin Finck } 2699c2c66affSColin Finck 2700c2c66affSColin Finck tv->view.ops->delete( &tv->view ); 2701c2c66affSColin Finck } 2702c2c66affSColin Finck 2703c2c66affSColin Finck /* 2704c2c66affSColin Finck * Apply _Tables and _Columns transforms first so that 2705c2c66affSColin Finck * the table metadata is correct, and empty tables exist. 2706c2c66affSColin Finck */ 2707c2c66affSColin Finck ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref ); 2708c2c66affSColin Finck if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) 2709c2c66affSColin Finck goto end; 2710c2c66affSColin Finck 2711c2c66affSColin Finck ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref ); 2712c2c66affSColin Finck if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) 2713c2c66affSColin Finck goto end; 2714c2c66affSColin Finck 2715c2c66affSColin Finck ret = ERROR_SUCCESS; 2716c2c66affSColin Finck 2717c2c66affSColin Finck while ( !list_empty( &transforms ) ) 2718c2c66affSColin Finck { 2719c2c66affSColin Finck transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry ); 2720c2c66affSColin Finck 2721c2c66affSColin Finck if ( strcmpW( transform->name, szColumns ) && 2722c2c66affSColin Finck strcmpW( transform->name, szTables ) && 2723c2c66affSColin Finck ret == ERROR_SUCCESS ) 2724c2c66affSColin Finck { 2725c2c66affSColin Finck ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref ); 2726c2c66affSColin Finck } 2727c2c66affSColin Finck 2728c2c66affSColin Finck list_remove( &transform->entry ); 2729c2c66affSColin Finck msi_free( transform->name ); 2730c2c66affSColin Finck msi_free( transform ); 2731c2c66affSColin Finck } 2732c2c66affSColin Finck 2733c2c66affSColin Finck if ( ret == ERROR_SUCCESS ) 2734c2c66affSColin Finck { 2735c2c66affSColin Finck append_storage_to_db( db, stg ); 2736c2c66affSColin Finck if (property_update) msi_clone_properties( db ); 2737c2c66affSColin Finck } 2738c2c66affSColin Finck 2739c2c66affSColin Finck end: 2740c2c66affSColin Finck if ( stgenum ) 2741c2c66affSColin Finck IEnumSTATSTG_Release( stgenum ); 2742c2c66affSColin Finck if ( strings ) 2743c2c66affSColin Finck msi_destroy_stringtable( strings ); 2744c2c66affSColin Finck 2745c2c66affSColin Finck return ret; 2746c2c66affSColin Finck } 2747