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" 38c2c66affSColin Finck 39c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL(msidb); 40c2c66affSColin Finck 41c2c66affSColin Finck #define MSITABLE_HASH_TABLE_SIZE 37 42c2c66affSColin Finck 43c2c66affSColin Finck typedef struct tagMSICOLUMNHASHENTRY 44c2c66affSColin Finck { 45c2c66affSColin Finck struct tagMSICOLUMNHASHENTRY *next; 46c2c66affSColin Finck UINT value; 47c2c66affSColin Finck UINT row; 48c2c66affSColin Finck } MSICOLUMNHASHENTRY; 49c2c66affSColin Finck 50c2c66affSColin Finck typedef struct tagMSICOLUMNINFO 51c2c66affSColin Finck { 52c2c66affSColin Finck LPCWSTR tablename; 53c2c66affSColin Finck UINT number; 54c2c66affSColin Finck LPCWSTR colname; 55c2c66affSColin Finck UINT type; 56c2c66affSColin Finck UINT offset; 57c2c66affSColin Finck BOOL temporary; 58c2c66affSColin Finck MSICOLUMNHASHENTRY **hash_table; 59c2c66affSColin Finck } MSICOLUMNINFO; 60c2c66affSColin Finck 61c2c66affSColin Finck struct tagMSITABLE 62c2c66affSColin Finck { 63c2c66affSColin Finck BYTE **data; 64c2c66affSColin Finck BOOL *data_persistent; 65c2c66affSColin Finck UINT row_count; 66c2c66affSColin Finck struct list entry; 67c2c66affSColin Finck MSICOLUMNINFO *colinfo; 68c2c66affSColin Finck UINT col_count; 69c2c66affSColin Finck MSICONDITION persistent; 70c2c66affSColin Finck INT ref_count; 71c2c66affSColin Finck WCHAR name[1]; 72c2c66affSColin Finck }; 73c2c66affSColin Finck 74c2c66affSColin Finck /* information for default tables */ 75c2c66affSColin Finck static const WCHAR szTables[] = {'_','T','a','b','l','e','s',0}; 76c2c66affSColin Finck static const WCHAR szTable[] = {'T','a','b','l','e',0}; 77c2c66affSColin Finck static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0}; 78c2c66affSColin Finck static const WCHAR szNumber[] = {'N','u','m','b','e','r',0}; 79c2c66affSColin Finck static const WCHAR szType[] = {'T','y','p','e',0}; 80c2c66affSColin Finck 81c2c66affSColin Finck static const MSICOLUMNINFO _Columns_cols[4] = { 82eb6788c9Swinesync { szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, NULL }, 83eb6788c9Swinesync { szColumns, 2, szNumber, MSITYPE_VALID | MSITYPE_KEY | 2, 2, 0, NULL }, 84eb6788c9Swinesync { szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4, 0, NULL }, 85eb6788c9Swinesync { szColumns, 4, szType, MSITYPE_VALID | 2, 6, 0, NULL }, 86c2c66affSColin Finck }; 87c2c66affSColin Finck 88c2c66affSColin Finck static const MSICOLUMNINFO _Tables_cols[1] = { 89eb6788c9Swinesync { szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, NULL }, 90c2c66affSColin Finck }; 91c2c66affSColin Finck 92c2c66affSColin Finck #define MAX_STREAM_NAME 0x1f 93c2c66affSColin Finck 94c2c66affSColin Finck static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref ) 95c2c66affSColin Finck { 96c2c66affSColin Finck if( MSITYPE_IS_BINARY(col->type) ) 97c2c66affSColin Finck return 2; 98c2c66affSColin Finck 99c2c66affSColin Finck if( col->type & MSITYPE_STRING ) 100c2c66affSColin Finck return bytes_per_strref; 101c2c66affSColin Finck 102c2c66affSColin Finck if( (col->type & 0xff) <= 2) 103c2c66affSColin Finck return 2; 104c2c66affSColin Finck 105c2c66affSColin Finck if( (col->type & 0xff) != 4 ) 106c2c66affSColin Finck ERR("Invalid column size %u\n", col->type & 0xff); 107c2c66affSColin Finck 108c2c66affSColin Finck return 4; 109c2c66affSColin Finck } 110c2c66affSColin Finck 111c2c66affSColin Finck static int utf2mime(int x) 112c2c66affSColin Finck { 113c2c66affSColin Finck if( (x>='0') && (x<='9') ) 114c2c66affSColin Finck return x-'0'; 115c2c66affSColin Finck if( (x>='A') && (x<='Z') ) 116c2c66affSColin Finck return x-'A'+10; 117c2c66affSColin Finck if( (x>='a') && (x<='z') ) 118c2c66affSColin Finck return x-'a'+10+26; 119c2c66affSColin Finck if( x=='.' ) 120c2c66affSColin Finck return 10+26+26; 121c2c66affSColin Finck if( x=='_' ) 122c2c66affSColin Finck return 10+26+26+1; 123c2c66affSColin Finck return -1; 124c2c66affSColin Finck } 125c2c66affSColin Finck 126c2c66affSColin Finck LPWSTR encode_streamname(BOOL bTable, LPCWSTR in) 127c2c66affSColin Finck { 128c2c66affSColin Finck DWORD count = MAX_STREAM_NAME; 129c2c66affSColin Finck DWORD ch, next; 130c2c66affSColin Finck LPWSTR out, p; 131c2c66affSColin Finck 132c2c66affSColin Finck if( !bTable ) 133c2c66affSColin Finck count = lstrlenW( in )+2; 134c2c66affSColin Finck if (!(out = msi_alloc( count*sizeof(WCHAR) ))) return NULL; 135c2c66affSColin Finck p = out; 136c2c66affSColin Finck 137c2c66affSColin Finck if( bTable ) 138c2c66affSColin Finck { 139c2c66affSColin Finck *p++ = 0x4840; 140c2c66affSColin Finck count --; 141c2c66affSColin Finck } 142c2c66affSColin Finck while( count -- ) 143c2c66affSColin Finck { 144c2c66affSColin Finck ch = *in++; 145c2c66affSColin Finck if( !ch ) 146c2c66affSColin Finck { 147c2c66affSColin Finck *p = ch; 148c2c66affSColin Finck return out; 149c2c66affSColin Finck } 150c2c66affSColin Finck if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) ) 151c2c66affSColin Finck { 152c2c66affSColin Finck ch = utf2mime(ch) + 0x4800; 153c2c66affSColin Finck next = *in; 154c2c66affSColin Finck if( next && (next<0x80) ) 155c2c66affSColin Finck { 156c2c66affSColin Finck next = utf2mime(next); 157c2c66affSColin Finck if( next != -1 ) 158c2c66affSColin Finck { 159c2c66affSColin Finck next += 0x3ffffc0; 160c2c66affSColin Finck ch += (next<<6); 161c2c66affSColin Finck in++; 162c2c66affSColin Finck } 163c2c66affSColin Finck } 164c2c66affSColin Finck } 165c2c66affSColin Finck *p++ = ch; 166c2c66affSColin Finck } 167c2c66affSColin Finck ERR("Failed to encode stream name (%s)\n",debugstr_w(in)); 168c2c66affSColin Finck msi_free( out ); 169c2c66affSColin Finck return NULL; 170c2c66affSColin Finck } 171c2c66affSColin Finck 172c2c66affSColin Finck static int mime2utf(int x) 173c2c66affSColin Finck { 174c2c66affSColin Finck if( x<10 ) 175c2c66affSColin Finck return x + '0'; 176c2c66affSColin Finck if( x<(10+26)) 177c2c66affSColin Finck return x - 10 + 'A'; 178c2c66affSColin Finck if( x<(10+26+26)) 179c2c66affSColin Finck return x - 10 - 26 + 'a'; 180c2c66affSColin Finck if( x == (10+26+26) ) 181c2c66affSColin Finck return '.'; 182c2c66affSColin Finck return '_'; 183c2c66affSColin Finck } 184c2c66affSColin Finck 185c2c66affSColin Finck BOOL decode_streamname(LPCWSTR in, LPWSTR out) 186c2c66affSColin Finck { 187c2c66affSColin Finck WCHAR ch; 188c2c66affSColin Finck DWORD count = 0; 189c2c66affSColin Finck 190c2c66affSColin Finck while ( (ch = *in++) ) 191c2c66affSColin Finck { 192c2c66affSColin Finck if( (ch >= 0x3800 ) && (ch < 0x4840 ) ) 193c2c66affSColin Finck { 194c2c66affSColin Finck if( ch >= 0x4800 ) 195c2c66affSColin Finck ch = mime2utf(ch-0x4800); 196c2c66affSColin Finck else 197c2c66affSColin Finck { 198c2c66affSColin Finck ch -= 0x3800; 199c2c66affSColin Finck *out++ = mime2utf(ch&0x3f); 200c2c66affSColin Finck count++; 201c2c66affSColin Finck ch = mime2utf((ch>>6)&0x3f); 202c2c66affSColin Finck } 203c2c66affSColin Finck } 204c2c66affSColin Finck *out++ = ch; 205c2c66affSColin Finck count++; 206c2c66affSColin Finck } 207c2c66affSColin Finck *out = 0; 208c2c66affSColin Finck return count; 209c2c66affSColin Finck } 210c2c66affSColin Finck 211c2c66affSColin Finck void enum_stream_names( IStorage *stg ) 212c2c66affSColin Finck { 213c2c66affSColin Finck IEnumSTATSTG *stgenum = NULL; 214c2c66affSColin Finck HRESULT r; 215c2c66affSColin Finck STATSTG stat; 216c2c66affSColin Finck ULONG n, count; 217c2c66affSColin Finck WCHAR name[0x40]; 218c2c66affSColin Finck 219c2c66affSColin Finck r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); 220c2c66affSColin Finck if( FAILED( r ) ) 221c2c66affSColin Finck return; 222c2c66affSColin Finck 223c2c66affSColin Finck n = 0; 224c2c66affSColin Finck while( 1 ) 225c2c66affSColin Finck { 226c2c66affSColin Finck count = 0; 227c2c66affSColin Finck r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); 228c2c66affSColin Finck if( FAILED( r ) || !count ) 229c2c66affSColin Finck break; 230c2c66affSColin Finck decode_streamname( stat.pwcsName, name ); 231c2c66affSColin Finck TRACE("stream %2d -> %s %s\n", n, 232c2c66affSColin Finck debugstr_w(stat.pwcsName), debugstr_w(name) ); 233c2c66affSColin Finck CoTaskMemFree( stat.pwcsName ); 234c2c66affSColin Finck n++; 235c2c66affSColin Finck } 236c2c66affSColin Finck 237c2c66affSColin Finck IEnumSTATSTG_Release( stgenum ); 238c2c66affSColin Finck } 239c2c66affSColin Finck 240c2c66affSColin Finck UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table, 241c2c66affSColin Finck BYTE **pdata, UINT *psz ) 242c2c66affSColin Finck { 243c2c66affSColin Finck HRESULT r; 244c2c66affSColin Finck UINT ret = ERROR_FUNCTION_FAILED; 245c2c66affSColin Finck VOID *data; 246c2c66affSColin Finck ULONG sz, count; 247c2c66affSColin Finck IStream *stm = NULL; 248c2c66affSColin Finck STATSTG stat; 249c2c66affSColin Finck LPWSTR encname; 250c2c66affSColin Finck 251c2c66affSColin Finck encname = encode_streamname(table, stname); 252c2c66affSColin Finck 253c2c66affSColin Finck TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname)); 254c2c66affSColin Finck 255c2c66affSColin Finck r = IStorage_OpenStream(stg, encname, NULL, 256c2c66affSColin Finck STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); 257c2c66affSColin Finck msi_free( encname ); 258c2c66affSColin Finck if( FAILED( r ) ) 259c2c66affSColin Finck { 260c2c66affSColin Finck WARN("open stream failed r = %08x - empty table?\n", r); 261c2c66affSColin Finck return ret; 262c2c66affSColin Finck } 263c2c66affSColin Finck 264c2c66affSColin Finck r = IStream_Stat(stm, &stat, STATFLAG_NONAME ); 265c2c66affSColin Finck if( FAILED( r ) ) 266c2c66affSColin Finck { 267c2c66affSColin Finck WARN("open stream failed r = %08x!\n", r); 268c2c66affSColin Finck goto end; 269c2c66affSColin Finck } 270c2c66affSColin Finck 271c2c66affSColin Finck if( stat.cbSize.QuadPart >> 32 ) 272c2c66affSColin Finck { 273c2c66affSColin Finck WARN("Too big!\n"); 274c2c66affSColin Finck goto end; 275c2c66affSColin Finck } 276c2c66affSColin Finck 277c2c66affSColin Finck sz = stat.cbSize.QuadPart; 278c2c66affSColin Finck data = msi_alloc( sz ); 279c2c66affSColin Finck if( !data ) 280c2c66affSColin Finck { 281c2c66affSColin Finck WARN("couldn't allocate memory r=%08x!\n", r); 282c2c66affSColin Finck ret = ERROR_NOT_ENOUGH_MEMORY; 283c2c66affSColin Finck goto end; 284c2c66affSColin Finck } 285c2c66affSColin Finck 286c2c66affSColin Finck r = IStream_Read(stm, data, sz, &count ); 287c2c66affSColin Finck if( FAILED( r ) || ( count != sz ) ) 288c2c66affSColin Finck { 289c2c66affSColin Finck msi_free( data ); 290c2c66affSColin Finck WARN("read stream failed r = %08x!\n", r); 291c2c66affSColin Finck goto end; 292c2c66affSColin Finck } 293c2c66affSColin Finck 294c2c66affSColin Finck *pdata = data; 295c2c66affSColin Finck *psz = sz; 296c2c66affSColin Finck ret = ERROR_SUCCESS; 297c2c66affSColin Finck 298c2c66affSColin Finck end: 299c2c66affSColin Finck IStream_Release( stm ); 300c2c66affSColin Finck 301c2c66affSColin Finck return ret; 302c2c66affSColin Finck } 303c2c66affSColin Finck 304c2c66affSColin Finck UINT write_stream_data( IStorage *stg, LPCWSTR stname, 305c2c66affSColin Finck LPCVOID data, UINT sz, BOOL bTable ) 306c2c66affSColin Finck { 307c2c66affSColin Finck HRESULT r; 308c2c66affSColin Finck UINT ret = ERROR_FUNCTION_FAILED; 309c2c66affSColin Finck ULONG count; 310c2c66affSColin Finck IStream *stm = NULL; 311c2c66affSColin Finck ULARGE_INTEGER size; 312c2c66affSColin Finck LARGE_INTEGER pos; 313c2c66affSColin Finck LPWSTR encname; 314c2c66affSColin Finck 315c2c66affSColin Finck encname = encode_streamname(bTable, stname ); 316c2c66affSColin Finck r = IStorage_OpenStream( stg, encname, NULL, 317c2c66affSColin Finck STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); 318c2c66affSColin Finck if( FAILED(r) ) 319c2c66affSColin Finck { 320c2c66affSColin Finck r = IStorage_CreateStream( stg, encname, 321c2c66affSColin Finck STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); 322c2c66affSColin Finck } 323c2c66affSColin Finck msi_free( encname ); 324c2c66affSColin Finck if( FAILED( r ) ) 325c2c66affSColin Finck { 326c2c66affSColin Finck WARN("open stream failed r = %08x\n", r); 327c2c66affSColin Finck return ret; 328c2c66affSColin Finck } 329c2c66affSColin Finck 330c2c66affSColin Finck size.QuadPart = sz; 331c2c66affSColin Finck r = IStream_SetSize( stm, size ); 332c2c66affSColin Finck if( FAILED( r ) ) 333c2c66affSColin Finck { 334c2c66affSColin Finck WARN("Failed to SetSize\n"); 335c2c66affSColin Finck goto end; 336c2c66affSColin Finck } 337c2c66affSColin Finck 338c2c66affSColin Finck pos.QuadPart = 0; 339c2c66affSColin Finck r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL ); 340c2c66affSColin Finck if( FAILED( r ) ) 341c2c66affSColin Finck { 342c2c66affSColin Finck WARN("Failed to Seek\n"); 343c2c66affSColin Finck goto end; 344c2c66affSColin Finck } 345c2c66affSColin Finck 346c2c66affSColin Finck if (sz) 347c2c66affSColin Finck { 348c2c66affSColin Finck r = IStream_Write(stm, data, sz, &count ); 349c2c66affSColin Finck if( FAILED( r ) || ( count != sz ) ) 350c2c66affSColin Finck { 351c2c66affSColin Finck WARN("Failed to Write\n"); 352c2c66affSColin Finck goto end; 353c2c66affSColin Finck } 354c2c66affSColin Finck } 355c2c66affSColin Finck 356c2c66affSColin Finck ret = ERROR_SUCCESS; 357c2c66affSColin Finck 358c2c66affSColin Finck end: 359c2c66affSColin Finck IStream_Release( stm ); 360c2c66affSColin Finck 361c2c66affSColin Finck return ret; 362c2c66affSColin Finck } 363c2c66affSColin Finck 364c2c66affSColin Finck static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count ) 365c2c66affSColin Finck { 366c2c66affSColin Finck UINT i; 367c2c66affSColin Finck for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table ); 368c2c66affSColin Finck } 369c2c66affSColin Finck 370c2c66affSColin Finck static void free_table( MSITABLE *table ) 371c2c66affSColin Finck { 372c2c66affSColin Finck UINT i; 373c2c66affSColin Finck for( i=0; i<table->row_count; i++ ) 374c2c66affSColin Finck msi_free( table->data[i] ); 375c2c66affSColin Finck msi_free( table->data ); 376c2c66affSColin Finck msi_free( table->data_persistent ); 377c2c66affSColin Finck msi_free_colinfo( table->colinfo, table->col_count ); 378c2c66affSColin Finck msi_free( table->colinfo ); 379c2c66affSColin Finck msi_free( table ); 380c2c66affSColin Finck } 381c2c66affSColin Finck 382c2c66affSColin Finck static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref ) 383c2c66affSColin Finck { 384c2c66affSColin Finck const MSICOLUMNINFO *last_col; 385c2c66affSColin Finck 386c2c66affSColin Finck if (!count) 387c2c66affSColin Finck return 0; 388c2c66affSColin Finck 389c2c66affSColin Finck if (bytes_per_strref != LONG_STR_BYTES) 390c2c66affSColin Finck { 391c2c66affSColin Finck UINT i, size = 0; 392c2c66affSColin Finck for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref ); 393c2c66affSColin Finck return size; 394c2c66affSColin Finck } 395c2c66affSColin Finck last_col = &cols[count - 1]; 396c2c66affSColin Finck return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref ); 397c2c66affSColin Finck } 398c2c66affSColin Finck 399c2c66affSColin Finck /* add this table to the list of cached tables in the database */ 400c2c66affSColin Finck static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg ) 401c2c66affSColin Finck { 402c2c66affSColin Finck BYTE *rawdata = NULL; 403c2c66affSColin Finck UINT rawsize = 0, i, j, row_size, row_size_mem; 404c2c66affSColin Finck 405c2c66affSColin Finck TRACE("%s\n",debugstr_w(t->name)); 406c2c66affSColin Finck 407c2c66affSColin Finck row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref ); 408c2c66affSColin Finck row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES ); 409c2c66affSColin Finck 410c2c66affSColin Finck /* if we can't read the table, just assume that it's empty */ 411c2c66affSColin Finck read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize ); 412c2c66affSColin Finck if( !rawdata ) 413c2c66affSColin Finck return ERROR_SUCCESS; 414c2c66affSColin Finck 415c2c66affSColin Finck TRACE("Read %d bytes\n", rawsize ); 416c2c66affSColin Finck 417c2c66affSColin Finck if( rawsize % row_size ) 418c2c66affSColin Finck { 419c2c66affSColin Finck WARN("Table size is invalid %d/%d\n", rawsize, row_size ); 420c2c66affSColin Finck goto err; 421c2c66affSColin Finck } 422c2c66affSColin Finck 423c2c66affSColin Finck if ((t->row_count = rawsize / row_size)) 424c2c66affSColin Finck { 425c2c66affSColin Finck if (!(t->data = msi_alloc_zero( t->row_count * sizeof(USHORT *) ))) goto err; 426c2c66affSColin Finck if (!(t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL) ))) goto err; 427c2c66affSColin Finck } 428c2c66affSColin Finck 429c2c66affSColin Finck /* transpose all the data */ 430c2c66affSColin Finck TRACE("Transposing data from %d rows\n", t->row_count ); 431c2c66affSColin Finck for (i = 0; i < t->row_count; i++) 432c2c66affSColin Finck { 433c2c66affSColin Finck UINT ofs = 0, ofs_mem = 0; 434c2c66affSColin Finck 435c2c66affSColin Finck t->data[i] = msi_alloc( row_size_mem ); 436c2c66affSColin Finck if( !t->data[i] ) 437c2c66affSColin Finck goto err; 438c2c66affSColin Finck t->data_persistent[i] = TRUE; 439c2c66affSColin Finck 440c2c66affSColin Finck for (j = 0; j < t->col_count; j++) 441c2c66affSColin Finck { 442c2c66affSColin Finck UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); 443c2c66affSColin Finck UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref ); 444c2c66affSColin Finck UINT k; 445c2c66affSColin Finck 446c2c66affSColin Finck if ( n != 2 && n != 3 && n != 4 ) 447c2c66affSColin Finck { 448c2c66affSColin Finck ERR("oops - unknown column width %d\n", n); 449c2c66affSColin Finck goto err; 450c2c66affSColin Finck } 451c2c66affSColin Finck if (t->colinfo[j].type & MSITYPE_STRING && n < m) 452c2c66affSColin Finck { 453c2c66affSColin Finck for (k = 0; k < m; k++) 454c2c66affSColin Finck { 455c2c66affSColin Finck if (k < n) 456c2c66affSColin Finck t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; 457c2c66affSColin Finck else 458c2c66affSColin Finck t->data[i][ofs_mem + k] = 0; 459c2c66affSColin Finck } 460c2c66affSColin Finck } 461c2c66affSColin Finck else 462c2c66affSColin Finck { 463c2c66affSColin Finck for (k = 0; k < n; k++) 464c2c66affSColin Finck t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k]; 465c2c66affSColin Finck } 466c2c66affSColin Finck ofs_mem += m; 467c2c66affSColin Finck ofs += n; 468c2c66affSColin Finck } 469c2c66affSColin Finck } 470c2c66affSColin Finck 471c2c66affSColin Finck msi_free( rawdata ); 472c2c66affSColin Finck return ERROR_SUCCESS; 473c2c66affSColin Finck err: 474c2c66affSColin Finck msi_free( rawdata ); 475c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 476c2c66affSColin Finck } 477c2c66affSColin Finck 478c2c66affSColin Finck void free_cached_tables( MSIDATABASE *db ) 479c2c66affSColin Finck { 480c2c66affSColin Finck while( !list_empty( &db->tables ) ) 481c2c66affSColin Finck { 482c2c66affSColin Finck MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry ); 483c2c66affSColin Finck 484c2c66affSColin Finck list_remove( &t->entry ); 485c2c66affSColin Finck free_table( t ); 486c2c66affSColin Finck } 487c2c66affSColin Finck } 488c2c66affSColin Finck 489c2c66affSColin Finck static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name ) 490c2c66affSColin Finck { 491c2c66affSColin Finck MSITABLE *t; 492c2c66affSColin Finck 493c2c66affSColin Finck LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry ) 494958f1addSwinesync if( !wcscmp( name, t->name ) ) 495c2c66affSColin Finck return t; 496c2c66affSColin Finck 497c2c66affSColin Finck return NULL; 498c2c66affSColin Finck } 499c2c66affSColin Finck 500c2c66affSColin Finck static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count ) 501c2c66affSColin Finck { 502c2c66affSColin Finck DWORD i; 503c2c66affSColin Finck 504c2c66affSColin Finck for (i = 0; colinfo && i < count; i++) 505c2c66affSColin Finck { 506c2c66affSColin Finck assert( i + 1 == colinfo[i].number ); 507c2c66affSColin Finck if (i) colinfo[i].offset = colinfo[i - 1].offset + 508c2c66affSColin Finck bytes_per_column( db, &colinfo[i - 1], LONG_STR_BYTES ); 509c2c66affSColin Finck else colinfo[i].offset = 0; 510c2c66affSColin Finck 511c2c66affSColin Finck TRACE("column %d is [%s] with type %08x ofs %d\n", 512c2c66affSColin Finck colinfo[i].number, debugstr_w(colinfo[i].colname), 513c2c66affSColin Finck colinfo[i].type, colinfo[i].offset); 514c2c66affSColin Finck } 515c2c66affSColin Finck } 516c2c66affSColin Finck 517c2c66affSColin Finck static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz ) 518c2c66affSColin Finck { 519c2c66affSColin Finck const MSICOLUMNINFO *p; 520c2c66affSColin Finck DWORD i, n; 521c2c66affSColin Finck 522c2c66affSColin Finck TRACE("%s\n", debugstr_w(name)); 523c2c66affSColin Finck 524958f1addSwinesync if (!wcscmp( name, szTables )) 525c2c66affSColin Finck { 526c2c66affSColin Finck p = _Tables_cols; 527c2c66affSColin Finck n = 1; 528c2c66affSColin Finck } 529958f1addSwinesync else if (!wcscmp( name, szColumns )) 530c2c66affSColin Finck { 531c2c66affSColin Finck p = _Columns_cols; 532c2c66affSColin Finck n = 4; 533c2c66affSColin Finck } 534c2c66affSColin Finck else return ERROR_FUNCTION_FAILED; 535c2c66affSColin Finck 536c2c66affSColin Finck for (i = 0; i < n; i++) 537c2c66affSColin Finck { 538c2c66affSColin Finck if (colinfo && i < *sz) colinfo[i] = p[i]; 539c2c66affSColin Finck if (colinfo && i >= *sz) break; 540c2c66affSColin Finck } 541c2c66affSColin Finck table_calc_column_offsets( db, colinfo, n ); 542c2c66affSColin Finck *sz = n; 543c2c66affSColin Finck return ERROR_SUCCESS; 544c2c66affSColin Finck } 545c2c66affSColin Finck 546c2c66affSColin Finck static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz ); 547c2c66affSColin Finck 548c2c66affSColin Finck static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount ) 549c2c66affSColin Finck { 550c2c66affSColin Finck UINT r, column_count = 0; 551c2c66affSColin Finck MSICOLUMNINFO *columns; 552c2c66affSColin Finck 553c2c66affSColin Finck /* get the number of columns in this table */ 554c2c66affSColin Finck column_count = 0; 555c2c66affSColin Finck r = get_tablecolumns( db, name, NULL, &column_count ); 556c2c66affSColin Finck if (r != ERROR_SUCCESS) 557c2c66affSColin Finck return r; 558c2c66affSColin Finck 559c2c66affSColin Finck *pcount = column_count; 560c2c66affSColin Finck 561c2c66affSColin Finck /* if there are no columns, there's no table */ 562c2c66affSColin Finck if (!column_count) 563c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 564c2c66affSColin Finck 565c2c66affSColin Finck TRACE("table %s found\n", debugstr_w(name)); 566c2c66affSColin Finck 567c2c66affSColin Finck columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) ); 568c2c66affSColin Finck if (!columns) 569c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 570c2c66affSColin Finck 571c2c66affSColin Finck r = get_tablecolumns( db, name, columns, &column_count ); 572c2c66affSColin Finck if (r != ERROR_SUCCESS) 573c2c66affSColin Finck { 574c2c66affSColin Finck msi_free( columns ); 575c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 576c2c66affSColin Finck } 577c2c66affSColin Finck *pcols = columns; 578c2c66affSColin Finck return r; 579c2c66affSColin Finck } 580c2c66affSColin Finck 581c2c66affSColin Finck static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret ) 582c2c66affSColin Finck { 583c2c66affSColin Finck MSITABLE *table; 584c2c66affSColin Finck UINT r; 585c2c66affSColin Finck 586c2c66affSColin Finck /* first, see if the table is cached */ 587c2c66affSColin Finck table = find_cached_table( db, name ); 588c2c66affSColin Finck if (table) 589c2c66affSColin Finck { 590c2c66affSColin Finck *table_ret = table; 591c2c66affSColin Finck return ERROR_SUCCESS; 592c2c66affSColin Finck } 593c2c66affSColin Finck 594c2c66affSColin Finck /* nonexistent tables should be interpreted as empty tables */ 595c2c66affSColin Finck table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) ); 596c2c66affSColin Finck if (!table) 597c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 598c2c66affSColin Finck 599c2c66affSColin Finck table->row_count = 0; 600c2c66affSColin Finck table->data = NULL; 601c2c66affSColin Finck table->data_persistent = NULL; 602c2c66affSColin Finck table->colinfo = NULL; 603c2c66affSColin Finck table->col_count = 0; 604c2c66affSColin Finck table->persistent = MSICONDITION_TRUE; 605c2c66affSColin Finck lstrcpyW( table->name, name ); 606c2c66affSColin Finck 607958f1addSwinesync if (!wcscmp( name, szTables ) || !wcscmp( name, szColumns )) 608c2c66affSColin Finck table->persistent = MSICONDITION_NONE; 609c2c66affSColin Finck 610c2c66affSColin Finck r = table_get_column_info( db, name, &table->colinfo, &table->col_count ); 611c2c66affSColin Finck if (r != ERROR_SUCCESS) 612c2c66affSColin Finck { 613c2c66affSColin Finck free_table( table ); 614c2c66affSColin Finck return r; 615c2c66affSColin Finck } 616c2c66affSColin Finck r = read_table_from_storage( db, table, db->storage ); 617c2c66affSColin Finck if (r != ERROR_SUCCESS) 618c2c66affSColin Finck { 619c2c66affSColin Finck free_table( table ); 620c2c66affSColin Finck return r; 621c2c66affSColin Finck } 622c2c66affSColin Finck list_add_head( &db->tables, &table->entry ); 623c2c66affSColin Finck *table_ret = table; 624c2c66affSColin Finck return ERROR_SUCCESS; 625c2c66affSColin Finck } 626c2c66affSColin Finck 627c2c66affSColin Finck static UINT read_table_int( BYTE *const *data, UINT row, UINT col, UINT bytes ) 628c2c66affSColin Finck { 629c2c66affSColin Finck UINT ret = 0, i; 630c2c66affSColin Finck 631c2c66affSColin Finck for (i = 0; i < bytes; i++) 632c2c66affSColin Finck ret += data[row][col + i] << i * 8; 633c2c66affSColin Finck 634c2c66affSColin Finck return ret; 635c2c66affSColin Finck } 636c2c66affSColin Finck 637c2c66affSColin Finck static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz ) 638c2c66affSColin Finck { 639c2c66affSColin Finck UINT r, i, n = 0, table_id, count, maxcount = *sz; 640c2c66affSColin Finck MSITABLE *table = NULL; 641c2c66affSColin Finck 642c2c66affSColin Finck TRACE("%s\n", debugstr_w(szTableName)); 643c2c66affSColin Finck 644c2c66affSColin Finck /* first check if there is a default table with that name */ 645c2c66affSColin Finck r = get_defaulttablecolumns( db, szTableName, colinfo, sz ); 646c2c66affSColin Finck if (r == ERROR_SUCCESS && *sz) 647c2c66affSColin Finck return r; 648c2c66affSColin Finck 649c2c66affSColin Finck r = get_table( db, szColumns, &table ); 650c2c66affSColin Finck if (r != ERROR_SUCCESS) 651c2c66affSColin Finck { 652c2c66affSColin Finck ERR("couldn't load _Columns table\n"); 653c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 654c2c66affSColin Finck } 655c2c66affSColin Finck 656c2c66affSColin Finck /* convert table and column names to IDs from the string table */ 657c2c66affSColin Finck r = msi_string2id( db->strings, szTableName, -1, &table_id ); 658c2c66affSColin Finck if (r != ERROR_SUCCESS) 659c2c66affSColin Finck { 660c2c66affSColin Finck WARN("Couldn't find id for %s\n", debugstr_w(szTableName)); 661c2c66affSColin Finck return r; 662c2c66affSColin Finck } 663c2c66affSColin Finck TRACE("Table id is %d, row count is %d\n", table_id, table->row_count); 664c2c66affSColin Finck 665c2c66affSColin Finck /* Note: _Columns table doesn't have non-persistent data */ 666c2c66affSColin Finck 667c2c66affSColin Finck /* if maxcount is non-zero, assume it's exactly right for this table */ 668c2c66affSColin Finck if (colinfo) memset( colinfo, 0, maxcount * sizeof(*colinfo) ); 669c2c66affSColin Finck count = table->row_count; 670c2c66affSColin Finck for (i = 0; i < count; i++) 671c2c66affSColin Finck { 672c2c66affSColin Finck if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue; 673c2c66affSColin Finck if (colinfo) 674c2c66affSColin Finck { 675c2c66affSColin Finck UINT id = read_table_int( table->data, i, table->colinfo[2].offset, LONG_STR_BYTES ); 676c2c66affSColin Finck UINT col = read_table_int( table->data, i, table->colinfo[1].offset, sizeof(USHORT) ) - (1 << 15); 677c2c66affSColin Finck 678c2c66affSColin Finck /* check the column number is in range */ 679c2c66affSColin Finck if (col < 1 || col > maxcount) 680c2c66affSColin Finck { 681c2c66affSColin Finck ERR("column %d out of range (maxcount: %d)\n", col, maxcount); 682c2c66affSColin Finck continue; 683c2c66affSColin Finck } 684c2c66affSColin Finck /* check if this column was already set */ 685c2c66affSColin Finck if (colinfo[col - 1].number) 686c2c66affSColin Finck { 687c2c66affSColin Finck ERR("duplicate column %d\n", col); 688c2c66affSColin Finck continue; 689c2c66affSColin Finck } 690c2c66affSColin Finck colinfo[col - 1].tablename = msi_string_lookup( db->strings, table_id, NULL ); 691c2c66affSColin Finck colinfo[col - 1].number = col; 692c2c66affSColin Finck colinfo[col - 1].colname = msi_string_lookup( db->strings, id, NULL ); 693c2c66affSColin Finck colinfo[col - 1].type = read_table_int( table->data, i, table->colinfo[3].offset, 694c2c66affSColin Finck sizeof(USHORT) ) - (1 << 15); 695c2c66affSColin Finck colinfo[col - 1].offset = 0; 696c2c66affSColin Finck colinfo[col - 1].hash_table = NULL; 697c2c66affSColin Finck } 698c2c66affSColin Finck n++; 699c2c66affSColin Finck } 700c2c66affSColin Finck TRACE("%s has %d columns\n", debugstr_w(szTableName), n); 701c2c66affSColin Finck 702c2c66affSColin Finck if (colinfo && n != maxcount) 703c2c66affSColin Finck { 704c2c66affSColin Finck ERR("missing column in table %s\n", debugstr_w(szTableName)); 705c2c66affSColin Finck msi_free_colinfo( colinfo, maxcount ); 706c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 707c2c66affSColin Finck } 708c2c66affSColin Finck table_calc_column_offsets( db, colinfo, n ); 709c2c66affSColin Finck *sz = n; 710c2c66affSColin Finck return ERROR_SUCCESS; 711c2c66affSColin Finck } 712c2c66affSColin Finck 713c2c66affSColin Finck UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info, 714caa225a4Swinesync MSICONDITION persistent, BOOL hold ) 715c2c66affSColin Finck { 716c2c66affSColin Finck UINT r, nField; 717c2c66affSColin Finck MSIVIEW *tv = NULL; 718c2c66affSColin Finck MSIRECORD *rec = NULL; 719c2c66affSColin Finck column_info *col; 720c2c66affSColin Finck MSITABLE *table; 721c2c66affSColin Finck UINT i; 722c2c66affSColin Finck 723c2c66affSColin Finck /* only add tables that don't exist already */ 724c2c66affSColin Finck if( TABLE_Exists(db, name ) ) 725c2c66affSColin Finck { 726c2c66affSColin Finck WARN("table %s exists\n", debugstr_w(name)); 727c2c66affSColin Finck return ERROR_BAD_QUERY_SYNTAX; 728c2c66affSColin Finck } 729c2c66affSColin Finck 730c2c66affSColin Finck table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) ); 731c2c66affSColin Finck if( !table ) 732c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 733c2c66affSColin Finck 734caa225a4Swinesync table->ref_count = 0; 735c2c66affSColin Finck table->row_count = 0; 736c2c66affSColin Finck table->data = NULL; 737c2c66affSColin Finck table->data_persistent = NULL; 738c2c66affSColin Finck table->colinfo = NULL; 739c2c66affSColin Finck table->col_count = 0; 740c2c66affSColin Finck table->persistent = persistent; 741c2c66affSColin Finck lstrcpyW( table->name, name ); 742c2c66affSColin Finck 743caa225a4Swinesync if( hold ) 744caa225a4Swinesync table->ref_count++; 745caa225a4Swinesync 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 { 758211c4104Swinesync UINT table_id = msi_add_string( db->strings, col->table, -1, persistent ); 759211c4104Swinesync 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 ].hash_table = NULL; 767*791734c2Swinesync table->colinfo[ i ].temporary = (col->type & MSITYPE_TEMPORARY) != 0; 768c2c66affSColin Finck } 769c2c66affSColin Finck table_calc_column_offsets( db, table->colinfo, table->col_count); 770c2c66affSColin Finck 771c2c66affSColin Finck r = TABLE_CreateView( db, szTables, &tv ); 772c2c66affSColin Finck TRACE("CreateView returned %x\n", r); 773c2c66affSColin Finck if( r ) 774c2c66affSColin Finck { 775c2c66affSColin Finck free_table( table ); 776c2c66affSColin Finck return r; 777c2c66affSColin Finck } 778c2c66affSColin Finck 779c2c66affSColin Finck r = tv->ops->execute( tv, 0 ); 780c2c66affSColin Finck TRACE("tv execute returned %x\n", r); 781c2c66affSColin Finck if( r ) 782c2c66affSColin Finck goto err; 783c2c66affSColin Finck 784c2c66affSColin Finck rec = MSI_CreateRecord( 1 ); 785c2c66affSColin Finck if( !rec ) 786c2c66affSColin Finck goto err; 787c2c66affSColin Finck 788c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 1, name ); 789c2c66affSColin Finck if( r ) 790c2c66affSColin Finck goto err; 791c2c66affSColin Finck 792c2c66affSColin Finck r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE ); 793c2c66affSColin Finck TRACE("insert_row returned %x\n", r); 794c2c66affSColin Finck if( r ) 795c2c66affSColin Finck goto err; 796c2c66affSColin Finck 797c2c66affSColin Finck tv->ops->delete( tv ); 798c2c66affSColin Finck tv = NULL; 799c2c66affSColin Finck 800c2c66affSColin Finck msiobj_release( &rec->hdr ); 801c2c66affSColin Finck rec = NULL; 802c2c66affSColin Finck 803c2c66affSColin Finck if( persistent != MSICONDITION_FALSE ) 804c2c66affSColin Finck { 805c2c66affSColin Finck /* add each column to the _Columns table */ 806c2c66affSColin Finck r = TABLE_CreateView( db, szColumns, &tv ); 807c2c66affSColin Finck if( r ) 808c2c66affSColin Finck goto err; 809c2c66affSColin Finck 810c2c66affSColin Finck r = tv->ops->execute( tv, 0 ); 811c2c66affSColin Finck TRACE("tv execute returned %x\n", r); 812c2c66affSColin Finck if( r ) 813c2c66affSColin Finck goto err; 814c2c66affSColin Finck 815c2c66affSColin Finck rec = MSI_CreateRecord( 4 ); 816c2c66affSColin Finck if( !rec ) 817c2c66affSColin Finck goto err; 818c2c66affSColin Finck 819c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 1, name ); 820c2c66affSColin Finck if( r ) 821c2c66affSColin Finck goto err; 822c2c66affSColin Finck 823c2c66affSColin Finck /* 824c2c66affSColin Finck * need to set the table, column number, col name and type 825c2c66affSColin Finck * for each column we enter in the table 826c2c66affSColin Finck */ 827c2c66affSColin Finck nField = 1; 828c2c66affSColin Finck for( col = col_info; col; col = col->next ) 829c2c66affSColin Finck { 830c2c66affSColin Finck r = MSI_RecordSetInteger( rec, 2, nField ); 831c2c66affSColin Finck if( r ) 832c2c66affSColin Finck goto err; 833c2c66affSColin Finck 834c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 3, col->column ); 835c2c66affSColin Finck if( r ) 836c2c66affSColin Finck goto err; 837c2c66affSColin Finck 838c2c66affSColin Finck r = MSI_RecordSetInteger( rec, 4, col->type ); 839c2c66affSColin Finck if( r ) 840c2c66affSColin Finck goto err; 841c2c66affSColin Finck 842c2c66affSColin Finck r = tv->ops->insert_row( tv, rec, -1, FALSE ); 843c2c66affSColin Finck if( r ) 844c2c66affSColin Finck goto err; 845c2c66affSColin Finck 846c2c66affSColin Finck nField++; 847c2c66affSColin Finck } 848c2c66affSColin Finck if( !col ) 849c2c66affSColin Finck r = ERROR_SUCCESS; 850c2c66affSColin Finck } 851c2c66affSColin Finck 852c2c66affSColin Finck err: 853c2c66affSColin Finck if (rec) 854c2c66affSColin Finck msiobj_release( &rec->hdr ); 855c2c66affSColin Finck /* FIXME: remove values from the string table on error */ 856c2c66affSColin Finck if( tv ) 857c2c66affSColin Finck tv->ops->delete( tv ); 858c2c66affSColin Finck 859c2c66affSColin Finck if (r == ERROR_SUCCESS) 860c2c66affSColin Finck list_add_head( &db->tables, &table->entry ); 861c2c66affSColin Finck else 862c2c66affSColin Finck free_table( table ); 863c2c66affSColin Finck 864c2c66affSColin Finck return r; 865c2c66affSColin Finck } 866c2c66affSColin Finck 867c2c66affSColin Finck static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref ) 868c2c66affSColin Finck { 869c2c66affSColin Finck BYTE *rawdata = NULL; 870c2c66affSColin Finck UINT rawsize, i, j, row_size, row_count; 871c2c66affSColin Finck UINT r = ERROR_FUNCTION_FAILED; 872c2c66affSColin Finck 873c2c66affSColin Finck /* Nothing to do for non-persistent tables */ 874c2c66affSColin Finck if( t->persistent == MSICONDITION_FALSE ) 875c2c66affSColin Finck return ERROR_SUCCESS; 876c2c66affSColin Finck 877c2c66affSColin Finck TRACE("Saving %s\n", debugstr_w( t->name ) ); 878c2c66affSColin Finck 879c2c66affSColin Finck row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref ); 880c2c66affSColin Finck row_count = t->row_count; 881c2c66affSColin Finck for (i = 0; i < t->row_count; i++) 882c2c66affSColin Finck { 883c2c66affSColin Finck if (!t->data_persistent[i]) 884c2c66affSColin Finck { 885c2c66affSColin Finck row_count = 1; /* yes, this is bizarre */ 886c2c66affSColin Finck break; 887c2c66affSColin Finck } 888c2c66affSColin Finck } 889c2c66affSColin Finck rawsize = row_count * row_size; 890c2c66affSColin Finck rawdata = msi_alloc_zero( rawsize ); 891c2c66affSColin Finck if( !rawdata ) 892c2c66affSColin Finck { 893c2c66affSColin Finck r = ERROR_NOT_ENOUGH_MEMORY; 894c2c66affSColin Finck goto err; 895c2c66affSColin Finck } 896c2c66affSColin Finck 897c2c66affSColin Finck rawsize = 0; 898c2c66affSColin Finck for (i = 0; i < row_count; i++) 899c2c66affSColin Finck { 900c2c66affSColin Finck UINT ofs = 0, ofs_mem = 0; 901c2c66affSColin Finck 902c2c66affSColin Finck if (!t->data_persistent[i]) break; 903c2c66affSColin Finck 904c2c66affSColin Finck for (j = 0; j < t->col_count; j++) 905c2c66affSColin Finck { 906c2c66affSColin Finck UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES ); 907c2c66affSColin Finck UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref ); 908c2c66affSColin Finck UINT k; 909c2c66affSColin Finck 910c2c66affSColin Finck if (n != 2 && n != 3 && n != 4) 911c2c66affSColin Finck { 912c2c66affSColin Finck ERR("oops - unknown column width %d\n", n); 913c2c66affSColin Finck goto err; 914c2c66affSColin Finck } 915c2c66affSColin Finck if (t->colinfo[j].type & MSITYPE_STRING && n < m) 916c2c66affSColin Finck { 917c2c66affSColin Finck UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES ); 918c2c66affSColin Finck if (id > 1 << bytes_per_strref * 8) 919c2c66affSColin Finck { 920c2c66affSColin Finck ERR("string id %u out of range\n", id); 921c2c66affSColin Finck goto err; 922c2c66affSColin Finck } 923c2c66affSColin Finck } 924c2c66affSColin Finck for (k = 0; k < n; k++) 925c2c66affSColin Finck { 926c2c66affSColin Finck rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k]; 927c2c66affSColin Finck } 928c2c66affSColin Finck ofs_mem += m; 929c2c66affSColin Finck ofs += n; 930c2c66affSColin Finck } 931c2c66affSColin Finck rawsize += row_size; 932c2c66affSColin Finck } 933c2c66affSColin Finck 934c2c66affSColin Finck TRACE("writing %d bytes\n", rawsize); 935c2c66affSColin Finck r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE ); 936c2c66affSColin Finck 937c2c66affSColin Finck err: 938c2c66affSColin Finck msi_free( rawdata ); 939c2c66affSColin Finck return r; 940c2c66affSColin Finck } 941c2c66affSColin Finck 942c2c66affSColin Finck static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name ) 943c2c66affSColin Finck { 944c2c66affSColin Finck MSITABLE *table; 945c2c66affSColin Finck UINT size, offset, old_count; 946c2c66affSColin Finck UINT n; 947c2c66affSColin Finck 948c2c66affSColin Finck if (!(table = find_cached_table( db, name ))) return; 949c2c66affSColin Finck old_count = table->col_count; 950c2c66affSColin Finck msi_free_colinfo( table->colinfo, table->col_count ); 951c2c66affSColin Finck msi_free( table->colinfo ); 952c2c66affSColin Finck table->colinfo = NULL; 953c2c66affSColin Finck 954c2c66affSColin Finck table_get_column_info( db, name, &table->colinfo, &table->col_count ); 955c2c66affSColin Finck if (!table->col_count) return; 956c2c66affSColin Finck 957c2c66affSColin Finck size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES ); 958c2c66affSColin Finck offset = table->colinfo[table->col_count - 1].offset; 959c2c66affSColin Finck 960c2c66affSColin Finck for ( n = 0; n < table->row_count; n++ ) 961c2c66affSColin Finck { 962c2c66affSColin Finck table->data[n] = msi_realloc( table->data[n], size ); 963c2c66affSColin Finck if (old_count < table->col_count) 964c2c66affSColin Finck memset( &table->data[n][offset], 0, size - offset ); 965c2c66affSColin Finck } 966c2c66affSColin Finck } 967c2c66affSColin Finck 968c2c66affSColin Finck /* try to find the table name in the _Tables table */ 969c2c66affSColin Finck BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ) 970c2c66affSColin Finck { 971c2c66affSColin Finck UINT r, table_id, i; 972c2c66affSColin Finck MSITABLE *table; 973c2c66affSColin Finck 974958f1addSwinesync if( !wcscmp( name, szTables ) || !wcscmp( name, szColumns ) || 975958f1addSwinesync !wcscmp( name, szStreams ) || !wcscmp( name, szStorages ) ) 976c2c66affSColin Finck return TRUE; 977c2c66affSColin Finck 978c2c66affSColin Finck r = msi_string2id( db->strings, name, -1, &table_id ); 979c2c66affSColin Finck if( r != ERROR_SUCCESS ) 980c2c66affSColin Finck { 981c2c66affSColin Finck TRACE("Couldn't find id for %s\n", debugstr_w(name)); 982c2c66affSColin Finck return FALSE; 983c2c66affSColin Finck } 984c2c66affSColin Finck 985c2c66affSColin Finck r = get_table( db, szTables, &table ); 986c2c66affSColin Finck if( r != ERROR_SUCCESS ) 987c2c66affSColin Finck { 988c2c66affSColin Finck ERR("table %s not available\n", debugstr_w(szTables)); 989c2c66affSColin Finck return FALSE; 990c2c66affSColin Finck } 991c2c66affSColin Finck 992c2c66affSColin Finck for( i = 0; i < table->row_count; i++ ) 993c2c66affSColin Finck { 994c2c66affSColin Finck if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id ) 995c2c66affSColin Finck return TRUE; 996c2c66affSColin Finck } 997c2c66affSColin Finck 998c2c66affSColin Finck return FALSE; 999c2c66affSColin Finck } 1000c2c66affSColin Finck 1001c2c66affSColin Finck /* below is the query interface to a table */ 1002c2c66affSColin Finck 1003c2c66affSColin Finck typedef struct tagMSITABLEVIEW 1004c2c66affSColin Finck { 1005c2c66affSColin Finck MSIVIEW view; 1006c2c66affSColin Finck MSIDATABASE *db; 1007c2c66affSColin Finck MSITABLE *table; 1008c2c66affSColin Finck MSICOLUMNINFO *columns; 1009c2c66affSColin Finck UINT num_cols; 1010c2c66affSColin Finck UINT row_size; 1011c2c66affSColin Finck WCHAR name[1]; 1012c2c66affSColin Finck } MSITABLEVIEW; 1013c2c66affSColin Finck 1014c2c66affSColin Finck static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) 1015c2c66affSColin Finck { 1016c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1017c2c66affSColin Finck UINT offset, n; 1018c2c66affSColin Finck 1019c2c66affSColin Finck if( !tv->table ) 1020c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1021c2c66affSColin Finck 1022c2c66affSColin Finck if( (col==0) || (col>tv->num_cols) ) 1023c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1024c2c66affSColin Finck 1025c2c66affSColin Finck /* how many rows are there ? */ 1026c2c66affSColin Finck if( row >= tv->table->row_count ) 1027c2c66affSColin Finck return ERROR_NO_MORE_ITEMS; 1028c2c66affSColin Finck 1029c2c66affSColin Finck if( tv->columns[col-1].offset >= tv->row_size ) 1030c2c66affSColin Finck { 1031c2c66affSColin Finck ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); 1032c2c66affSColin Finck ERR("%p %p\n", tv, tv->columns ); 1033c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1034c2c66affSColin Finck } 1035c2c66affSColin Finck 1036c2c66affSColin Finck n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES ); 1037c2c66affSColin Finck if (n != 2 && n != 3 && n != 4) 1038c2c66affSColin Finck { 1039c2c66affSColin Finck ERR("oops! what is %d bytes per column?\n", n ); 1040c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1041c2c66affSColin Finck } 1042c2c66affSColin Finck 1043c2c66affSColin Finck offset = tv->columns[col-1].offset; 1044c2c66affSColin Finck *val = read_table_int(tv->table->data, row, offset, n); 1045c2c66affSColin Finck 1046c2c66affSColin Finck /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */ 1047c2c66affSColin Finck 1048c2c66affSColin Finck return ERROR_SUCCESS; 1049c2c66affSColin Finck } 1050c2c66affSColin Finck 1051c2c66affSColin Finck static UINT get_stream_name( const MSITABLEVIEW *tv, UINT row, WCHAR **pstname ) 1052c2c66affSColin Finck { 1053c2c66affSColin Finck LPWSTR p, stname = NULL; 1054c2c66affSColin Finck UINT i, r, type, ival; 1055c2c66affSColin Finck DWORD len; 1056c2c66affSColin Finck LPCWSTR sval; 1057c2c66affSColin Finck MSIVIEW *view = (MSIVIEW *) tv; 1058c2c66affSColin Finck 1059c2c66affSColin Finck TRACE("%p %d\n", tv, row); 1060c2c66affSColin Finck 1061c2c66affSColin Finck len = lstrlenW( tv->name ) + 1; 1062c2c66affSColin Finck stname = msi_alloc( len*sizeof(WCHAR) ); 1063c2c66affSColin Finck if ( !stname ) 1064c2c66affSColin Finck { 1065c2c66affSColin Finck r = ERROR_OUTOFMEMORY; 1066c2c66affSColin Finck goto err; 1067c2c66affSColin Finck } 1068c2c66affSColin Finck 1069c2c66affSColin Finck lstrcpyW( stname, tv->name ); 1070c2c66affSColin Finck 1071c2c66affSColin Finck for ( i = 0; i < tv->num_cols; i++ ) 1072c2c66affSColin Finck { 1073c2c66affSColin Finck type = tv->columns[i].type; 1074c2c66affSColin Finck if ( type & MSITYPE_KEY ) 1075c2c66affSColin Finck { 1076c2c66affSColin Finck WCHAR number[0x20]; 1077c2c66affSColin Finck 1078c2c66affSColin Finck r = TABLE_fetch_int( view, row, i+1, &ival ); 1079c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1080c2c66affSColin Finck goto err; 1081c2c66affSColin Finck 1082c2c66affSColin Finck if ( tv->columns[i].type & MSITYPE_STRING ) 1083c2c66affSColin Finck { 1084c2c66affSColin Finck sval = msi_string_lookup( tv->db->strings, ival, NULL ); 1085c2c66affSColin Finck if ( !sval ) 1086c2c66affSColin Finck { 1087c2c66affSColin Finck r = ERROR_INVALID_PARAMETER; 1088c2c66affSColin Finck goto err; 1089c2c66affSColin Finck } 1090c2c66affSColin Finck } 1091c2c66affSColin Finck else 1092c2c66affSColin Finck { 1093c2c66affSColin Finck static const WCHAR fmt[] = { '%','d',0 }; 1094c2c66affSColin Finck UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES ); 1095c2c66affSColin Finck 1096c2c66affSColin Finck switch( n ) 1097c2c66affSColin Finck { 1098c2c66affSColin Finck case 2: 1099958f1addSwinesync swprintf( number, ARRAY_SIZE(number), fmt, ival-0x8000 ); 1100c2c66affSColin Finck break; 1101c2c66affSColin Finck case 4: 1102958f1addSwinesync swprintf( number, ARRAY_SIZE(number), fmt, ival^0x80000000 ); 1103c2c66affSColin Finck break; 1104c2c66affSColin Finck default: 1105c2c66affSColin Finck ERR( "oops - unknown column width %d\n", n ); 1106c2c66affSColin Finck r = ERROR_FUNCTION_FAILED; 1107c2c66affSColin Finck goto err; 1108c2c66affSColin Finck } 1109c2c66affSColin Finck sval = number; 1110c2c66affSColin Finck } 1111c2c66affSColin Finck 1112c2c66affSColin Finck len += lstrlenW( szDot ) + lstrlenW( sval ); 1113c2c66affSColin Finck p = msi_realloc ( stname, len*sizeof(WCHAR) ); 1114c2c66affSColin Finck if ( !p ) 1115c2c66affSColin Finck { 1116c2c66affSColin Finck r = ERROR_OUTOFMEMORY; 1117c2c66affSColin Finck goto err; 1118c2c66affSColin Finck } 1119c2c66affSColin Finck stname = p; 1120c2c66affSColin Finck 1121c2c66affSColin Finck lstrcatW( stname, szDot ); 1122c2c66affSColin Finck lstrcatW( stname, sval ); 1123c2c66affSColin Finck } 1124c2c66affSColin Finck else 1125c2c66affSColin Finck continue; 1126c2c66affSColin Finck } 1127c2c66affSColin Finck 1128c2c66affSColin Finck *pstname = stname; 1129c2c66affSColin Finck return ERROR_SUCCESS; 1130c2c66affSColin Finck 1131c2c66affSColin Finck err: 1132c2c66affSColin Finck msi_free( stname ); 1133c2c66affSColin Finck *pstname = NULL; 1134c2c66affSColin Finck return r; 1135c2c66affSColin Finck } 1136c2c66affSColin Finck 1137c2c66affSColin Finck /* 1138c2c66affSColin Finck * We need a special case for streams, as we need to reference column with 1139c2c66affSColin Finck * the name of the stream in the same table, and the table name 1140c2c66affSColin Finck * which may not be available at higher levels of the query 1141c2c66affSColin Finck */ 1142c2c66affSColin Finck static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm ) 1143c2c66affSColin Finck { 1144c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1145c2c66affSColin Finck UINT r; 1146c2c66affSColin Finck WCHAR *name; 1147c2c66affSColin Finck 1148c2c66affSColin Finck if( !view->ops->fetch_int ) 1149c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1150c2c66affSColin Finck 1151c2c66affSColin Finck r = get_stream_name( tv, row, &name ); 1152c2c66affSColin Finck if (r != ERROR_SUCCESS) 1153c2c66affSColin Finck { 1154c2c66affSColin Finck ERR("fetching stream, error = %u\n", r); 1155c2c66affSColin Finck return r; 1156c2c66affSColin Finck } 1157c2c66affSColin Finck 1158c2c66affSColin Finck r = msi_get_stream( tv->db, name, stm ); 1159c2c66affSColin Finck if (r != ERROR_SUCCESS) 1160c2c66affSColin Finck ERR("fetching stream %s, error = %u\n", debugstr_w(name), r); 1161c2c66affSColin Finck 1162c2c66affSColin Finck msi_free( name ); 1163c2c66affSColin Finck return r; 1164c2c66affSColin Finck } 1165c2c66affSColin Finck 116697918c36Swinesync /* Set a table value, i.e. preadjusted integer or string ID. */ 116797918c36Swinesync static UINT table_set_bytes( 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 12040e83dd03Swinesync static UINT int_to_table_storage( const MSITABLEVIEW *tv, UINT col, int val, UINT *ret ) 12050e83dd03Swinesync { 12060e83dd03Swinesync if ((tv->columns[col-1].type & MSI_DATASIZEMASK) == 2) 12070e83dd03Swinesync { 12080e83dd03Swinesync if (val == MSI_NULL_INTEGER) 12090e83dd03Swinesync *ret = 0; 12100e83dd03Swinesync else if ((val + 0x8000) & 0xffff0000) 12110e83dd03Swinesync { 12120e83dd03Swinesync ERR("value %d out of range\n", val); 12130e83dd03Swinesync return ERROR_FUNCTION_FAILED; 12140e83dd03Swinesync } 12150e83dd03Swinesync else 12160e83dd03Swinesync *ret = val + 0x8000; 12170e83dd03Swinesync } 12180e83dd03Swinesync else 12190e83dd03Swinesync *ret = val ^ 0x80000000; 12200e83dd03Swinesync 12210e83dd03Swinesync return ERROR_SUCCESS; 12220e83dd03Swinesync } 12230e83dd03Swinesync 122497918c36Swinesync static UINT TABLE_set_int( MSIVIEW *view, UINT row, UINT col, int val ) 122597918c36Swinesync { 122697918c36Swinesync MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 122797918c36Swinesync UINT r, table_int; 122897918c36Swinesync 122997918c36Swinesync TRACE("row %u, col %u, val %d.\n", row, col, val); 123097918c36Swinesync 123197918c36Swinesync if ((r = int_to_table_storage( tv, col, val, &table_int ))) 123297918c36Swinesync return r; 123397918c36Swinesync 123497918c36Swinesync if (tv->columns[col-1].type & MSITYPE_KEY) 123597918c36Swinesync { 123697918c36Swinesync UINT key; 123797918c36Swinesync 123897918c36Swinesync if ((r = TABLE_fetch_int( view, row, col, &key ))) 123997918c36Swinesync return r; 124097918c36Swinesync if (key != table_int) 124197918c36Swinesync { 124297918c36Swinesync ERR("Cannot modify primary key %s.%s.\n", 124397918c36Swinesync debugstr_w(tv->table->name), debugstr_w(tv->columns[col-1].colname)); 124497918c36Swinesync return ERROR_FUNCTION_FAILED; 124597918c36Swinesync } 124697918c36Swinesync } 124797918c36Swinesync 124897918c36Swinesync return table_set_bytes( tv, row, col, table_int ); 124997918c36Swinesync } 125097918c36Swinesync 125197918c36Swinesync static UINT TABLE_set_string( MSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len ) 125297918c36Swinesync { 125397918c36Swinesync MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 125497918c36Swinesync BOOL persistent; 125597918c36Swinesync UINT id, r; 125697918c36Swinesync 125797918c36Swinesync TRACE("row %u, col %u, val %s.\n", row, col, debugstr_wn(val, len)); 125897918c36Swinesync 125997918c36Swinesync persistent = (tv->table->persistent != MSICONDITION_FALSE) 126097918c36Swinesync && tv->table->data_persistent[row]; 126197918c36Swinesync 126297918c36Swinesync if (val) 126397918c36Swinesync { 126497918c36Swinesync r = msi_string2id( tv->db->strings, val, len, &id ); 126597918c36Swinesync if (r != ERROR_SUCCESS) 126697918c36Swinesync id = msi_add_string( tv->db->strings, val, len, persistent ); 126797918c36Swinesync } 126897918c36Swinesync else 126997918c36Swinesync id = 0; 127097918c36Swinesync 127197918c36Swinesync if (tv->columns[col-1].type & MSITYPE_KEY) 127297918c36Swinesync { 127397918c36Swinesync UINT key; 127497918c36Swinesync 127597918c36Swinesync if ((r = TABLE_fetch_int( view, row, col, &key ))) 127697918c36Swinesync return r; 127797918c36Swinesync if (key != id) 127897918c36Swinesync { 127997918c36Swinesync ERR("Cannot modify primary key %s.%s.\n", 128097918c36Swinesync debugstr_w(tv->table->name), debugstr_w(tv->columns[col-1].colname)); 128197918c36Swinesync return ERROR_FUNCTION_FAILED; 128297918c36Swinesync } 128397918c36Swinesync } 128497918c36Swinesync 128597918c36Swinesync return table_set_bytes( tv, row, col, id ); 128697918c36Swinesync } 128797918c36Swinesync 1288c2c66affSColin Finck static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec ) 1289c2c66affSColin Finck { 1290c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1291c2c66affSColin Finck 1292c2c66affSColin Finck if (!tv->table) 1293c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1294c2c66affSColin Finck 1295c2c66affSColin Finck return msi_view_get_row(tv->db, view, row, rec); 1296c2c66affSColin Finck } 1297c2c66affSColin Finck 1298c2c66affSColin Finck static UINT add_stream( MSIDATABASE *db, const WCHAR *name, IStream *data ) 1299c2c66affSColin Finck { 1300c2c66affSColin Finck static const WCHAR insert[] = { 1301c2c66affSColin Finck 'I','N','S','E','R','T',' ','I','N','T','O',' ', 1302c2c66affSColin Finck '`','_','S','t','r','e','a','m','s','`',' ', 1303c2c66affSColin Finck '(','`','N','a','m','e','`',',','`','D','a','t','a','`',')',' ', 1304c2c66affSColin Finck 'V','A','L','U','E','S',' ','(','?',',','?',')',0}; 1305c2c66affSColin Finck static const WCHAR update[] = { 1306c2c66affSColin Finck 'U','P','D','A','T','E',' ','`','_','S','t','r','e','a','m','s','`',' ', 1307c2c66affSColin Finck 'S','E','T',' ','`','D','a','t','a','`',' ','=',' ','?',' ', 1308c2c66affSColin Finck 'W','H','E','R','E',' ','`','N','a','m','e','`',' ','=',' ','?',0}; 1309c2c66affSColin Finck MSIQUERY *query; 1310c2c66affSColin Finck MSIRECORD *rec; 1311c2c66affSColin Finck UINT r; 1312c2c66affSColin Finck 1313c2c66affSColin Finck TRACE("%p %s %p\n", db, debugstr_w(name), data); 1314c2c66affSColin Finck 1315c2c66affSColin Finck if (!(rec = MSI_CreateRecord( 2 ))) 1316c2c66affSColin Finck return ERROR_OUTOFMEMORY; 1317c2c66affSColin Finck 1318c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 1, name ); 1319c2c66affSColin Finck if (r != ERROR_SUCCESS) 1320c2c66affSColin Finck goto done; 1321c2c66affSColin Finck 1322c2c66affSColin Finck r = MSI_RecordSetIStream( rec, 2, data ); 1323c2c66affSColin Finck if (r != ERROR_SUCCESS) 1324c2c66affSColin Finck goto done; 1325c2c66affSColin Finck 1326c2c66affSColin Finck r = MSI_DatabaseOpenViewW( db, insert, &query ); 1327c2c66affSColin Finck if (r != ERROR_SUCCESS) 1328c2c66affSColin Finck goto done; 1329c2c66affSColin Finck 1330c2c66affSColin Finck r = MSI_ViewExecute( query, rec ); 1331c2c66affSColin Finck msiobj_release( &query->hdr ); 1332c2c66affSColin Finck if (r == ERROR_SUCCESS) 1333c2c66affSColin Finck goto done; 1334c2c66affSColin Finck 1335c2c66affSColin Finck msiobj_release( &rec->hdr ); 1336c2c66affSColin Finck if (!(rec = MSI_CreateRecord( 2 ))) 1337c2c66affSColin Finck return ERROR_OUTOFMEMORY; 1338c2c66affSColin Finck 1339c2c66affSColin Finck r = MSI_RecordSetIStream( rec, 1, data ); 1340c2c66affSColin Finck if (r != ERROR_SUCCESS) 1341c2c66affSColin Finck goto done; 1342c2c66affSColin Finck 1343c2c66affSColin Finck r = MSI_RecordSetStringW( rec, 2, name ); 1344c2c66affSColin Finck if (r != ERROR_SUCCESS) 1345c2c66affSColin Finck goto done; 1346c2c66affSColin Finck 1347c2c66affSColin Finck r = MSI_DatabaseOpenViewW( db, update, &query ); 1348c2c66affSColin Finck if (r != ERROR_SUCCESS) 1349c2c66affSColin Finck goto done; 1350c2c66affSColin Finck 1351c2c66affSColin Finck r = MSI_ViewExecute( query, rec ); 1352c2c66affSColin Finck msiobj_release( &query->hdr ); 1353c2c66affSColin Finck 1354c2c66affSColin Finck done: 1355c2c66affSColin Finck msiobj_release( &rec->hdr ); 1356c2c66affSColin Finck return r; 1357c2c66affSColin Finck } 1358c2c66affSColin Finck 1359580d3e1bSwinesync static UINT TABLE_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream ) 1360580d3e1bSwinesync { 1361580d3e1bSwinesync MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1362580d3e1bSwinesync WCHAR *name; 1363580d3e1bSwinesync UINT r; 1364580d3e1bSwinesync 1365580d3e1bSwinesync TRACE("row %u, col %u, stream %p.\n", row, col, stream); 1366580d3e1bSwinesync 1367580d3e1bSwinesync if ((r = get_stream_name( tv, row - 1, &name ))) 1368580d3e1bSwinesync return r; 1369580d3e1bSwinesync 1370580d3e1bSwinesync r = add_stream( tv->db, name, stream ); 1371580d3e1bSwinesync msi_free( name ); 1372580d3e1bSwinesync return r; 1373580d3e1bSwinesync } 1374580d3e1bSwinesync 1375c2c66affSColin Finck static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue ) 1376c2c66affSColin Finck { 1377c2c66affSColin Finck MSICOLUMNINFO columninfo; 1378c2c66affSColin Finck UINT r; 1379c2c66affSColin Finck 1380306890e8Swinesync if (!iField || iField > tv->num_cols || MSI_RecordIsNull( rec, iField )) 1381c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1382c2c66affSColin Finck 1383c2c66affSColin Finck columninfo = tv->columns[ iField - 1 ]; 1384c2c66affSColin Finck 1385c2c66affSColin Finck if ( MSITYPE_IS_BINARY(columninfo.type) ) 1386c2c66affSColin Finck { 1387c2c66affSColin Finck *pvalue = 1; /* refers to the first key column */ 1388c2c66affSColin Finck } 1389c2c66affSColin Finck else if ( columninfo.type & MSITYPE_STRING ) 1390c2c66affSColin Finck { 1391c2c66affSColin Finck int len; 1392c2c66affSColin Finck const WCHAR *sval = msi_record_get_string( rec, iField, &len ); 1393c2c66affSColin Finck if (sval) 1394c2c66affSColin Finck { 1395c2c66affSColin Finck r = msi_string2id( tv->db->strings, sval, len, pvalue ); 1396c2c66affSColin Finck if (r != ERROR_SUCCESS) 1397c2c66affSColin Finck return ERROR_NOT_FOUND; 1398c2c66affSColin Finck } 1399c2c66affSColin Finck else *pvalue = 0; 1400c2c66affSColin Finck } 1401c2c66affSColin Finck else 14020e83dd03Swinesync return int_to_table_storage( tv, iField, MSI_RecordGetInteger( rec, iField ), pvalue ); 1403c2c66affSColin Finck 1404c2c66affSColin Finck return ERROR_SUCCESS; 1405c2c66affSColin Finck } 1406c2c66affSColin Finck 1407c2c66affSColin Finck static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask ) 1408c2c66affSColin Finck { 1409c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1410c2c66affSColin Finck UINT i, val, r = ERROR_SUCCESS; 1411c2c66affSColin Finck 1412c2c66affSColin Finck if ( !tv->table ) 1413c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1414c2c66affSColin Finck 1415c2c66affSColin Finck /* test if any of the mask bits are invalid */ 1416c2c66affSColin Finck if ( mask >= (1<<tv->num_cols) ) 1417c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1418c2c66affSColin Finck 1419c2c66affSColin Finck for ( i = 0; i < tv->num_cols; i++ ) 1420c2c66affSColin Finck { 1421c2c66affSColin Finck BOOL persistent; 1422c2c66affSColin Finck 1423c2c66affSColin Finck /* only update the fields specified in the mask */ 1424c2c66affSColin Finck if ( !(mask&(1<<i)) ) 1425c2c66affSColin Finck continue; 1426c2c66affSColin Finck 1427c2c66affSColin Finck persistent = (tv->table->persistent != MSICONDITION_FALSE) && 1428c2c66affSColin Finck (tv->table->data_persistent[row]); 1429c2c66affSColin Finck /* FIXME: should we allow updating keys? */ 1430c2c66affSColin Finck 1431c2c66affSColin Finck val = 0; 1432c2c66affSColin Finck if ( !MSI_RecordIsNull( rec, i + 1 ) ) 1433c2c66affSColin Finck { 1434c2c66affSColin Finck r = get_table_value_from_record (tv, rec, i + 1, &val); 1435c2c66affSColin Finck if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) ) 1436c2c66affSColin Finck { 1437c2c66affSColin Finck IStream *stm; 1438c2c66affSColin Finck LPWSTR stname; 1439c2c66affSColin Finck 1440c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1441c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1442c2c66affSColin Finck 1443c2c66affSColin Finck r = MSI_RecordGetIStream( rec, i + 1, &stm ); 1444c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1445c2c66affSColin Finck return r; 1446c2c66affSColin Finck 1447c2c66affSColin Finck r = get_stream_name( tv, row, &stname ); 1448c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1449c2c66affSColin Finck { 1450c2c66affSColin Finck IStream_Release( stm ); 1451c2c66affSColin Finck return r; 1452c2c66affSColin Finck } 1453c2c66affSColin Finck 1454c2c66affSColin Finck r = add_stream( tv->db, stname, stm ); 1455c2c66affSColin Finck IStream_Release( stm ); 1456c2c66affSColin Finck msi_free ( stname ); 1457c2c66affSColin Finck 1458c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1459c2c66affSColin Finck return r; 1460c2c66affSColin Finck } 1461c2c66affSColin Finck else if ( tv->columns[i].type & MSITYPE_STRING ) 1462c2c66affSColin Finck { 1463c2c66affSColin Finck UINT x; 1464c2c66affSColin Finck 1465c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1466c2c66affSColin Finck { 1467c2c66affSColin Finck int len; 1468c2c66affSColin Finck const WCHAR *sval = msi_record_get_string( rec, i + 1, &len ); 1469211c4104Swinesync val = msi_add_string( tv->db->strings, sval, len, persistent ); 1470c2c66affSColin Finck } 1471c2c66affSColin Finck else 1472c2c66affSColin Finck { 1473c2c66affSColin Finck TABLE_fetch_int(&tv->view, row, i + 1, &x); 1474c2c66affSColin Finck if (val == x) 1475c2c66affSColin Finck continue; 1476c2c66affSColin Finck } 1477c2c66affSColin Finck } 1478c2c66affSColin Finck else 1479c2c66affSColin Finck { 1480c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1481c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1482c2c66affSColin Finck } 1483c2c66affSColin Finck } 1484c2c66affSColin Finck 148597918c36Swinesync r = table_set_bytes( tv, row, i+1, val ); 1486c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1487c2c66affSColin Finck break; 1488c2c66affSColin Finck } 1489c2c66affSColin Finck return r; 1490c2c66affSColin Finck } 1491c2c66affSColin Finck 1492c2c66affSColin Finck static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary ) 1493c2c66affSColin Finck { 1494c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1495c2c66affSColin Finck BYTE **p, *row; 1496c2c66affSColin Finck BOOL *b; 1497c2c66affSColin Finck UINT sz; 1498c2c66affSColin Finck BYTE ***data_ptr; 1499c2c66affSColin Finck BOOL **data_persist_ptr; 1500c2c66affSColin Finck UINT *row_count; 1501c2c66affSColin Finck 1502c2c66affSColin Finck TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE"); 1503c2c66affSColin Finck 1504c2c66affSColin Finck if( !tv->table ) 1505c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1506c2c66affSColin Finck 1507c2c66affSColin Finck row = msi_alloc_zero( tv->row_size ); 1508c2c66affSColin Finck if( !row ) 1509c2c66affSColin Finck return ERROR_NOT_ENOUGH_MEMORY; 1510c2c66affSColin Finck 1511c2c66affSColin Finck row_count = &tv->table->row_count; 1512c2c66affSColin Finck data_ptr = &tv->table->data; 1513c2c66affSColin Finck data_persist_ptr = &tv->table->data_persistent; 1514c2c66affSColin Finck if (*num == -1) 1515c2c66affSColin Finck *num = tv->table->row_count; 1516c2c66affSColin Finck 1517c2c66affSColin Finck sz = (*row_count + 1) * sizeof (BYTE*); 1518c2c66affSColin Finck if( *data_ptr ) 1519c2c66affSColin Finck p = msi_realloc( *data_ptr, sz ); 1520c2c66affSColin Finck else 1521c2c66affSColin Finck p = msi_alloc( sz ); 1522c2c66affSColin Finck if( !p ) 1523c2c66affSColin Finck { 1524c2c66affSColin Finck msi_free( row ); 1525c2c66affSColin Finck return ERROR_NOT_ENOUGH_MEMORY; 1526c2c66affSColin Finck } 1527c2c66affSColin Finck 1528c2c66affSColin Finck sz = (*row_count + 1) * sizeof (BOOL); 1529c2c66affSColin Finck if( *data_persist_ptr ) 1530c2c66affSColin Finck b = msi_realloc( *data_persist_ptr, sz ); 1531c2c66affSColin Finck else 1532c2c66affSColin Finck b = msi_alloc( sz ); 1533c2c66affSColin Finck if( !b ) 1534c2c66affSColin Finck { 1535c2c66affSColin Finck msi_free( row ); 1536c2c66affSColin Finck msi_free( p ); 1537c2c66affSColin Finck return ERROR_NOT_ENOUGH_MEMORY; 1538c2c66affSColin Finck } 1539c2c66affSColin Finck 1540c2c66affSColin Finck *data_ptr = p; 1541c2c66affSColin Finck (*data_ptr)[*row_count] = row; 1542c2c66affSColin Finck 1543c2c66affSColin Finck *data_persist_ptr = b; 1544c2c66affSColin Finck (*data_persist_ptr)[*row_count] = !temporary; 1545c2c66affSColin Finck 1546c2c66affSColin Finck (*row_count)++; 1547c2c66affSColin Finck 1548c2c66affSColin Finck return ERROR_SUCCESS; 1549c2c66affSColin Finck } 1550c2c66affSColin Finck 1551c2c66affSColin Finck static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) 1552c2c66affSColin Finck { 1553c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1554c2c66affSColin Finck 1555c2c66affSColin Finck TRACE("%p %p\n", tv, record); 1556c2c66affSColin Finck 1557c2c66affSColin Finck TRACE("There are %d columns\n", tv->num_cols ); 1558c2c66affSColin Finck 1559c2c66affSColin Finck return ERROR_SUCCESS; 1560c2c66affSColin Finck } 1561c2c66affSColin Finck 1562c2c66affSColin Finck static UINT TABLE_close( struct tagMSIVIEW *view ) 1563c2c66affSColin Finck { 1564c2c66affSColin Finck TRACE("%p\n", view ); 1565c2c66affSColin Finck 1566c2c66affSColin Finck return ERROR_SUCCESS; 1567c2c66affSColin Finck } 1568c2c66affSColin Finck 1569c2c66affSColin Finck static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols) 1570c2c66affSColin Finck { 1571c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1572c2c66affSColin Finck 1573c2c66affSColin Finck TRACE("%p %p %p\n", view, rows, cols ); 1574c2c66affSColin Finck 1575c2c66affSColin Finck if( cols ) 1576c2c66affSColin Finck *cols = tv->num_cols; 1577c2c66affSColin Finck if( rows ) 1578c2c66affSColin Finck { 1579c2c66affSColin Finck if( !tv->table ) 1580c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1581c2c66affSColin Finck *rows = tv->table->row_count; 1582c2c66affSColin Finck } 1583c2c66affSColin Finck 1584c2c66affSColin Finck return ERROR_SUCCESS; 1585c2c66affSColin Finck } 1586c2c66affSColin Finck 1587c2c66affSColin Finck static UINT TABLE_get_column_info( struct tagMSIVIEW *view, 1588c2c66affSColin Finck UINT n, LPCWSTR *name, UINT *type, BOOL *temporary, 1589c2c66affSColin Finck LPCWSTR *table_name ) 1590c2c66affSColin Finck { 1591c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1592c2c66affSColin Finck 1593c2c66affSColin Finck TRACE("%p %d %p %p\n", tv, n, name, type ); 1594c2c66affSColin Finck 1595c2c66affSColin Finck if( ( n == 0 ) || ( n > tv->num_cols ) ) 1596c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1597c2c66affSColin Finck 1598c2c66affSColin Finck if( name ) 1599c2c66affSColin Finck { 1600c2c66affSColin Finck *name = tv->columns[n-1].colname; 1601c2c66affSColin Finck if( !*name ) 1602c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1603c2c66affSColin Finck } 1604c2c66affSColin Finck 1605c2c66affSColin Finck if( table_name ) 1606c2c66affSColin Finck { 1607c2c66affSColin Finck *table_name = tv->columns[n-1].tablename; 1608c2c66affSColin Finck if( !*table_name ) 1609c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1610c2c66affSColin Finck } 1611c2c66affSColin Finck 1612c2c66affSColin Finck if( type ) 1613c2c66affSColin Finck *type = tv->columns[n-1].type; 1614c2c66affSColin Finck 1615c2c66affSColin Finck if( temporary ) 1616c2c66affSColin Finck *temporary = tv->columns[n-1].temporary; 1617c2c66affSColin Finck 1618c2c66affSColin Finck return ERROR_SUCCESS; 1619c2c66affSColin Finck } 1620c2c66affSColin Finck 1621c2c66affSColin Finck static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column ); 1622c2c66affSColin Finck 1623c2c66affSColin Finck static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column ) 1624c2c66affSColin Finck { 1625c2c66affSColin Finck UINT r, row, i; 1626c2c66affSColin Finck 1627c2c66affSColin Finck /* check there are no null values where they're not allowed */ 1628c2c66affSColin Finck for( i = 0; i < tv->num_cols; i++ ) 1629c2c66affSColin Finck { 1630c2c66affSColin Finck if ( tv->columns[i].type & MSITYPE_NULLABLE ) 1631c2c66affSColin Finck continue; 1632c2c66affSColin Finck 1633c2c66affSColin Finck if ( MSITYPE_IS_BINARY(tv->columns[i].type) ) 1634c2c66affSColin Finck TRACE("skipping binary column\n"); 1635c2c66affSColin Finck else if ( tv->columns[i].type & MSITYPE_STRING ) 1636c2c66affSColin Finck { 1637c2c66affSColin Finck int len; 1638c2c66affSColin Finck const WCHAR *str = msi_record_get_string( rec, i+1, &len ); 1639c2c66affSColin Finck 1640c2c66affSColin Finck if (!str || (!str[0] && !len)) 1641c2c66affSColin Finck { 1642c2c66affSColin Finck if (column) *column = i; 1643c2c66affSColin Finck return ERROR_INVALID_DATA; 1644c2c66affSColin Finck } 1645c2c66affSColin Finck } 1646c2c66affSColin Finck else 1647c2c66affSColin Finck { 1648c2c66affSColin Finck UINT n; 1649c2c66affSColin Finck 1650c2c66affSColin Finck n = MSI_RecordGetInteger( rec, i+1 ); 1651c2c66affSColin Finck if (n == MSI_NULL_INTEGER) 1652c2c66affSColin Finck { 1653c2c66affSColin Finck if (column) *column = i; 1654c2c66affSColin Finck return ERROR_INVALID_DATA; 1655c2c66affSColin Finck } 1656c2c66affSColin Finck } 1657c2c66affSColin Finck } 1658c2c66affSColin Finck 1659c2c66affSColin Finck /* check there are no duplicate keys */ 1660c2c66affSColin Finck r = msi_table_find_row( tv, rec, &row, column ); 1661c2c66affSColin Finck if (r == ERROR_SUCCESS) 1662c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1663c2c66affSColin Finck 1664c2c66affSColin Finck return ERROR_SUCCESS; 1665c2c66affSColin Finck } 1666c2c66affSColin Finck 1667c2c66affSColin Finck static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec ) 1668c2c66affSColin Finck { 1669c2c66affSColin Finck UINT r, i, ivalue, x; 1670c2c66affSColin Finck 1671c2c66affSColin Finck for (i = 0; i < tv->num_cols; i++ ) 1672c2c66affSColin Finck { 1673c2c66affSColin Finck if (!(tv->columns[i].type & MSITYPE_KEY)) continue; 1674c2c66affSColin Finck 1675c2c66affSColin Finck r = get_table_value_from_record( tv, rec, i + 1, &ivalue ); 1676c2c66affSColin Finck if (r != ERROR_SUCCESS) 1677c2c66affSColin Finck return 1; 1678c2c66affSColin Finck 1679c2c66affSColin Finck r = TABLE_fetch_int( &tv->view, row, i + 1, &x ); 1680c2c66affSColin Finck if (r != ERROR_SUCCESS) 1681c2c66affSColin Finck { 1682c2c66affSColin Finck WARN("TABLE_fetch_int should not fail here %u\n", r); 1683c2c66affSColin Finck return -1; 1684c2c66affSColin Finck } 1685c2c66affSColin Finck if (ivalue > x) 1686c2c66affSColin Finck { 1687c2c66affSColin Finck return 1; 1688c2c66affSColin Finck } 1689c2c66affSColin Finck else if (ivalue == x) 1690c2c66affSColin Finck { 1691c2c66affSColin Finck if (i < tv->num_cols - 1) continue; 1692c2c66affSColin Finck return 0; 1693c2c66affSColin Finck } 1694c2c66affSColin Finck else 1695c2c66affSColin Finck return -1; 1696c2c66affSColin Finck } 1697c2c66affSColin Finck return 1; 1698c2c66affSColin Finck } 1699c2c66affSColin Finck 1700c2c66affSColin Finck static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec ) 1701c2c66affSColin Finck { 1702c2c66affSColin Finck int idx, c, low = 0, high = tv->table->row_count - 1; 1703c2c66affSColin Finck 1704c2c66affSColin Finck TRACE("%p %p\n", tv, rec); 1705c2c66affSColin Finck 1706c2c66affSColin Finck while (low <= high) 1707c2c66affSColin Finck { 1708c2c66affSColin Finck idx = (low + high) / 2; 1709c2c66affSColin Finck c = compare_record( tv, idx, rec ); 1710c2c66affSColin Finck 1711c2c66affSColin Finck if (c < 0) 1712c2c66affSColin Finck high = idx - 1; 1713c2c66affSColin Finck else if (c > 0) 1714c2c66affSColin Finck low = idx + 1; 1715c2c66affSColin Finck else 1716c2c66affSColin Finck { 1717c2c66affSColin Finck TRACE("found %u\n", idx); 1718c2c66affSColin Finck return idx; 1719c2c66affSColin Finck } 1720c2c66affSColin Finck } 1721c2c66affSColin Finck TRACE("found %u\n", high + 1); 1722c2c66affSColin Finck return high + 1; 1723c2c66affSColin Finck } 1724c2c66affSColin Finck 1725c2c66affSColin Finck static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary ) 1726c2c66affSColin Finck { 1727c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1728c2c66affSColin Finck UINT i, r; 1729c2c66affSColin Finck 1730c2c66affSColin Finck TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" ); 1731c2c66affSColin Finck 1732c2c66affSColin Finck /* check that the key is unique - can we find a matching row? */ 1733c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1734c2c66affSColin Finck if( r != ERROR_SUCCESS ) 1735c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1736c2c66affSColin Finck 1737c2c66affSColin Finck if (row == -1) 1738c2c66affSColin Finck row = find_insert_index( tv, rec ); 1739c2c66affSColin Finck 1740c2c66affSColin Finck r = table_create_new_row( view, &row, temporary ); 1741c2c66affSColin Finck TRACE("insert_row returned %08x\n", r); 1742c2c66affSColin Finck if( r != ERROR_SUCCESS ) 1743c2c66affSColin Finck return r; 1744c2c66affSColin Finck 1745c2c66affSColin Finck /* shift the rows to make room for the new row */ 1746c2c66affSColin Finck for (i = tv->table->row_count - 1; i > row; i--) 1747c2c66affSColin Finck { 1748c2c66affSColin Finck memmove(&(tv->table->data[i][0]), 1749c2c66affSColin Finck &(tv->table->data[i - 1][0]), tv->row_size); 1750c2c66affSColin Finck tv->table->data_persistent[i] = tv->table->data_persistent[i - 1]; 1751c2c66affSColin Finck } 1752c2c66affSColin Finck 1753c2c66affSColin Finck /* Re-set the persistence flag */ 1754c2c66affSColin Finck tv->table->data_persistent[row] = !temporary; 1755c2c66affSColin Finck return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 ); 1756c2c66affSColin Finck } 1757c2c66affSColin Finck 1758c2c66affSColin Finck static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row ) 1759c2c66affSColin Finck { 1760c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1761c2c66affSColin Finck UINT r, num_rows, num_cols, i; 1762c2c66affSColin Finck 1763c2c66affSColin Finck TRACE("%p %d\n", tv, row); 1764c2c66affSColin Finck 1765c2c66affSColin Finck if ( !tv->table ) 1766c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1767c2c66affSColin Finck 1768c2c66affSColin Finck r = TABLE_get_dimensions( view, &num_rows, &num_cols ); 1769c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 1770c2c66affSColin Finck return r; 1771c2c66affSColin Finck 1772c2c66affSColin Finck if ( row >= num_rows ) 1773c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1774c2c66affSColin Finck 1775c2c66affSColin Finck num_rows = tv->table->row_count; 1776c2c66affSColin Finck tv->table->row_count--; 1777c2c66affSColin Finck 1778c2c66affSColin Finck /* reset the hash tables */ 1779c2c66affSColin Finck for (i = 0; i < tv->num_cols; i++) 1780c2c66affSColin Finck { 1781c2c66affSColin Finck msi_free( tv->columns[i].hash_table ); 1782c2c66affSColin Finck tv->columns[i].hash_table = NULL; 1783c2c66affSColin Finck } 1784c2c66affSColin Finck 1785c2c66affSColin Finck for (i = row + 1; i < num_rows; i++) 1786c2c66affSColin Finck { 1787c2c66affSColin Finck memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size); 1788c2c66affSColin Finck tv->table->data_persistent[i - 1] = tv->table->data_persistent[i]; 1789c2c66affSColin Finck } 1790c2c66affSColin Finck 1791c2c66affSColin Finck msi_free(tv->table->data[num_rows - 1]); 1792c2c66affSColin Finck 1793c2c66affSColin Finck return ERROR_SUCCESS; 1794c2c66affSColin Finck } 1795c2c66affSColin Finck 1796c2c66affSColin Finck static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row) 1797c2c66affSColin Finck { 1798c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1799c2c66affSColin Finck UINT r, new_row; 1800c2c66affSColin Finck 1801c2c66affSColin Finck /* FIXME: MsiViewFetch should set rec index 0 to some ID that 1802c2c66affSColin Finck * sets the fetched record apart from other records 1803c2c66affSColin Finck */ 1804c2c66affSColin Finck 1805c2c66affSColin Finck if (!tv->table) 1806c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1807c2c66affSColin Finck 1808c2c66affSColin Finck r = msi_table_find_row(tv, rec, &new_row, NULL); 1809c2c66affSColin Finck if (r != ERROR_SUCCESS) 1810c2c66affSColin Finck { 1811c2c66affSColin Finck ERR("can't find row to modify\n"); 1812c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1813c2c66affSColin Finck } 1814c2c66affSColin Finck 1815c2c66affSColin Finck /* the row cannot be changed */ 1816def8c91eSwinesync if (row != new_row) 1817c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 1818c2c66affSColin Finck 1819c2c66affSColin Finck return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1); 1820c2c66affSColin Finck } 1821c2c66affSColin Finck 1822c2c66affSColin Finck static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec) 1823c2c66affSColin Finck { 1824c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW *)view; 1825c2c66affSColin Finck UINT r, row; 1826c2c66affSColin Finck 1827c2c66affSColin Finck if (!tv->table) 1828c2c66affSColin Finck return ERROR_INVALID_PARAMETER; 1829c2c66affSColin Finck 1830c2c66affSColin Finck r = msi_table_find_row(tv, rec, &row, NULL); 1831c2c66affSColin Finck if (r == ERROR_SUCCESS) 1832c2c66affSColin Finck return TABLE_set_row(view, row, rec, (1 << tv->num_cols) - 1); 1833c2c66affSColin Finck else 1834c2c66affSColin Finck return TABLE_insert_row( view, rec, -1, FALSE ); 1835c2c66affSColin Finck } 1836c2c66affSColin Finck 1837c2c66affSColin Finck static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row ) 1838c2c66affSColin Finck { 1839c2c66affSColin Finck MSIRECORD *curr; 1840c2c66affSColin Finck UINT r, i, count; 1841c2c66affSColin Finck 1842def8c91eSwinesync r = TABLE_get_row(view, row, &curr); 1843c2c66affSColin Finck if (r != ERROR_SUCCESS) 1844c2c66affSColin Finck return r; 1845c2c66affSColin Finck 1846c2c66affSColin Finck /* Close the original record */ 1847c2c66affSColin Finck MSI_CloseRecord(&rec->hdr); 1848c2c66affSColin Finck 1849c2c66affSColin Finck count = MSI_RecordGetFieldCount(rec); 1850c2c66affSColin Finck for (i = 0; i < count; i++) 1851c2c66affSColin Finck MSI_RecordCopyField(curr, i + 1, rec, i + 1); 1852c2c66affSColin Finck 1853c2c66affSColin Finck msiobj_release(&curr->hdr); 1854c2c66affSColin Finck return ERROR_SUCCESS; 1855c2c66affSColin Finck } 1856c2c66affSColin Finck 1857c2c66affSColin Finck static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, 1858c2c66affSColin Finck MSIRECORD *rec, UINT row) 1859c2c66affSColin Finck { 1860c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1861c2c66affSColin Finck UINT r, frow, column; 1862c2c66affSColin Finck 1863c2c66affSColin Finck TRACE("%p %d %p\n", view, eModifyMode, rec ); 1864c2c66affSColin Finck 1865c2c66affSColin Finck switch (eModifyMode) 1866c2c66affSColin Finck { 1867c2c66affSColin Finck case MSIMODIFY_DELETE: 18685f1570f4Swinesync r = TABLE_delete_row( view, row ); 1869c2c66affSColin Finck break; 1870c2c66affSColin Finck case MSIMODIFY_VALIDATE_NEW: 1871c2c66affSColin Finck r = table_validate_new( tv, rec, &column ); 1872c2c66affSColin Finck if (r != ERROR_SUCCESS) 1873c2c66affSColin Finck { 1874c2c66affSColin Finck tv->view.error = MSIDBERROR_DUPLICATEKEY; 1875c2c66affSColin Finck tv->view.error_column = tv->columns[column].colname; 1876c2c66affSColin Finck r = ERROR_INVALID_DATA; 1877c2c66affSColin Finck } 1878c2c66affSColin Finck break; 1879c2c66affSColin Finck 1880c2c66affSColin Finck case MSIMODIFY_INSERT: 1881c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1882c2c66affSColin Finck if (r != ERROR_SUCCESS) 1883c2c66affSColin Finck break; 1884c2c66affSColin Finck r = TABLE_insert_row( view, rec, -1, FALSE ); 1885c2c66affSColin Finck break; 1886c2c66affSColin Finck 1887c2c66affSColin Finck case MSIMODIFY_INSERT_TEMPORARY: 1888c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1889c2c66affSColin Finck if (r != ERROR_SUCCESS) 1890c2c66affSColin Finck break; 1891c2c66affSColin Finck r = TABLE_insert_row( view, rec, -1, TRUE ); 1892c2c66affSColin Finck break; 1893c2c66affSColin Finck 1894c2c66affSColin Finck case MSIMODIFY_REFRESH: 1895c2c66affSColin Finck r = msi_refresh_record( view, rec, row ); 1896c2c66affSColin Finck break; 1897c2c66affSColin Finck 1898c2c66affSColin Finck case MSIMODIFY_UPDATE: 1899c2c66affSColin Finck r = msi_table_update( view, rec, row ); 1900c2c66affSColin Finck break; 1901c2c66affSColin Finck 1902c2c66affSColin Finck case MSIMODIFY_ASSIGN: 1903c2c66affSColin Finck r = msi_table_assign( view, rec ); 1904c2c66affSColin Finck break; 1905c2c66affSColin Finck 1906c2c66affSColin Finck case MSIMODIFY_MERGE: 1907c2c66affSColin Finck /* check row that matches this record */ 1908c2c66affSColin Finck r = msi_table_find_row( tv, rec, &frow, &column ); 1909c2c66affSColin Finck if (r != ERROR_SUCCESS) 1910c2c66affSColin Finck { 1911c2c66affSColin Finck r = table_validate_new( tv, rec, NULL ); 1912c2c66affSColin Finck if (r == ERROR_SUCCESS) 1913c2c66affSColin Finck r = TABLE_insert_row( view, rec, -1, FALSE ); 1914c2c66affSColin Finck } 1915c2c66affSColin Finck break; 1916c2c66affSColin Finck 1917c2c66affSColin Finck case MSIMODIFY_REPLACE: 1918c2c66affSColin Finck case MSIMODIFY_VALIDATE: 1919c2c66affSColin Finck case MSIMODIFY_VALIDATE_FIELD: 1920c2c66affSColin Finck case MSIMODIFY_VALIDATE_DELETE: 1921c2c66affSColin Finck FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); 1922c2c66affSColin Finck r = ERROR_CALL_NOT_IMPLEMENTED; 1923c2c66affSColin Finck break; 1924c2c66affSColin Finck 1925c2c66affSColin Finck default: 1926c2c66affSColin Finck r = ERROR_INVALID_DATA; 1927c2c66affSColin Finck } 1928c2c66affSColin Finck 1929c2c66affSColin Finck return r; 1930c2c66affSColin Finck } 1931c2c66affSColin Finck 1932c2c66affSColin Finck static UINT TABLE_delete( struct tagMSIVIEW *view ) 1933c2c66affSColin Finck { 1934c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1935c2c66affSColin Finck 1936c2c66affSColin Finck TRACE("%p\n", view ); 1937c2c66affSColin Finck 1938c2c66affSColin Finck tv->table = NULL; 1939c2c66affSColin Finck tv->columns = NULL; 1940c2c66affSColin Finck 1941c2c66affSColin Finck msi_free( tv ); 1942c2c66affSColin Finck 1943c2c66affSColin Finck return ERROR_SUCCESS; 1944c2c66affSColin Finck } 1945c2c66affSColin Finck 1946c2c66affSColin Finck static UINT TABLE_add_ref(struct tagMSIVIEW *view) 1947c2c66affSColin Finck { 1948c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1949c2c66affSColin Finck 1950c2c66affSColin Finck TRACE("%p %d\n", view, tv->table->ref_count); 1951c2c66affSColin Finck return InterlockedIncrement(&tv->table->ref_count); 1952c2c66affSColin Finck } 1953c2c66affSColin Finck 1954c2c66affSColin Finck static UINT TABLE_remove_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number) 1955c2c66affSColin Finck { 1956c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 1957c2c66affSColin Finck MSIRECORD *rec = NULL; 1958c2c66affSColin Finck MSIVIEW *columns = NULL; 1959c2c66affSColin Finck UINT row, r; 1960c2c66affSColin Finck 19615c718f5fSwinesync if (tv->table->col_count != number) 19625c718f5fSwinesync return ERROR_BAD_QUERY_SYNTAX; 19635c718f5fSwinesync 19645c718f5fSwinesync if (tv->table->colinfo[number-1].temporary) 19655c718f5fSwinesync { 19665c718f5fSwinesync UINT size = tv->table->colinfo[number-1].offset; 19675c718f5fSwinesync tv->table->col_count--; 19685c718f5fSwinesync tv->table->colinfo = msi_realloc( tv->table->colinfo, sizeof(*tv->table->colinfo) * tv->table->col_count ); 19695c718f5fSwinesync 19705c718f5fSwinesync for (row = 0; row < tv->table->row_count; row++) 19715c718f5fSwinesync tv->table->data[row] = msi_realloc( tv->table->data[row], size ); 19725c718f5fSwinesync return ERROR_SUCCESS; 19735c718f5fSwinesync } 19745c718f5fSwinesync 1975c2c66affSColin Finck rec = MSI_CreateRecord(2); 1976c2c66affSColin Finck if (!rec) 1977c2c66affSColin Finck return ERROR_OUTOFMEMORY; 1978c2c66affSColin Finck 1979c2c66affSColin Finck MSI_RecordSetStringW(rec, 1, table); 1980c2c66affSColin Finck MSI_RecordSetInteger(rec, 2, number); 1981c2c66affSColin Finck 1982c2c66affSColin Finck r = TABLE_CreateView(tv->db, szColumns, &columns); 1983c2c66affSColin Finck if (r != ERROR_SUCCESS) 1984c2c66affSColin Finck { 1985c2c66affSColin Finck msiobj_release(&rec->hdr); 1986c2c66affSColin Finck return r; 1987c2c66affSColin Finck } 1988c2c66affSColin Finck 1989c2c66affSColin Finck r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL); 1990c2c66affSColin Finck if (r != ERROR_SUCCESS) 1991c2c66affSColin Finck goto done; 1992c2c66affSColin Finck 1993c2c66affSColin Finck r = TABLE_delete_row(columns, row); 1994c2c66affSColin Finck if (r != ERROR_SUCCESS) 1995c2c66affSColin Finck goto done; 1996c2c66affSColin Finck 1997c2c66affSColin Finck msi_update_table_columns(tv->db, table); 1998c2c66affSColin Finck 1999c2c66affSColin Finck done: 2000c2c66affSColin Finck msiobj_release(&rec->hdr); 2001c2c66affSColin Finck columns->ops->delete(columns); 2002c2c66affSColin Finck return r; 2003c2c66affSColin Finck } 2004c2c66affSColin Finck 2005c2c66affSColin Finck static UINT TABLE_release(struct tagMSIVIEW *view) 2006c2c66affSColin Finck { 2007c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 2008c2c66affSColin Finck INT ref = tv->table->ref_count; 20095c718f5fSwinesync UINT r; 20105c718f5fSwinesync INT i; 2011c2c66affSColin Finck 2012c2c66affSColin Finck TRACE("%p %d\n", view, ref); 2013c2c66affSColin Finck 2014eb6788c9Swinesync ref = InterlockedDecrement(&tv->table->ref_count); 2015eb6788c9Swinesync if (ref == 0) 2016eb6788c9Swinesync { 20175c718f5fSwinesync for (i = tv->table->col_count - 1; i >= 0; i--) 2018c2c66affSColin Finck { 2019c2c66affSColin Finck if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY) 2020c2c66affSColin Finck { 2021c2c66affSColin Finck r = TABLE_remove_column(view, tv->table->colinfo[i].tablename, 2022c2c66affSColin Finck tv->table->colinfo[i].number); 2023c2c66affSColin Finck if (r != ERROR_SUCCESS) 2024c2c66affSColin Finck break; 2025c2c66affSColin Finck } 2026eb6788c9Swinesync else 2027eb6788c9Swinesync { 2028eb6788c9Swinesync break; 2029c2c66affSColin Finck } 2030c2c66affSColin Finck } 2031c2c66affSColin Finck 2032eb6788c9Swinesync if (!tv->table->col_count) 2033c2c66affSColin Finck { 2034c2c66affSColin Finck list_remove(&tv->table->entry); 2035c2c66affSColin Finck free_table(tv->table); 2036c2c66affSColin Finck TABLE_delete(view); 2037c2c66affSColin Finck } 2038c2c66affSColin Finck } 2039c2c66affSColin Finck 2040c2c66affSColin Finck return ref; 2041c2c66affSColin Finck } 2042c2c66affSColin Finck 20439cf1ecf2Swinesync static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR column, 2044*791734c2Swinesync INT type, BOOL hold) 2045c2c66affSColin Finck { 20469cf1ecf2Swinesync UINT i, r, table_id, col_id, size, offset; 2047*791734c2Swinesync BOOL temporary = type & MSITYPE_TEMPORARY; 2048c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 20499cf1ecf2Swinesync MSICOLUMNINFO *colinfo; 20509cf1ecf2Swinesync 20519cf1ecf2Swinesync if (temporary && !hold && !tv->table->ref_count) 20529cf1ecf2Swinesync return ERROR_SUCCESS; 20539cf1ecf2Swinesync 20549cf1ecf2Swinesync if (!temporary && tv->table->col_count && 20559cf1ecf2Swinesync tv->table->colinfo[tv->table->col_count-1].temporary) 20569cf1ecf2Swinesync return ERROR_BAD_QUERY_SYNTAX; 20579cf1ecf2Swinesync 20589cf1ecf2Swinesync for (i = 0; i < tv->table->col_count; i++) 20599cf1ecf2Swinesync { 20609cf1ecf2Swinesync if (!wcscmp(tv->table->colinfo[i].colname, column)) 20619cf1ecf2Swinesync return ERROR_BAD_QUERY_SYNTAX; 20629cf1ecf2Swinesync } 20639cf1ecf2Swinesync 20649cf1ecf2Swinesync colinfo = msi_realloc(tv->table->colinfo, sizeof(*tv->table->colinfo) * (tv->table->col_count + 1)); 20659cf1ecf2Swinesync if (!colinfo) 20669cf1ecf2Swinesync return ERROR_OUTOFMEMORY; 20679cf1ecf2Swinesync tv->table->colinfo = colinfo; 20689cf1ecf2Swinesync 20699cf1ecf2Swinesync r = msi_string2id( tv->db->strings, tv->name, -1, &table_id ); 20709cf1ecf2Swinesync if (r != ERROR_SUCCESS) 20719cf1ecf2Swinesync return r; 20729cf1ecf2Swinesync col_id = msi_add_string( tv->db->strings, column, -1, !temporary ); 20739cf1ecf2Swinesync 20749cf1ecf2Swinesync colinfo[tv->table->col_count].tablename = msi_string_lookup( tv->db->strings, table_id, NULL ); 20759cf1ecf2Swinesync colinfo[tv->table->col_count].number = tv->table->col_count + 1; 20769cf1ecf2Swinesync colinfo[tv->table->col_count].colname = msi_string_lookup( tv->db->strings, col_id, NULL ); 20779cf1ecf2Swinesync colinfo[tv->table->col_count].type = type; 20789cf1ecf2Swinesync colinfo[tv->table->col_count].offset = 0; 20799cf1ecf2Swinesync colinfo[tv->table->col_count].hash_table = NULL; 20809cf1ecf2Swinesync colinfo[tv->table->col_count].temporary = temporary; 20819cf1ecf2Swinesync tv->table->col_count++; 20829cf1ecf2Swinesync 20839cf1ecf2Swinesync table_calc_column_offsets( tv->db, tv->table->colinfo, tv->table->col_count); 20849cf1ecf2Swinesync 20859cf1ecf2Swinesync size = msi_table_get_row_size( tv->db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES ); 20869cf1ecf2Swinesync offset = tv->table->colinfo[tv->table->col_count - 1].offset; 20879cf1ecf2Swinesync for (i = 0; i < tv->table->row_count; i++) 20889cf1ecf2Swinesync { 20899cf1ecf2Swinesync BYTE *data = msi_realloc( tv->table->data[i], size ); 20909cf1ecf2Swinesync if (!data) 20919cf1ecf2Swinesync { 20929cf1ecf2Swinesync tv->table->col_count--; 20939cf1ecf2Swinesync return ERROR_OUTOFMEMORY; 20949cf1ecf2Swinesync } 20959cf1ecf2Swinesync 20969cf1ecf2Swinesync tv->table->data[i] = data; 20979cf1ecf2Swinesync memset(data + offset, 0, size - offset); 20989cf1ecf2Swinesync } 20999cf1ecf2Swinesync 21009cf1ecf2Swinesync if (!temporary) 21019cf1ecf2Swinesync { 21029cf1ecf2Swinesync MSIVIEW *columns; 2103c2c66affSColin Finck MSIRECORD *rec; 2104c2c66affSColin Finck 2105c2c66affSColin Finck rec = MSI_CreateRecord(4); 2106c2c66affSColin Finck if (!rec) 21079cf1ecf2Swinesync { 21089cf1ecf2Swinesync tv->table->col_count--; 2109c2c66affSColin Finck return ERROR_OUTOFMEMORY; 21109cf1ecf2Swinesync } 2111c2c66affSColin Finck 21129cf1ecf2Swinesync MSI_RecordSetStringW(rec, 1, tv->name); 21139cf1ecf2Swinesync MSI_RecordSetInteger(rec, 2, tv->table->col_count); 2114c2c66affSColin Finck MSI_RecordSetStringW(rec, 3, column); 2115c2c66affSColin Finck MSI_RecordSetInteger(rec, 4, type); 2116c2c66affSColin Finck 21179cf1ecf2Swinesync r = TABLE_CreateView(tv->db, szColumns, &columns); 2118c2c66affSColin Finck if (r != ERROR_SUCCESS) 21199cf1ecf2Swinesync { 21209cf1ecf2Swinesync tv->table->col_count--; 2121c2c66affSColin Finck msiobj_release(&rec->hdr); 2122c2c66affSColin Finck return r; 2123c2c66affSColin Finck } 2124c2c66affSColin Finck 21259cf1ecf2Swinesync r = TABLE_insert_row(columns, rec, -1, FALSE); 21269cf1ecf2Swinesync columns->ops->delete(columns); 21279cf1ecf2Swinesync msiobj_release(&rec->hdr); 21289cf1ecf2Swinesync if (r != ERROR_SUCCESS) 21299cf1ecf2Swinesync { 21309cf1ecf2Swinesync tv->table->col_count--; 21319cf1ecf2Swinesync return r; 21329cf1ecf2Swinesync } 21339cf1ecf2Swinesync } 21349cf1ecf2Swinesync if (hold) 21359cf1ecf2Swinesync TABLE_add_ref(view); 21369cf1ecf2Swinesync return ERROR_SUCCESS; 21379cf1ecf2Swinesync } 21389cf1ecf2Swinesync 2139c2c66affSColin Finck static UINT TABLE_drop(struct tagMSIVIEW *view) 2140c2c66affSColin Finck { 2141c2c66affSColin Finck MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 2142c2c66affSColin Finck MSIVIEW *tables = NULL; 2143c2c66affSColin Finck MSIRECORD *rec = NULL; 2144c2c66affSColin Finck UINT r, row; 2145c2c66affSColin Finck INT i; 2146c2c66affSColin Finck 2147c2c66affSColin Finck TRACE("dropping table %s\n", debugstr_w(tv->name)); 2148c2c66affSColin Finck 2149c2c66affSColin Finck for (i = tv->table->col_count - 1; i >= 0; i--) 2150c2c66affSColin Finck { 2151c2c66affSColin Finck r = TABLE_remove_column(view, tv->table->colinfo[i].tablename, 2152c2c66affSColin Finck tv->table->colinfo[i].number); 2153c2c66affSColin Finck if (r != ERROR_SUCCESS) 2154c2c66affSColin Finck return r; 2155c2c66affSColin Finck } 2156c2c66affSColin Finck 2157c2c66affSColin Finck rec = MSI_CreateRecord(1); 2158c2c66affSColin Finck if (!rec) 2159c2c66affSColin Finck return ERROR_OUTOFMEMORY; 2160c2c66affSColin Finck 2161c2c66affSColin Finck MSI_RecordSetStringW(rec, 1, tv->name); 2162c2c66affSColin Finck 2163c2c66affSColin Finck r = TABLE_CreateView(tv->db, szTables, &tables); 2164c2c66affSColin Finck if (r != ERROR_SUCCESS) 2165c2c66affSColin Finck { 2166c2c66affSColin Finck msiobj_release(&rec->hdr); 2167c2c66affSColin Finck return r; 2168c2c66affSColin Finck } 2169c2c66affSColin Finck 2170c2c66affSColin Finck r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row, NULL); 2171c2c66affSColin Finck if (r != ERROR_SUCCESS) 2172c2c66affSColin Finck goto done; 2173c2c66affSColin Finck 2174c2c66affSColin Finck r = TABLE_delete_row(tables, row); 2175c2c66affSColin Finck if (r != ERROR_SUCCESS) 2176c2c66affSColin Finck goto done; 2177c2c66affSColin Finck 2178c2c66affSColin Finck list_remove(&tv->table->entry); 2179c2c66affSColin Finck free_table(tv->table); 2180c2c66affSColin Finck 2181c2c66affSColin Finck done: 2182c2c66affSColin Finck msiobj_release(&rec->hdr); 2183c2c66affSColin Finck tables->ops->delete(tables); 2184c2c66affSColin Finck 2185c2c66affSColin Finck return r; 2186c2c66affSColin Finck } 2187c2c66affSColin Finck 2188c2c66affSColin Finck static const MSIVIEWOPS table_ops = 2189c2c66affSColin Finck { 2190c2c66affSColin Finck TABLE_fetch_int, 2191c2c66affSColin Finck TABLE_fetch_stream, 219297918c36Swinesync TABLE_set_int, 219397918c36Swinesync TABLE_set_string, 2194580d3e1bSwinesync TABLE_set_stream, 2195c2c66affSColin Finck TABLE_set_row, 2196c2c66affSColin Finck TABLE_insert_row, 2197c2c66affSColin Finck TABLE_delete_row, 2198c2c66affSColin Finck TABLE_execute, 2199c2c66affSColin Finck TABLE_close, 2200c2c66affSColin Finck TABLE_get_dimensions, 2201c2c66affSColin Finck TABLE_get_column_info, 2202c2c66affSColin Finck TABLE_modify, 2203c2c66affSColin Finck TABLE_delete, 2204c2c66affSColin Finck TABLE_add_ref, 2205c2c66affSColin Finck TABLE_release, 2206c2c66affSColin Finck TABLE_add_column, 2207c2c66affSColin Finck NULL, 2208c2c66affSColin Finck TABLE_drop, 2209c2c66affSColin Finck }; 2210c2c66affSColin Finck 2211c2c66affSColin Finck UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) 2212c2c66affSColin Finck { 2213c2c66affSColin Finck MSITABLEVIEW *tv ; 2214c2c66affSColin Finck UINT r, sz; 2215c2c66affSColin Finck 2216c2c66affSColin Finck TRACE("%p %s %p\n", db, debugstr_w(name), view ); 2217c2c66affSColin Finck 2218958f1addSwinesync if ( !wcscmp( name, szStreams ) ) 2219c2c66affSColin Finck return STREAMS_CreateView( db, view ); 2220958f1addSwinesync else if ( !wcscmp( name, szStorages ) ) 2221c2c66affSColin Finck return STORAGES_CreateView( db, view ); 2222c2c66affSColin Finck 2223c2c66affSColin Finck sz = FIELD_OFFSET( MSITABLEVIEW, name[lstrlenW( name ) + 1] ); 2224c2c66affSColin Finck tv = msi_alloc_zero( sz ); 2225c2c66affSColin Finck if( !tv ) 2226c2c66affSColin Finck return ERROR_FUNCTION_FAILED; 2227c2c66affSColin Finck 2228c2c66affSColin Finck r = get_table( db, name, &tv->table ); 2229c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2230c2c66affSColin Finck { 2231c2c66affSColin Finck msi_free( tv ); 2232c2c66affSColin Finck WARN("table not found\n"); 2233c2c66affSColin Finck return r; 2234c2c66affSColin Finck } 2235c2c66affSColin Finck 2236c2c66affSColin Finck TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count); 2237c2c66affSColin Finck 2238c2c66affSColin Finck /* fill the structure */ 2239c2c66affSColin Finck tv->view.ops = &table_ops; 2240c2c66affSColin Finck tv->db = db; 2241c2c66affSColin Finck tv->columns = tv->table->colinfo; 2242c2c66affSColin Finck tv->num_cols = tv->table->col_count; 2243c2c66affSColin Finck tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES ); 2244c2c66affSColin Finck 2245c2c66affSColin Finck TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size ); 2246c2c66affSColin Finck 2247c2c66affSColin Finck *view = (MSIVIEW*) tv; 2248c2c66affSColin Finck lstrcpyW( tv->name, name ); 2249c2c66affSColin Finck 2250c2c66affSColin Finck return ERROR_SUCCESS; 2251c2c66affSColin Finck } 2252c2c66affSColin Finck 2253317c1f6cSwinesync static WCHAR* create_key_string(MSITABLEVIEW *tv, MSIRECORD *rec) 2254317c1f6cSwinesync { 2255317c1f6cSwinesync DWORD i, p, len, key_len = 0; 2256317c1f6cSwinesync WCHAR *key; 2257317c1f6cSwinesync 2258317c1f6cSwinesync for (i = 0; i < tv->num_cols; i++) 2259317c1f6cSwinesync { 2260317c1f6cSwinesync if (!(tv->columns[i].type & MSITYPE_KEY)) 2261317c1f6cSwinesync continue; 2262317c1f6cSwinesync if (MSI_RecordGetStringW( rec, i+1, NULL, &len ) == ERROR_SUCCESS) 2263317c1f6cSwinesync key_len += len; 2264317c1f6cSwinesync key_len++; 2265317c1f6cSwinesync } 2266317c1f6cSwinesync 2267317c1f6cSwinesync key = msi_alloc( key_len * sizeof(WCHAR) ); 2268317c1f6cSwinesync if(!key) 2269317c1f6cSwinesync return NULL; 2270317c1f6cSwinesync 2271317c1f6cSwinesync p = 0; 2272317c1f6cSwinesync for (i = 0; i < tv->num_cols; i++) 2273317c1f6cSwinesync { 2274317c1f6cSwinesync if (!(tv->columns[i].type & MSITYPE_KEY)) 2275317c1f6cSwinesync continue; 2276317c1f6cSwinesync if (p) 2277317c1f6cSwinesync key[p++] = '\t'; 2278317c1f6cSwinesync len = key_len - p; 2279317c1f6cSwinesync if (MSI_RecordGetStringW( rec, i+1, key + p, &len ) == ERROR_SUCCESS) 2280317c1f6cSwinesync p += len; 2281317c1f6cSwinesync } 2282317c1f6cSwinesync return key; 2283317c1f6cSwinesync } 2284317c1f6cSwinesync 22858e75cb8aSwinesync static UINT msi_record_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR name, UINT *len ) 22868e75cb8aSwinesync { 22878e75cb8aSwinesync UINT p = 0, l, i, r; 22888e75cb8aSwinesync 22898e75cb8aSwinesync l = wcslen( tv->name ); 22908e75cb8aSwinesync if (name && *len > l) 22918e75cb8aSwinesync memcpy(name, tv->name, l * sizeof(WCHAR)); 22928e75cb8aSwinesync p += l; 22938e75cb8aSwinesync 22948e75cb8aSwinesync for ( i = 0; i < tv->num_cols; i++ ) 22958e75cb8aSwinesync { 22968e75cb8aSwinesync if (!(tv->columns[i].type & MSITYPE_KEY)) 22978e75cb8aSwinesync continue; 22988e75cb8aSwinesync 22998e75cb8aSwinesync if (name && *len > p + 1) 23008e75cb8aSwinesync name[p] = '.'; 23018e75cb8aSwinesync p++; 23028e75cb8aSwinesync 23038e75cb8aSwinesync l = (*len > p ? *len - p : 0); 23048e75cb8aSwinesync r = MSI_RecordGetStringW( rec, i + 1, name ? name + p : NULL, &l ); 23058e75cb8aSwinesync if (r != ERROR_SUCCESS) 23068e75cb8aSwinesync return r; 23078e75cb8aSwinesync p += l; 23088e75cb8aSwinesync } 23098e75cb8aSwinesync 23108e75cb8aSwinesync if (name && *len > p) 23118e75cb8aSwinesync name[p] = 0; 23128e75cb8aSwinesync 23138e75cb8aSwinesync *len = p; 23148e75cb8aSwinesync return ERROR_SUCCESS; 23158e75cb8aSwinesync } 23168e75cb8aSwinesync 23171bbb87bbSwinesync static UINT TransformView_fetch_int( MSIVIEW *view, UINT row, UINT col, UINT *val ) 23181bbb87bbSwinesync { 231927a430bdSwinesync MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 232027a430bdSwinesync 232128c50c1aSwinesync if (!tv->table || col > tv->table->col_count) 232227a430bdSwinesync { 232327a430bdSwinesync *val = 0; 232427a430bdSwinesync return ERROR_SUCCESS; 232527a430bdSwinesync } 2326317c1f6cSwinesync return TABLE_fetch_int( view, row, col, val ); 23271bbb87bbSwinesync } 23281bbb87bbSwinesync 23291bbb87bbSwinesync static UINT TransformView_fetch_stream( MSIVIEW *view, UINT row, UINT col, IStream **stm ) 23301bbb87bbSwinesync { 233127a430bdSwinesync MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 233227a430bdSwinesync 233328c50c1aSwinesync if (!tv->table || col > tv->table->col_count) 233427a430bdSwinesync { 233527a430bdSwinesync *stm = NULL; 233627a430bdSwinesync return ERROR_SUCCESS; 233727a430bdSwinesync } 2338317c1f6cSwinesync return TABLE_fetch_stream( view, row, col, stm ); 23391bbb87bbSwinesync } 23401bbb87bbSwinesync 23411bbb87bbSwinesync static UINT TransformView_set_row( MSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask ) 23421bbb87bbSwinesync { 2343317c1f6cSwinesync static const WCHAR query_pfx[] = 2344317c1f6cSwinesync L"INSERT INTO `_TransformView` (`Table`, `Column`, `Row`, `Data`, `Current`) VALUES ('"; 2345317c1f6cSwinesync 2346317c1f6cSwinesync MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 2347317c1f6cSwinesync WCHAR buf[256], *query = buf; 2348317c1f6cSwinesync MSIRECORD *old_rec; 2349317c1f6cSwinesync MSIQUERY *q; 2350317c1f6cSwinesync WCHAR *key; 2351317c1f6cSwinesync UINT i, p, r, len, qlen; 2352317c1f6cSwinesync 2353317c1f6cSwinesync if (!wcscmp( tv->name, szColumns )) 2354317c1f6cSwinesync { 2355317c1f6cSwinesync ERR( "trying to modify existing column\n" ); 2356317c1f6cSwinesync return ERROR_INSTALL_TRANSFORM_FAILURE; 2357317c1f6cSwinesync } 2358317c1f6cSwinesync 2359317c1f6cSwinesync if (!wcscmp( tv->name, szTables )) 2360317c1f6cSwinesync { 2361317c1f6cSwinesync ERR( "trying to modify existing table\n" ); 2362317c1f6cSwinesync return ERROR_INSTALL_TRANSFORM_FAILURE; 2363317c1f6cSwinesync } 2364317c1f6cSwinesync 2365317c1f6cSwinesync key = create_key_string( tv, rec ); 2366317c1f6cSwinesync if (!key) 2367317c1f6cSwinesync return ERROR_OUTOFMEMORY; 2368317c1f6cSwinesync 2369317c1f6cSwinesync r = msi_view_get_row( tv->db, view, row, &old_rec ); 2370317c1f6cSwinesync if (r != ERROR_SUCCESS) 2371317c1f6cSwinesync old_rec = NULL; 2372317c1f6cSwinesync 2373317c1f6cSwinesync for (i = 0; i < tv->num_cols; i++) 2374317c1f6cSwinesync { 2375317c1f6cSwinesync if (!(mask & (1 << i))) 2376317c1f6cSwinesync continue; 2377317c1f6cSwinesync if (tv->columns[i].type & MSITYPE_KEY) 2378317c1f6cSwinesync continue; 2379317c1f6cSwinesync 2380317c1f6cSwinesync qlen = p = wcslen( query_pfx ); 2381317c1f6cSwinesync qlen += wcslen( tv->name ) + 3; /* strlen("','") */ 2382317c1f6cSwinesync qlen += wcslen( tv->columns[i].colname ) + 3; 2383317c1f6cSwinesync qlen += wcslen( key ) + 3; 2384317c1f6cSwinesync if (MSITYPE_IS_BINARY( tv->columns[i].type )) 2385317c1f6cSwinesync r = msi_record_stream_name( tv, rec, NULL, &len ); 2386317c1f6cSwinesync else 2387317c1f6cSwinesync r = MSI_RecordGetStringW( rec, i + 1, NULL, &len ); 2388317c1f6cSwinesync if (r != ERROR_SUCCESS) 2389317c1f6cSwinesync { 2390317c1f6cSwinesync if (old_rec) 2391317c1f6cSwinesync msiobj_release( &old_rec->hdr ); 2392317c1f6cSwinesync msi_free( key ); 2393317c1f6cSwinesync return r; 2394317c1f6cSwinesync } 2395317c1f6cSwinesync qlen += len + 3; 2396317c1f6cSwinesync if (old_rec && (r = MSI_RecordGetStringW( old_rec, i+1, NULL, &len ))) 2397317c1f6cSwinesync { 2398317c1f6cSwinesync msiobj_release( &old_rec->hdr ); 2399317c1f6cSwinesync msi_free( key ); 2400317c1f6cSwinesync return r; 2401317c1f6cSwinesync } 2402317c1f6cSwinesync qlen += len + 3; /* strlen("')") + 1 */ 2403317c1f6cSwinesync 2404317c1f6cSwinesync if (qlen > ARRAY_SIZE(buf)) 2405317c1f6cSwinesync { 2406317c1f6cSwinesync query = msi_alloc( qlen * sizeof(WCHAR) ); 2407317c1f6cSwinesync if (!query) 2408317c1f6cSwinesync { 2409317c1f6cSwinesync if (old_rec) 2410317c1f6cSwinesync msiobj_release( &old_rec->hdr ); 2411317c1f6cSwinesync msi_free( key ); 2412317c1f6cSwinesync return ERROR_OUTOFMEMORY; 2413317c1f6cSwinesync } 2414317c1f6cSwinesync } 2415317c1f6cSwinesync 2416317c1f6cSwinesync memcpy( query, query_pfx, p * sizeof(WCHAR) ); 2417317c1f6cSwinesync len = wcslen( tv->name ); 2418317c1f6cSwinesync memcpy( query + p, tv->name, len * sizeof(WCHAR) ); 2419317c1f6cSwinesync p += len; 2420317c1f6cSwinesync query[p++] = '\''; 2421317c1f6cSwinesync query[p++] = ','; 2422317c1f6cSwinesync query[p++] = '\''; 2423317c1f6cSwinesync len = wcslen( tv->columns[i].colname ); 2424317c1f6cSwinesync memcpy( query + p, tv->columns[i].colname, len * sizeof(WCHAR) ); 2425317c1f6cSwinesync p += len; 2426317c1f6cSwinesync query[p++] = '\''; 2427317c1f6cSwinesync query[p++] = ','; 2428317c1f6cSwinesync query[p++] = '\''; 2429317c1f6cSwinesync len = wcslen( key ); 2430317c1f6cSwinesync memcpy( query + p, key, len * sizeof(WCHAR) ); 2431317c1f6cSwinesync p += len; 2432317c1f6cSwinesync query[p++] = '\''; 2433317c1f6cSwinesync query[p++] = ','; 2434317c1f6cSwinesync query[p++] = '\''; 2435317c1f6cSwinesync len = qlen - p; 2436317c1f6cSwinesync if (MSITYPE_IS_BINARY( tv->columns[i].type )) 2437317c1f6cSwinesync msi_record_stream_name( tv, rec, query + p, &len ); 2438317c1f6cSwinesync else 2439317c1f6cSwinesync MSI_RecordGetStringW( rec, i + 1, query + p, &len ); 2440317c1f6cSwinesync p += len; 2441317c1f6cSwinesync query[p++] = '\''; 2442317c1f6cSwinesync query[p++] = ','; 2443317c1f6cSwinesync query[p++] = '\''; 2444317c1f6cSwinesync if (old_rec) 2445317c1f6cSwinesync { 2446317c1f6cSwinesync len = qlen - p; 2447317c1f6cSwinesync MSI_RecordGetStringW( old_rec, i + 1, query + p, &len ); 2448317c1f6cSwinesync p += len; 2449317c1f6cSwinesync } 2450317c1f6cSwinesync query[p++] = '\''; 2451317c1f6cSwinesync query[p++] = ')'; 2452317c1f6cSwinesync query[p++] = 0; 2453317c1f6cSwinesync 2454317c1f6cSwinesync r = MSI_DatabaseOpenViewW( tv->db, query, &q ); 2455317c1f6cSwinesync if (query != buf) 2456317c1f6cSwinesync msi_free( query ); 2457317c1f6cSwinesync if (r != ERROR_SUCCESS) 2458317c1f6cSwinesync { 2459317c1f6cSwinesync if (old_rec) 2460317c1f6cSwinesync msiobj_release( &old_rec->hdr ); 2461317c1f6cSwinesync msi_free( key ); 2462317c1f6cSwinesync return r; 2463317c1f6cSwinesync } 2464317c1f6cSwinesync 2465317c1f6cSwinesync r = MSI_ViewExecute( q, NULL ); 2466317c1f6cSwinesync msiobj_release( &q->hdr ); 2467317c1f6cSwinesync if (r != ERROR_SUCCESS) 2468317c1f6cSwinesync { 2469317c1f6cSwinesync if (old_rec) 2470317c1f6cSwinesync msiobj_release( &old_rec->hdr ); 2471317c1f6cSwinesync msi_free( key ); 2472317c1f6cSwinesync return r; 2473317c1f6cSwinesync } 2474317c1f6cSwinesync } 2475317c1f6cSwinesync 2476317c1f6cSwinesync if (old_rec) 2477317c1f6cSwinesync msiobj_release( &old_rec->hdr ); 2478317c1f6cSwinesync msi_free( key ); 2479317c1f6cSwinesync return ERROR_SUCCESS; 24801bbb87bbSwinesync } 24811bbb87bbSwinesync 248227a430bdSwinesync static UINT TransformView_create_table( MSITABLEVIEW *tv, MSIRECORD *rec ) 248327a430bdSwinesync { 248427a430bdSwinesync static const WCHAR query_fmt[] = 248527a430bdSwinesync L"INSERT INTO `_TransformView` (`Table`, `Column`) VALUES ('%s', 'CREATE')"; 248627a430bdSwinesync 248727a430bdSwinesync WCHAR buf[256], *query = buf; 248827a430bdSwinesync const WCHAR *name; 248927a430bdSwinesync MSIQUERY *q; 249027a430bdSwinesync int len; 249127a430bdSwinesync UINT r; 249227a430bdSwinesync 249327a430bdSwinesync name = msi_record_get_string( rec, 1, &len ); 249427a430bdSwinesync if (!name) 249527a430bdSwinesync return ERROR_INSTALL_TRANSFORM_FAILURE; 249627a430bdSwinesync 249727a430bdSwinesync len = _snwprintf( NULL, 0, query_fmt, name ) + 1; 249827a430bdSwinesync if (len > ARRAY_SIZE(buf)) 249927a430bdSwinesync { 250027a430bdSwinesync query = msi_alloc( len * sizeof(WCHAR) ); 250127a430bdSwinesync if (!query) 250227a430bdSwinesync return ERROR_OUTOFMEMORY; 250327a430bdSwinesync } 250427a430bdSwinesync swprintf( query, len, query_fmt, name ); 250527a430bdSwinesync 250627a430bdSwinesync r = MSI_DatabaseOpenViewW( tv->db, query, &q ); 250727a430bdSwinesync if (query != buf) 250827a430bdSwinesync msi_free( query ); 250927a430bdSwinesync if (r != ERROR_SUCCESS) 251027a430bdSwinesync return r; 251127a430bdSwinesync 251227a430bdSwinesync r = MSI_ViewExecute( q, NULL ); 251327a430bdSwinesync msiobj_release( &q->hdr ); 251427a430bdSwinesync return r; 251527a430bdSwinesync } 251627a430bdSwinesync 251728c50c1aSwinesync static UINT TransformView_add_column( MSITABLEVIEW *tv, MSIRECORD *rec ) 251828c50c1aSwinesync { 251928c50c1aSwinesync static const WCHAR query_pfx[] = 252028c50c1aSwinesync L"INSERT INTO `_TransformView` (`Table`, `Current`, `Column`, `Data`) VALUES ('"; 252128c50c1aSwinesync 252228c50c1aSwinesync WCHAR buf[256], *query = buf; 252328c50c1aSwinesync UINT i, p, len, r, qlen; 252428c50c1aSwinesync MSIQUERY *q; 252528c50c1aSwinesync 252628c50c1aSwinesync qlen = p = wcslen( query_pfx ); 252728c50c1aSwinesync for (i = 1; i <= 4; i++) 252828c50c1aSwinesync { 252928c50c1aSwinesync r = MSI_RecordGetStringW( rec, i, NULL, &len ); 253028c50c1aSwinesync if (r != ERROR_SUCCESS) 253128c50c1aSwinesync return r; 253228c50c1aSwinesync qlen += len + 3; /* strlen( "','" ) */ 253328c50c1aSwinesync } 253428c50c1aSwinesync 253528c50c1aSwinesync if (qlen > ARRAY_SIZE(buf)) 253628c50c1aSwinesync { 253728c50c1aSwinesync query = msi_alloc( len * sizeof(WCHAR) ); 253828c50c1aSwinesync qlen = len; 253928c50c1aSwinesync if (!query) 254028c50c1aSwinesync return ERROR_OUTOFMEMORY; 254128c50c1aSwinesync } 254228c50c1aSwinesync 254328c50c1aSwinesync memcpy( query, query_pfx, p * sizeof(WCHAR) ); 254428c50c1aSwinesync for (i = 1; i <= 4; i++) 254528c50c1aSwinesync { 254628c50c1aSwinesync len = qlen - p; 254728c50c1aSwinesync MSI_RecordGetStringW( rec, i, query + p, &len ); 254828c50c1aSwinesync p += len; 254928c50c1aSwinesync query[p++] = '\''; 255028c50c1aSwinesync if (i != 4) 255128c50c1aSwinesync { 255228c50c1aSwinesync query[p++] = ','; 255328c50c1aSwinesync query[p++] = '\''; 255428c50c1aSwinesync } 255528c50c1aSwinesync } 255628c50c1aSwinesync query[p++] = ')'; 255728c50c1aSwinesync query[p++] = 0; 255828c50c1aSwinesync 255928c50c1aSwinesync r = MSI_DatabaseOpenViewW( tv->db, query, &q ); 256028c50c1aSwinesync if (query != buf) 256128c50c1aSwinesync msi_free( query ); 256228c50c1aSwinesync if (r != ERROR_SUCCESS) 256328c50c1aSwinesync return r; 256428c50c1aSwinesync 256528c50c1aSwinesync r = MSI_ViewExecute( q, NULL ); 256628c50c1aSwinesync msiobj_release( &q->hdr ); 256728c50c1aSwinesync return r; 256828c50c1aSwinesync } 256928c50c1aSwinesync 25701bbb87bbSwinesync static UINT TransformView_insert_row( MSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary ) 25711bbb87bbSwinesync { 2572e6af2a5aSwinesync static const WCHAR query_fmt[] = 2573e6af2a5aSwinesync L"INSERT INTO `_TransformView` (`Table`, `Column`, `Row`) VALUES ('%s', 'INSERT', '%s')"; 2574e6af2a5aSwinesync 257527a430bdSwinesync MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 2576e6af2a5aSwinesync WCHAR buf[256], *query = buf; 2577e6af2a5aSwinesync MSIQUERY *q; 2578e6af2a5aSwinesync WCHAR *key; 2579e6af2a5aSwinesync int len; 2580e6af2a5aSwinesync UINT r; 258127a430bdSwinesync 258227a430bdSwinesync if (!wcscmp(tv->name, szTables)) 258327a430bdSwinesync return TransformView_create_table( tv, rec ); 258427a430bdSwinesync 258528c50c1aSwinesync if (!wcscmp(tv->name, szColumns)) 258628c50c1aSwinesync return TransformView_add_column( tv, rec ); 258728c50c1aSwinesync 2588e6af2a5aSwinesync key = create_key_string( tv, rec ); 2589e6af2a5aSwinesync if (!key) 2590e6af2a5aSwinesync return ERROR_OUTOFMEMORY; 2591e6af2a5aSwinesync 2592e6af2a5aSwinesync len = _snwprintf( NULL, 0, query_fmt, tv->name, key ) + 1; 2593e6af2a5aSwinesync if (len > ARRAY_SIZE(buf)) 2594e6af2a5aSwinesync { 2595e6af2a5aSwinesync query = msi_alloc( len * sizeof(WCHAR) ); 2596e6af2a5aSwinesync if (!query) 2597e6af2a5aSwinesync { 2598e6af2a5aSwinesync msi_free( key ); 2599e6af2a5aSwinesync return ERROR_OUTOFMEMORY; 2600e6af2a5aSwinesync } 2601e6af2a5aSwinesync } 2602e6af2a5aSwinesync swprintf( query, len, query_fmt, tv->name, key ); 2603e6af2a5aSwinesync msi_free( key ); 2604e6af2a5aSwinesync 2605e6af2a5aSwinesync r = MSI_DatabaseOpenViewW( tv->db, query, &q ); 2606e6af2a5aSwinesync if (query != buf) 2607e6af2a5aSwinesync msi_free( query ); 2608e6af2a5aSwinesync if (r != ERROR_SUCCESS) 2609e6af2a5aSwinesync return r; 2610e6af2a5aSwinesync 2611e6af2a5aSwinesync r = MSI_ViewExecute( q, NULL ); 2612e6af2a5aSwinesync msiobj_release( &q->hdr ); 2613e6af2a5aSwinesync if (r != ERROR_SUCCESS) 2614e6af2a5aSwinesync return r; 2615e6af2a5aSwinesync 2616e6af2a5aSwinesync return TransformView_set_row( view, row, rec, ~0 ); 26171bbb87bbSwinesync } 26181bbb87bbSwinesync 26199d793f29Swinesync static UINT TransformView_drop_table( MSITABLEVIEW *tv, UINT row ) 26209d793f29Swinesync { 26219d793f29Swinesync static const WCHAR query_pfx[] = L"INSERT INTO `_TransformView` ( `Table`, `Column` ) VALUES ( '"; 26229d793f29Swinesync static const WCHAR query_sfx[] = L"', 'DROP' )"; 26239d793f29Swinesync 26249d793f29Swinesync WCHAR buf[256], *query = buf; 26259d793f29Swinesync UINT r, table_id, len; 26269d793f29Swinesync const WCHAR *table; 26279d793f29Swinesync int table_len; 26289d793f29Swinesync MSIQUERY *q; 26299d793f29Swinesync 26309d793f29Swinesync r = TABLE_fetch_int( &tv->view, row, 1, &table_id ); 26319d793f29Swinesync if (r != ERROR_SUCCESS) 26329d793f29Swinesync return r; 26339d793f29Swinesync 26349d793f29Swinesync table = msi_string_lookup( tv->db->strings, table_id, &table_len ); 26359d793f29Swinesync if (!table) 26369d793f29Swinesync return ERROR_INSTALL_TRANSFORM_FAILURE; 26379d793f29Swinesync 26389d793f29Swinesync len = ARRAY_SIZE(query_pfx) - 1 + table_len + ARRAY_SIZE(query_sfx); 26399d793f29Swinesync if (len > ARRAY_SIZE(buf)) 26409d793f29Swinesync { 26419d793f29Swinesync query = msi_alloc( len * sizeof(WCHAR) ); 26429d793f29Swinesync if (!query) 26439d793f29Swinesync return ERROR_OUTOFMEMORY; 26449d793f29Swinesync } 26459d793f29Swinesync 26469d793f29Swinesync memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) ); 26479d793f29Swinesync len = ARRAY_SIZE(query_pfx) - 1; 26489d793f29Swinesync memcpy( query + len, table, table_len * sizeof(WCHAR) ); 26499d793f29Swinesync len += table_len; 26509d793f29Swinesync memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) ); 26519d793f29Swinesync 26529d793f29Swinesync r = MSI_DatabaseOpenViewW( tv->db, query, &q ); 26539d793f29Swinesync if (query != buf) 26549d793f29Swinesync msi_free( query ); 26559d793f29Swinesync if (r != ERROR_SUCCESS) 26569d793f29Swinesync return r; 26579d793f29Swinesync 26589d793f29Swinesync r = MSI_ViewExecute( q, NULL ); 26599d793f29Swinesync msiobj_release( &q->hdr ); 26609d793f29Swinesync return r; 26619d793f29Swinesync } 26629d793f29Swinesync 26631bbb87bbSwinesync static UINT TransformView_delete_row( MSIVIEW *view, UINT row ) 26641bbb87bbSwinesync { 26654048c060Swinesync static const WCHAR query_pfx[] = L"INSERT INTO `_TransformView` ( `Table`, `Column`, `Row`) VALUES ( '"; 26664048c060Swinesync static const WCHAR query_column[] = L"', 'DELETE', '"; 26674048c060Swinesync static const WCHAR query_sfx[] = L"')"; 26684048c060Swinesync 26699d793f29Swinesync MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 26704048c060Swinesync WCHAR *key, buf[256], *query = buf; 26714048c060Swinesync UINT r, len, name_len, key_len; 26724048c060Swinesync MSIRECORD *rec; 26734048c060Swinesync MSIQUERY *q; 26749d793f29Swinesync 26759d793f29Swinesync if (!wcscmp( tv->name, szColumns )) 26769d793f29Swinesync { 26779d793f29Swinesync ERR("trying to remove column\n"); 26789d793f29Swinesync return ERROR_INSTALL_TRANSFORM_FAILURE; 26799d793f29Swinesync } 26809d793f29Swinesync 26819d793f29Swinesync if (!wcscmp( tv->name, szTables )) 26829d793f29Swinesync return TransformView_drop_table( tv, row ); 26839d793f29Swinesync 26844048c060Swinesync r = msi_view_get_row( tv->db, view, row, &rec ); 26854048c060Swinesync if (r != ERROR_SUCCESS) 26864048c060Swinesync return r; 26874048c060Swinesync 26884048c060Swinesync key = create_key_string( tv, rec ); 26894048c060Swinesync msiobj_release( &rec->hdr ); 26904048c060Swinesync if (!key) 26914048c060Swinesync return ERROR_OUTOFMEMORY; 26924048c060Swinesync 26934048c060Swinesync name_len = wcslen( tv->name ); 26944048c060Swinesync key_len = wcslen( key ); 26954048c060Swinesync len = ARRAY_SIZE(query_pfx) + name_len + ARRAY_SIZE(query_column) + key_len + ARRAY_SIZE(query_sfx) - 2; 26964048c060Swinesync if (len > ARRAY_SIZE(buf)) 26974048c060Swinesync { 26984048c060Swinesync query = msi_alloc( len * sizeof(WCHAR) ); 26994048c060Swinesync if (!query) 27004048c060Swinesync { 27014048c060Swinesync msi_free( tv ); 27024048c060Swinesync msi_free( key ); 27034048c060Swinesync return ERROR_OUTOFMEMORY; 27044048c060Swinesync } 27054048c060Swinesync } 27064048c060Swinesync 27074048c060Swinesync memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) ); 27084048c060Swinesync len = ARRAY_SIZE(query_pfx) - 1; 27094048c060Swinesync memcpy( query + len, tv->name, name_len * sizeof(WCHAR) ); 27104048c060Swinesync len += name_len; 27114048c060Swinesync memcpy( query + len, query_column, ARRAY_SIZE(query_column) * sizeof(WCHAR) ); 27124048c060Swinesync len += ARRAY_SIZE(query_column) - 1; 27134048c060Swinesync memcpy( query + len, key, key_len * sizeof(WCHAR) ); 27144048c060Swinesync len += key_len; 27154048c060Swinesync memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) ); 27164048c060Swinesync msi_free( key ); 27174048c060Swinesync 27184048c060Swinesync r = MSI_DatabaseOpenViewW( tv->db, query, &q ); 27194048c060Swinesync if (query != buf) 27204048c060Swinesync msi_free( query ); 27214048c060Swinesync if (r != ERROR_SUCCESS) 27224048c060Swinesync return r; 27234048c060Swinesync 27244048c060Swinesync r = MSI_ViewExecute( q, NULL ); 27254048c060Swinesync msiobj_release( &q->hdr ); 27264048c060Swinesync return r; 27271bbb87bbSwinesync } 27281bbb87bbSwinesync 27291bbb87bbSwinesync static UINT TransformView_execute( MSIVIEW *view, MSIRECORD *record ) 27301bbb87bbSwinesync { 27311bbb87bbSwinesync return ERROR_SUCCESS; 27321bbb87bbSwinesync } 27331bbb87bbSwinesync 27341bbb87bbSwinesync static UINT TransformView_close( MSIVIEW *view ) 27351bbb87bbSwinesync { 27361bbb87bbSwinesync return ERROR_SUCCESS; 27371bbb87bbSwinesync } 27381bbb87bbSwinesync 27391bbb87bbSwinesync static UINT TransformView_get_dimensions( MSIVIEW *view, UINT *rows, UINT *cols ) 27401bbb87bbSwinesync { 2741317c1f6cSwinesync return TABLE_get_dimensions( view, rows, cols ); 27421bbb87bbSwinesync } 27431bbb87bbSwinesync 27441bbb87bbSwinesync static UINT TransformView_get_column_info( MSIVIEW *view, UINT n, LPCWSTR *name, UINT *type, 27451bbb87bbSwinesync BOOL *temporary, LPCWSTR *table_name ) 27461bbb87bbSwinesync { 2747317c1f6cSwinesync return TABLE_get_column_info( view, n, name, type, temporary, table_name ); 27481bbb87bbSwinesync } 27491bbb87bbSwinesync 27501bbb87bbSwinesync static UINT TransformView_delete( MSIVIEW *view ) 27511bbb87bbSwinesync { 275228c50c1aSwinesync MSITABLEVIEW *tv = (MSITABLEVIEW*)view; 275328c50c1aSwinesync if (!tv->table || tv->columns != tv->table->colinfo) 275428c50c1aSwinesync msi_free( tv->columns ); 27551bbb87bbSwinesync return TABLE_delete( view ); 27561bbb87bbSwinesync } 27571bbb87bbSwinesync 27581bbb87bbSwinesync static const MSIVIEWOPS transform_view_ops = 27591bbb87bbSwinesync { 27601bbb87bbSwinesync TransformView_fetch_int, 27611bbb87bbSwinesync TransformView_fetch_stream, 27621bbb87bbSwinesync NULL, 27631bbb87bbSwinesync NULL, 27641bbb87bbSwinesync NULL, 27651bbb87bbSwinesync TransformView_set_row, 27661bbb87bbSwinesync TransformView_insert_row, 27671bbb87bbSwinesync TransformView_delete_row, 27681bbb87bbSwinesync TransformView_execute, 27691bbb87bbSwinesync TransformView_close, 27701bbb87bbSwinesync TransformView_get_dimensions, 27711bbb87bbSwinesync TransformView_get_column_info, 27721bbb87bbSwinesync NULL, 27731bbb87bbSwinesync TransformView_delete, 27741bbb87bbSwinesync NULL, 27751bbb87bbSwinesync NULL, 27761bbb87bbSwinesync NULL, 27771bbb87bbSwinesync NULL, 27781bbb87bbSwinesync NULL 27791bbb87bbSwinesync }; 27801bbb87bbSwinesync 27811bbb87bbSwinesync UINT TransformView_Create( MSIDATABASE *db, string_table *st, LPCWSTR name, MSIVIEW **view ) 27821bbb87bbSwinesync { 278328c50c1aSwinesync static const WCHAR query_pfx[] = L"SELECT `Column`, `Data`, `Current` FROM `_TransformView` WHERE `Table`='"; 278428c50c1aSwinesync static const WCHAR query_sfx[] = L"' AND `Row` IS NULL AND `Current` IS NOT NULL"; 278528c50c1aSwinesync 278628c50c1aSwinesync WCHAR buf[256], *query = buf; 278728c50c1aSwinesync UINT r, len, name_len, size, add_col; 278828c50c1aSwinesync MSICOLUMNINFO *colinfo; 27891bbb87bbSwinesync MSITABLEVIEW *tv; 279028c50c1aSwinesync MSIRECORD *rec; 279128c50c1aSwinesync MSIQUERY *q; 279227a430bdSwinesync 279327a430bdSwinesync name_len = wcslen( name ); 27941bbb87bbSwinesync 27951bbb87bbSwinesync r = TABLE_CreateView( db, name, view ); 27961bbb87bbSwinesync if (r == ERROR_INVALID_PARAMETER) 27971bbb87bbSwinesync { 27981bbb87bbSwinesync /* table does not exist */ 279927a430bdSwinesync size = FIELD_OFFSET( MSITABLEVIEW, name[name_len + 1] ); 280027a430bdSwinesync tv = msi_alloc_zero( size ); 280127a430bdSwinesync if (!tv) 280227a430bdSwinesync return ERROR_OUTOFMEMORY; 280327a430bdSwinesync 280427a430bdSwinesync tv->db = db; 280527a430bdSwinesync memcpy( tv->name, name, name_len * sizeof(WCHAR) ); 280627a430bdSwinesync *view = (MSIVIEW*)tv; 28071bbb87bbSwinesync } 28081bbb87bbSwinesync else if (r != ERROR_SUCCESS) 28091bbb87bbSwinesync { 28101bbb87bbSwinesync return r; 28111bbb87bbSwinesync } 28121bbb87bbSwinesync else 28131bbb87bbSwinesync { 28141bbb87bbSwinesync tv = (MSITABLEVIEW*)*view; 28151bbb87bbSwinesync } 28161bbb87bbSwinesync 28171bbb87bbSwinesync tv->view.ops = &transform_view_ops; 281828c50c1aSwinesync 281928c50c1aSwinesync len = ARRAY_SIZE(query_pfx) + name_len + ARRAY_SIZE(query_sfx) - 1; 282028c50c1aSwinesync if (len > ARRAY_SIZE(buf)) 282128c50c1aSwinesync { 282228c50c1aSwinesync query = msi_alloc( len * sizeof(WCHAR) ); 282328c50c1aSwinesync if (!query) 282428c50c1aSwinesync { 282528c50c1aSwinesync msi_free( tv ); 282628c50c1aSwinesync return ERROR_OUTOFMEMORY; 282728c50c1aSwinesync } 282828c50c1aSwinesync } 282928c50c1aSwinesync memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) ); 283028c50c1aSwinesync len = ARRAY_SIZE(query_pfx) - 1; 283128c50c1aSwinesync memcpy( query + len, name, name_len * sizeof(WCHAR) ); 283228c50c1aSwinesync len += name_len; 283328c50c1aSwinesync memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) ); 283428c50c1aSwinesync 283528c50c1aSwinesync r = MSI_DatabaseOpenViewW( tv->db, query, &q ); 283628c50c1aSwinesync if (query != buf) 283728c50c1aSwinesync msi_free( query ); 283828c50c1aSwinesync if (r != ERROR_SUCCESS) 283928c50c1aSwinesync { 284028c50c1aSwinesync msi_free( tv ); 284128c50c1aSwinesync return r; 284228c50c1aSwinesync } 284328c50c1aSwinesync 284428c50c1aSwinesync r = MSI_ViewExecute( q, NULL ); 284528c50c1aSwinesync if (r != ERROR_SUCCESS) 284628c50c1aSwinesync { 284728c50c1aSwinesync msi_free( tv ); 284828c50c1aSwinesync return r; 284928c50c1aSwinesync } 285028c50c1aSwinesync 285128c50c1aSwinesync r = q->view->ops->get_dimensions( q->view, &add_col, NULL ); 285228c50c1aSwinesync if (r != ERROR_SUCCESS) 285328c50c1aSwinesync { 285428c50c1aSwinesync MSI_ViewClose( q ); 285528c50c1aSwinesync msiobj_release( &q->hdr ); 285628c50c1aSwinesync msi_free( tv ); 285728c50c1aSwinesync return r; 285828c50c1aSwinesync } 285928c50c1aSwinesync if (!add_col) 286028c50c1aSwinesync { 286128c50c1aSwinesync MSI_ViewClose( q ); 286228c50c1aSwinesync msiobj_release( &q->hdr ); 286328c50c1aSwinesync return ERROR_SUCCESS; 286428c50c1aSwinesync } 286528c50c1aSwinesync 286628c50c1aSwinesync colinfo = msi_alloc_zero( (add_col + tv->num_cols) * sizeof(*colinfo) ); 286728c50c1aSwinesync if (!colinfo) 286828c50c1aSwinesync { 286928c50c1aSwinesync MSI_ViewClose( q ); 287028c50c1aSwinesync msiobj_release( &q->hdr ); 287128c50c1aSwinesync msi_free( tv ); 287228c50c1aSwinesync return r; 287328c50c1aSwinesync } 287428c50c1aSwinesync 287528c50c1aSwinesync while (MSI_ViewFetch( q, &rec ) == ERROR_SUCCESS) 287628c50c1aSwinesync { 287728c50c1aSwinesync int name_len; 287828c50c1aSwinesync const WCHAR *name = msi_record_get_string( rec, 1, &name_len ); 287928c50c1aSwinesync const WCHAR *type = msi_record_get_string( rec, 2, NULL ); 288028c50c1aSwinesync UINT name_id, idx; 288128c50c1aSwinesync 288228c50c1aSwinesync idx = _wtoi( msi_record_get_string(rec, 3, NULL) ); 288328c50c1aSwinesync colinfo[idx - 1].number = idx; 288428c50c1aSwinesync colinfo[idx - 1].type = _wtoi( type ); 288528c50c1aSwinesync 288628c50c1aSwinesync r = msi_string2id( st, name, name_len, &name_id ); 288728c50c1aSwinesync if (r == ERROR_SUCCESS) 288828c50c1aSwinesync colinfo[idx - 1].colname = msi_string_lookup( st, name_id, NULL ); 288928c50c1aSwinesync else 289028c50c1aSwinesync ERR( "column name %s is not defined in strings table\n", wine_dbgstr_w(name) ); 289128c50c1aSwinesync } 289228c50c1aSwinesync MSI_ViewClose( q ); 289328c50c1aSwinesync msiobj_release( &q->hdr ); 289428c50c1aSwinesync 289528c50c1aSwinesync memcpy( colinfo, tv->columns, tv->num_cols * sizeof(*colinfo) ); 289628c50c1aSwinesync tv->columns = colinfo; 289728c50c1aSwinesync tv->num_cols += add_col; 28981bbb87bbSwinesync return ERROR_SUCCESS; 28991bbb87bbSwinesync } 29001bbb87bbSwinesync 2901c2c66affSColin Finck UINT MSI_CommitTables( MSIDATABASE *db ) 2902c2c66affSColin Finck { 2903c2c66affSColin Finck UINT r, bytes_per_strref; 2904c2c66affSColin Finck HRESULT hr; 2905c2c66affSColin Finck MSITABLE *table = NULL; 2906c2c66affSColin Finck 2907c2c66affSColin Finck TRACE("%p\n",db); 2908c2c66affSColin Finck 2909c2c66affSColin Finck r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref ); 2910c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2911c2c66affSColin Finck { 2912c2c66affSColin Finck WARN("failed to save string table r=%08x\n",r); 2913c2c66affSColin Finck return r; 2914c2c66affSColin Finck } 2915c2c66affSColin Finck 2916c2c66affSColin Finck LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry ) 2917c2c66affSColin Finck { 2918c2c66affSColin Finck r = save_table( db, table, bytes_per_strref ); 2919c2c66affSColin Finck if( r != ERROR_SUCCESS ) 2920c2c66affSColin Finck { 2921c2c66affSColin Finck WARN("failed to save table %s (r=%08x)\n", 2922c2c66affSColin Finck debugstr_w(table->name), r); 2923c2c66affSColin Finck return r; 2924c2c66affSColin Finck } 2925c2c66affSColin Finck } 2926c2c66affSColin Finck 2927c2c66affSColin Finck hr = IStorage_Commit( db->storage, 0 ); 2928c2c66affSColin Finck if (FAILED( hr )) 2929c2c66affSColin Finck { 2930c2c66affSColin Finck WARN("failed to commit changes 0x%08x\n", hr); 2931c2c66affSColin Finck r = ERROR_FUNCTION_FAILED; 2932c2c66affSColin Finck } 2933c2c66affSColin Finck return r; 2934c2c66affSColin Finck } 2935c2c66affSColin Finck 2936c2c66affSColin Finck MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table ) 2937c2c66affSColin Finck { 2938c2c66affSColin Finck MSITABLE *t; 2939c2c66affSColin Finck UINT r; 2940c2c66affSColin Finck 2941c2c66affSColin Finck TRACE("%p %s\n", db, debugstr_w(table)); 2942c2c66affSColin Finck 2943c2c66affSColin Finck if (!table) 2944c2c66affSColin Finck return MSICONDITION_ERROR; 2945c2c66affSColin Finck 2946c2c66affSColin Finck r = get_table( db, table, &t ); 2947c2c66affSColin Finck if (r != ERROR_SUCCESS) 2948c2c66affSColin Finck return MSICONDITION_NONE; 2949c2c66affSColin Finck 2950c2c66affSColin Finck return t->persistent; 2951c2c66affSColin Finck } 2952c2c66affSColin Finck 2953c2c66affSColin Finck static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes) 2954c2c66affSColin Finck { 2955c2c66affSColin Finck UINT ret = 0, i; 2956c2c66affSColin Finck 2957c2c66affSColin Finck for (i = 0; i < bytes; i++) 2958c2c66affSColin Finck ret += (data[col + i] << i * 8); 2959c2c66affSColin Finck 2960c2c66affSColin Finck return ret; 2961c2c66affSColin Finck } 2962c2c66affSColin Finck 2963c2c66affSColin Finck static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname ) 2964c2c66affSColin Finck { 29658e75cb8aSwinesync UINT r, len; 29668e75cb8aSwinesync WCHAR *name; 2967c2c66affSColin Finck 2968c2c66affSColin Finck TRACE("%p %p\n", tv, rec); 2969c2c66affSColin Finck 29708e75cb8aSwinesync r = msi_record_stream_name( tv, rec, NULL, &len ); 29718e75cb8aSwinesync if (r != ERROR_SUCCESS) 2972c2c66affSColin Finck return r; 29738e75cb8aSwinesync len++; 29748e75cb8aSwinesync 29758e75cb8aSwinesync name = msi_alloc( len * sizeof(WCHAR) ); 29768e75cb8aSwinesync if (!name) 29778e75cb8aSwinesync return ERROR_OUTOFMEMORY; 29788e75cb8aSwinesync 29798e75cb8aSwinesync r = msi_record_stream_name( tv, rec, name, &len ); 29808e75cb8aSwinesync if (r != ERROR_SUCCESS) 29818e75cb8aSwinesync { 29828e75cb8aSwinesync msi_free( name ); 29838e75cb8aSwinesync return r; 29848e75cb8aSwinesync } 29858e75cb8aSwinesync 29868e75cb8aSwinesync *pstname = encode_streamname( FALSE, name ); 29878e75cb8aSwinesync msi_free( name ); 29888e75cb8aSwinesync return ERROR_SUCCESS; 2989c2c66affSColin Finck } 2990c2c66affSColin Finck 2991c2c66affSColin Finck static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st, 2992c2c66affSColin Finck IStorage *stg, const BYTE *rawdata, UINT bytes_per_strref ) 2993c2c66affSColin Finck { 2994c2c66affSColin Finck UINT i, val, ofs = 0; 2995c2c66affSColin Finck USHORT mask; 2996c2c66affSColin Finck MSICOLUMNINFO *columns = tv->columns; 2997c2c66affSColin Finck MSIRECORD *rec; 2998c2c66affSColin Finck 2999c2c66affSColin Finck mask = rawdata[0] | (rawdata[1] << 8); 3000c2c66affSColin Finck rawdata += 2; 3001c2c66affSColin Finck 3002c2c66affSColin Finck rec = MSI_CreateRecord( tv->num_cols ); 3003c2c66affSColin Finck if( !rec ) 3004c2c66affSColin Finck return rec; 3005c2c66affSColin Finck 3006c2c66affSColin Finck TRACE("row ->\n"); 3007c2c66affSColin Finck for( i=0; i<tv->num_cols; i++ ) 3008c2c66affSColin Finck { 3009c2c66affSColin Finck if ( (mask&1) && (i>=(mask>>8)) ) 3010c2c66affSColin Finck break; 3011c2c66affSColin Finck /* all keys must be present */ 3012c2c66affSColin Finck if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) ) 3013c2c66affSColin Finck continue; 3014c2c66affSColin Finck 3015c2c66affSColin Finck if( MSITYPE_IS_BINARY(tv->columns[i].type) ) 3016c2c66affSColin Finck { 3017c2c66affSColin Finck LPWSTR encname; 3018c2c66affSColin Finck IStream *stm = NULL; 3019c2c66affSColin Finck UINT r; 3020c2c66affSColin Finck 3021c2c66affSColin Finck ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref ); 3022c2c66affSColin Finck 3023c2c66affSColin Finck r = msi_record_encoded_stream_name( tv, rec, &encname ); 3024c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 3025c2c66affSColin Finck { 3026c2c66affSColin Finck msiobj_release( &rec->hdr ); 3027c2c66affSColin Finck return NULL; 3028c2c66affSColin Finck } 3029c2c66affSColin Finck r = IStorage_OpenStream( stg, encname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm ); 3030c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 3031c2c66affSColin Finck { 3032c2c66affSColin Finck msiobj_release( &rec->hdr ); 3033c2c66affSColin Finck msi_free( encname ); 3034c2c66affSColin Finck return NULL; 3035c2c66affSColin Finck } 3036c2c66affSColin Finck 3037c2c66affSColin Finck MSI_RecordSetStream( rec, i+1, stm ); 3038c2c66affSColin Finck TRACE(" field %d [%s]\n", i+1, debugstr_w(encname)); 3039c2c66affSColin Finck msi_free( encname ); 3040c2c66affSColin Finck } 3041c2c66affSColin Finck else if( columns[i].type & MSITYPE_STRING ) 3042c2c66affSColin Finck { 3043c2c66affSColin Finck int len; 3044c2c66affSColin Finck const WCHAR *sval; 3045c2c66affSColin Finck 3046c2c66affSColin Finck val = read_raw_int(rawdata, ofs, bytes_per_strref); 3047c2c66affSColin Finck sval = msi_string_lookup( st, val, &len ); 3048c2c66affSColin Finck msi_record_set_string( rec, i+1, sval, len ); 3049c2c66affSColin Finck TRACE(" field %d [%s]\n", i+1, debugstr_wn(sval, len)); 3050c2c66affSColin Finck ofs += bytes_per_strref; 3051c2c66affSColin Finck } 3052c2c66affSColin Finck else 3053c2c66affSColin Finck { 3054c2c66affSColin Finck UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref ); 3055c2c66affSColin Finck switch( n ) 3056c2c66affSColin Finck { 3057c2c66affSColin Finck case 2: 3058c2c66affSColin Finck val = read_raw_int(rawdata, ofs, n); 3059c2c66affSColin Finck if (val) 3060c2c66affSColin Finck MSI_RecordSetInteger( rec, i+1, val-0x8000 ); 3061c2c66affSColin Finck TRACE(" field %d [0x%04x]\n", i+1, val ); 3062c2c66affSColin Finck break; 3063c2c66affSColin Finck case 4: 3064c2c66affSColin Finck val = read_raw_int(rawdata, ofs, n); 3065c2c66affSColin Finck if (val) 3066c2c66affSColin Finck MSI_RecordSetInteger( rec, i+1, val^0x80000000 ); 3067c2c66affSColin Finck TRACE(" field %d [0x%08x]\n", i+1, val ); 3068c2c66affSColin Finck break; 3069c2c66affSColin Finck default: 3070c2c66affSColin Finck ERR("oops - unknown column width %d\n", n); 3071c2c66affSColin Finck break; 3072c2c66affSColin Finck } 3073c2c66affSColin Finck ofs += n; 3074c2c66affSColin Finck } 3075c2c66affSColin Finck } 3076c2c66affSColin Finck return rec; 3077c2c66affSColin Finck } 3078c2c66affSColin Finck 3079c2c66affSColin Finck static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize ) 3080c2c66affSColin Finck { 3081c2c66affSColin Finck UINT i; 3082c2c66affSColin Finck for (i = 0; i < rawsize / 2; i++) 3083c2c66affSColin Finck { 3084c2c66affSColin Finck int len; 3085c2c66affSColin Finck const WCHAR *sval = msi_string_lookup( st, rawdata[i], &len ); 3086c2c66affSColin Finck MESSAGE(" %04x %s\n", rawdata[i], debugstr_wn(sval, len) ); 3087c2c66affSColin Finck } 3088c2c66affSColin Finck } 3089c2c66affSColin Finck 3090c2c66affSColin Finck static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec ) 3091c2c66affSColin Finck { 3092c2c66affSColin Finck UINT i, r, *data; 3093c2c66affSColin Finck 3094c2c66affSColin Finck data = msi_alloc( tv->num_cols *sizeof (UINT) ); 3095c2c66affSColin Finck for( i=0; i<tv->num_cols; i++ ) 3096c2c66affSColin Finck { 3097c2c66affSColin Finck data[i] = 0; 3098c2c66affSColin Finck 3099c2c66affSColin Finck if ( ~tv->columns[i].type & MSITYPE_KEY ) 3100c2c66affSColin Finck continue; 3101c2c66affSColin Finck 3102c2c66affSColin Finck /* turn the transform column value into a row value */ 3103c2c66affSColin Finck if ( ( tv->columns[i].type & MSITYPE_STRING ) && 3104c2c66affSColin Finck ! MSITYPE_IS_BINARY(tv->columns[i].type) ) 3105c2c66affSColin Finck { 3106c2c66affSColin Finck int len; 3107c2c66affSColin Finck const WCHAR *str = msi_record_get_string( rec, i+1, &len ); 3108c2c66affSColin Finck if (str) 3109c2c66affSColin Finck { 3110c2c66affSColin Finck r = msi_string2id( tv->db->strings, str, len, &data[i] ); 3111c2c66affSColin Finck 3112c2c66affSColin Finck /* if there's no matching string in the string table, 3113c2c66affSColin Finck these keys can't match any record, so fail now. */ 3114c2c66affSColin Finck if (r != ERROR_SUCCESS) 3115c2c66affSColin Finck { 3116c2c66affSColin Finck msi_free( data ); 3117c2c66affSColin Finck return NULL; 3118c2c66affSColin Finck } 3119c2c66affSColin Finck } 3120c2c66affSColin Finck else data[i] = 0; 3121c2c66affSColin Finck } 3122c2c66affSColin Finck else 3123c2c66affSColin Finck { 31240e83dd03Swinesync if (int_to_table_storage( tv, i + 1, MSI_RecordGetInteger( rec, i + 1 ), &data[i] )) 31250e83dd03Swinesync { 31260e83dd03Swinesync msi_free( data ); 31270e83dd03Swinesync return NULL; 31280e83dd03Swinesync } 3129c2c66affSColin Finck } 3130c2c66affSColin Finck } 3131c2c66affSColin Finck return data; 3132c2c66affSColin Finck } 3133c2c66affSColin Finck 3134c2c66affSColin Finck static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column ) 3135c2c66affSColin Finck { 3136c2c66affSColin Finck UINT i, r, x, ret = ERROR_FUNCTION_FAILED; 3137c2c66affSColin Finck 3138c2c66affSColin Finck for( i=0; i<tv->num_cols; i++ ) 3139c2c66affSColin Finck { 3140c2c66affSColin Finck if ( ~tv->columns[i].type & MSITYPE_KEY ) 3141c2c66affSColin Finck continue; 3142c2c66affSColin Finck 3143c2c66affSColin Finck /* turn the transform column value into a row value */ 3144c2c66affSColin Finck r = TABLE_fetch_int( &tv->view, row, i+1, &x ); 3145c2c66affSColin Finck if ( r != ERROR_SUCCESS ) 3146c2c66affSColin Finck { 3147c2c66affSColin Finck ERR("TABLE_fetch_int shouldn't fail here\n"); 3148c2c66affSColin Finck break; 3149c2c66affSColin Finck } 3150c2c66affSColin Finck 3151c2c66affSColin Finck /* if this key matches, move to the next column */ 3152c2c66affSColin Finck if ( x != data[i] ) 3153c2c66affSColin Finck { 3154c2c66affSColin Finck ret = ERROR_FUNCTION_FAILED; 3155c2c66affSColin Finck break; 3156c2c66affSColin Finck } 3157c2c66affSColin Finck if (column) *column = i; 3158c2c66affSColin Finck ret = ERROR_SUCCESS; 3159c2c66affSColin Finck } 3160c2c66affSColin Finck return ret; 3161c2c66affSColin Finck } 3162c2c66affSColin Finck 3163c2c66affSColin Finck static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column ) 3164c2c66affSColin Finck { 3165c2c66affSColin Finck UINT i, r = ERROR_FUNCTION_FAILED, *data; 3166c2c66affSColin Finck 3167c2c66affSColin Finck data = msi_record_to_row( tv, rec ); 3168c2c66affSColin Finck if( !data ) 3169c2c66affSColin Finck return r; 3170c2c66affSColin Finck for( i = 0; i < tv->table->row_count; i++ ) 3171c2c66affSColin Finck { 3172c2c66affSColin Finck r = msi_row_matches( tv, i, data, column ); 3173c2c66affSColin Finck if( r == ERROR_SUCCESS ) 3174c2c66affSColin Finck { 3175c2c66affSColin Finck *row = i; 3176c2c66affSColin Finck break; 3177c2c66affSColin Finck } 3178c2c66affSColin Finck } 3179c2c66affSColin Finck msi_free( data ); 3180c2c66affSColin Finck return r; 3181c2c66affSColin Finck } 3182c2c66affSColin Finck 3183c2c66affSColin Finck typedef struct 3184c2c66affSColin Finck { 3185c2c66affSColin Finck struct list entry; 3186c2c66affSColin Finck LPWSTR name; 3187c2c66affSColin Finck } TRANSFORMDATA; 3188c2c66affSColin Finck 3189c2c66affSColin Finck static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, 3190c2c66affSColin Finck string_table *st, TRANSFORMDATA *transform, 31911bbb87bbSwinesync UINT bytes_per_strref, int err_cond ) 3192c2c66affSColin Finck { 3193c2c66affSColin Finck BYTE *rawdata = NULL; 3194c2c66affSColin Finck MSITABLEVIEW *tv = NULL; 3195c2c66affSColin Finck UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0; 3196c2c66affSColin Finck MSIRECORD *rec = NULL; 3197c2c66affSColin Finck WCHAR coltable[32]; 3198c2c66affSColin Finck const WCHAR *name; 3199c2c66affSColin Finck 3200c2c66affSColin Finck if (!transform) 3201c2c66affSColin Finck return ERROR_SUCCESS; 3202c2c66affSColin Finck 3203c2c66affSColin Finck name = transform->name; 3204c2c66affSColin Finck 3205c2c66affSColin Finck coltable[0] = 0; 3206c2c66affSColin Finck TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) ); 3207c2c66affSColin Finck 3208c2c66affSColin Finck /* read the transform data */ 3209c2c66affSColin Finck read_stream_data( stg, name, TRUE, &rawdata, &rawsize ); 3210c2c66affSColin Finck if ( !rawdata ) 3211c2c66affSColin Finck { 3212c2c66affSColin Finck TRACE("table %s empty\n", debugstr_w(name) ); 3213c2c66affSColin Finck return ERROR_INVALID_TABLE; 3214c2c66affSColin Finck } 3215c2c66affSColin Finck 3216c2c66affSColin Finck /* create a table view */ 32171bbb87bbSwinesync if ( err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM ) 32181bbb87bbSwinesync r = TransformView_Create( db, st, name, (MSIVIEW**) &tv ); 32191bbb87bbSwinesync else 3220c2c66affSColin Finck r = TABLE_CreateView( db, name, (MSIVIEW**) &tv ); 3221c2c66affSColin Finck if( r != ERROR_SUCCESS ) 3222c2c66affSColin Finck goto err; 3223c2c66affSColin Finck 3224c2c66affSColin Finck r = tv->view.ops->execute( &tv->view, NULL ); 3225c2c66affSColin Finck if( r != ERROR_SUCCESS ) 3226c2c66affSColin Finck goto err; 3227c2c66affSColin Finck 3228c2c66affSColin Finck TRACE("name = %s columns = %u row_size = %u raw size = %u\n", 3229c2c66affSColin Finck debugstr_w(name), tv->num_cols, tv->row_size, rawsize ); 3230c2c66affSColin Finck 3231c2c66affSColin Finck /* interpret the data */ 3232c2c66affSColin Finck for (n = 0; n < rawsize;) 3233c2c66affSColin Finck { 3234c2c66affSColin Finck mask = rawdata[n] | (rawdata[n + 1] << 8); 3235c2c66affSColin Finck if (mask & 1) 3236c2c66affSColin Finck { 3237c2c66affSColin Finck /* 3238c2c66affSColin Finck * if the low bit is set, columns are continuous and 3239c2c66affSColin Finck * the number of columns is specified in the high byte 3240c2c66affSColin Finck */ 3241c2c66affSColin Finck sz = 2; 3242c2c66affSColin Finck num_cols = mask >> 8; 3243c2c66affSColin Finck if (num_cols > tv->num_cols) 3244c2c66affSColin Finck { 3245c2c66affSColin Finck ERR("excess columns in transform: %u > %u\n", num_cols, tv->num_cols); 3246c2c66affSColin Finck break; 3247c2c66affSColin Finck } 3248c2c66affSColin Finck 3249c2c66affSColin Finck for (i = 0; i < num_cols; i++) 3250c2c66affSColin Finck { 3251c2c66affSColin Finck if( (tv->columns[i].type & MSITYPE_STRING) && 3252c2c66affSColin Finck ! MSITYPE_IS_BINARY(tv->columns[i].type) ) 3253c2c66affSColin Finck sz += bytes_per_strref; 3254c2c66affSColin Finck else 3255c2c66affSColin Finck sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); 3256c2c66affSColin Finck } 3257c2c66affSColin Finck } 3258c2c66affSColin Finck else 3259c2c66affSColin Finck { 3260c2c66affSColin Finck /* 3261c2c66affSColin Finck * If the low bit is not set, mask is a bitmask. 3262c2c66affSColin Finck * Excepting for key fields, which are always present, 3263c2c66affSColin Finck * each bit indicates that a field is present in the transform record. 3264c2c66affSColin Finck * 3265c2c66affSColin Finck * mask == 0 is a special case ... only the keys will be present 3266c2c66affSColin Finck * and it means that this row should be deleted. 3267c2c66affSColin Finck */ 3268c2c66affSColin Finck sz = 2; 3269c2c66affSColin Finck num_cols = tv->num_cols; 3270c2c66affSColin Finck for (i = 0; i < num_cols; i++) 3271c2c66affSColin Finck { 3272c2c66affSColin Finck if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask)) 3273c2c66affSColin Finck { 3274c2c66affSColin Finck if ((tv->columns[i].type & MSITYPE_STRING) && 3275c2c66affSColin Finck !MSITYPE_IS_BINARY(tv->columns[i].type)) 3276c2c66affSColin Finck sz += bytes_per_strref; 3277c2c66affSColin Finck else 3278c2c66affSColin Finck sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref ); 3279c2c66affSColin Finck } 3280c2c66affSColin Finck } 3281c2c66affSColin Finck } 3282c2c66affSColin Finck 3283c2c66affSColin Finck /* check we didn't run of the end of the table */ 3284c2c66affSColin Finck if (n + sz > rawsize) 3285c2c66affSColin Finck { 3286c2c66affSColin Finck ERR("borked.\n"); 3287c2c66affSColin Finck dump_table( st, (USHORT *)rawdata, rawsize ); 3288c2c66affSColin Finck break; 3289c2c66affSColin Finck } 3290c2c66affSColin Finck 3291c2c66affSColin Finck rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref ); 3292c2c66affSColin Finck if (rec) 3293c2c66affSColin Finck { 3294c2c66affSColin Finck WCHAR table[32]; 3295c2c66affSColin Finck DWORD sz = 32; 3296c2c66affSColin Finck UINT number = MSI_NULL_INTEGER; 3297c2c66affSColin Finck UINT row = 0; 3298c2c66affSColin Finck 3299958f1addSwinesync if (!wcscmp( name, szColumns )) 3300c2c66affSColin Finck { 3301c2c66affSColin Finck MSI_RecordGetStringW( rec, 1, table, &sz ); 3302c2c66affSColin Finck number = MSI_RecordGetInteger( rec, 2 ); 3303c2c66affSColin Finck 3304c2c66affSColin Finck /* 3305c2c66affSColin Finck * Native msi seems writes nul into the Number (2nd) column of 3306c2c66affSColin Finck * the _Columns table when there are new columns 3307c2c66affSColin Finck */ 3308c2c66affSColin Finck if ( number == MSI_NULL_INTEGER ) 3309c2c66affSColin Finck { 3310c2c66affSColin Finck /* reset the column number on a new table */ 3311958f1addSwinesync if (wcscmp( coltable, table )) 3312c2c66affSColin Finck { 3313c2c66affSColin Finck colcol = 0; 3314c2c66affSColin Finck lstrcpyW( coltable, table ); 3315c2c66affSColin Finck } 3316c2c66affSColin Finck 3317c2c66affSColin Finck /* fix nul column numbers */ 3318c2c66affSColin Finck MSI_RecordSetInteger( rec, 2, ++colcol ); 3319c2c66affSColin Finck } 3320c2c66affSColin Finck } 3321c2c66affSColin Finck 3322c2c66affSColin Finck if (TRACE_ON(msidb)) dump_record( rec ); 3323c2c66affSColin Finck 332427a430bdSwinesync if (tv->table) 3325c2c66affSColin Finck r = msi_table_find_row( tv, rec, &row, NULL ); 332627a430bdSwinesync else 332727a430bdSwinesync r = ERROR_FUNCTION_FAILED; 3328c2c66affSColin Finck if (r == ERROR_SUCCESS) 3329c2c66affSColin Finck { 3330c2c66affSColin Finck if (!mask) 3331c2c66affSColin Finck { 3332c2c66affSColin Finck TRACE("deleting row [%d]:\n", row); 33331bbb87bbSwinesync r = tv->view.ops->delete_row( &tv->view, row ); 3334c2c66affSColin Finck if (r != ERROR_SUCCESS) 3335c2c66affSColin Finck WARN("failed to delete row %u\n", r); 3336c2c66affSColin Finck } 3337c2c66affSColin Finck else if (mask & 1) 3338c2c66affSColin Finck { 3339c2c66affSColin Finck TRACE("modifying full row [%d]:\n", row); 33401bbb87bbSwinesync r = tv->view.ops->set_row( &tv->view, row, rec, (1 << tv->num_cols) - 1 ); 3341c2c66affSColin Finck if (r != ERROR_SUCCESS) 3342c2c66affSColin Finck WARN("failed to modify row %u\n", r); 3343c2c66affSColin Finck } 3344c2c66affSColin Finck else 3345c2c66affSColin Finck { 3346c2c66affSColin Finck TRACE("modifying masked row [%d]:\n", row); 33471bbb87bbSwinesync r = tv->view.ops->set_row( &tv->view, row, rec, mask ); 3348c2c66affSColin Finck if (r != ERROR_SUCCESS) 3349c2c66affSColin Finck WARN("failed to modify row %u\n", r); 3350c2c66affSColin Finck } 3351c2c66affSColin Finck } 3352c2c66affSColin Finck else 3353c2c66affSColin Finck { 3354c2c66affSColin Finck TRACE("inserting row\n"); 33551bbb87bbSwinesync r = tv->view.ops->insert_row( &tv->view, rec, -1, FALSE ); 3356c2c66affSColin Finck if (r != ERROR_SUCCESS) 3357c2c66affSColin Finck WARN("failed to insert row %u\n", r); 3358c2c66affSColin Finck } 3359c2c66affSColin Finck 33601bbb87bbSwinesync if (!(err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM) && 33611bbb87bbSwinesync !wcscmp( name, szColumns )) 3362c2c66affSColin Finck msi_update_table_columns( db, table ); 3363c2c66affSColin Finck 3364c2c66affSColin Finck msiobj_release( &rec->hdr ); 3365c2c66affSColin Finck } 3366c2c66affSColin Finck 3367c2c66affSColin Finck n += sz; 3368c2c66affSColin Finck } 3369c2c66affSColin Finck 3370c2c66affSColin Finck err: 3371c2c66affSColin Finck /* no need to free the table, it's associated with the database */ 3372c2c66affSColin Finck msi_free( rawdata ); 3373c2c66affSColin Finck if( tv ) 3374c2c66affSColin Finck tv->view.ops->delete( &tv->view ); 3375c2c66affSColin Finck 3376c2c66affSColin Finck return ERROR_SUCCESS; 3377c2c66affSColin Finck } 3378c2c66affSColin Finck 3379c2c66affSColin Finck /* 3380c2c66affSColin Finck * msi_table_apply_transform 3381c2c66affSColin Finck * 3382c2c66affSColin Finck * Enumerate the table transforms in a transform storage and apply each one. 3383c2c66affSColin Finck */ 33841bbb87bbSwinesync UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg, int err_cond ) 3385c2c66affSColin Finck { 3386c2c66affSColin Finck struct list transforms; 3387c2c66affSColin Finck IEnumSTATSTG *stgenum = NULL; 3388c2c66affSColin Finck TRANSFORMDATA *transform; 3389c2c66affSColin Finck TRANSFORMDATA *tables = NULL, *columns = NULL; 3390c2c66affSColin Finck HRESULT hr; 3391c2c66affSColin Finck STATSTG stat; 3392c2c66affSColin Finck string_table *strings; 3393c2c66affSColin Finck UINT ret = ERROR_FUNCTION_FAILED; 3394c2c66affSColin Finck UINT bytes_per_strref; 3395c2c66affSColin Finck BOOL property_update = FALSE; 33961bbb87bbSwinesync BOOL free_transform_view = FALSE; 3397c2c66affSColin Finck 3398c2c66affSColin Finck TRACE("%p %p\n", db, stg ); 3399c2c66affSColin Finck 3400c2c66affSColin Finck strings = msi_load_string_table( stg, &bytes_per_strref ); 3401c2c66affSColin Finck if( !strings ) 3402c2c66affSColin Finck goto end; 3403c2c66affSColin Finck 3404c2c66affSColin Finck hr = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); 3405c2c66affSColin Finck if (FAILED( hr )) 3406c2c66affSColin Finck goto end; 3407c2c66affSColin Finck 3408c2c66affSColin Finck list_init(&transforms); 3409c2c66affSColin Finck 3410c2c66affSColin Finck while ( TRUE ) 3411c2c66affSColin Finck { 3412c2c66affSColin Finck MSITABLEVIEW *tv = NULL; 3413c2c66affSColin Finck WCHAR name[0x40]; 3414c2c66affSColin Finck ULONG count = 0; 3415c2c66affSColin Finck 3416c2c66affSColin Finck hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); 3417c2c66affSColin Finck if (FAILED( hr ) || !count) 3418c2c66affSColin Finck break; 3419c2c66affSColin Finck 3420c2c66affSColin Finck decode_streamname( stat.pwcsName, name ); 3421c2c66affSColin Finck CoTaskMemFree( stat.pwcsName ); 3422c2c66affSColin Finck if ( name[0] != 0x4840 ) 3423c2c66affSColin Finck continue; 3424c2c66affSColin Finck 3425958f1addSwinesync if ( !wcscmp( name+1, szStringPool ) || 3426958f1addSwinesync !wcscmp( name+1, szStringData ) ) 3427c2c66affSColin Finck continue; 3428c2c66affSColin Finck 3429c2c66affSColin Finck transform = msi_alloc_zero( sizeof(TRANSFORMDATA) ); 3430c2c66affSColin Finck if ( !transform ) 3431c2c66affSColin Finck break; 3432c2c66affSColin Finck 3433c2c66affSColin Finck list_add_tail( &transforms, &transform->entry ); 3434c2c66affSColin Finck 3435c2c66affSColin Finck transform->name = strdupW( name + 1 ); 3436c2c66affSColin Finck 3437958f1addSwinesync if ( !wcscmp( transform->name, szTables ) ) 3438c2c66affSColin Finck tables = transform; 3439958f1addSwinesync else if (!wcscmp( transform->name, szColumns ) ) 3440c2c66affSColin Finck columns = transform; 3441958f1addSwinesync else if (!wcscmp( transform->name, szProperty )) 3442c2c66affSColin Finck property_update = TRUE; 3443c2c66affSColin Finck 3444c2c66affSColin Finck TRACE("transform contains stream %s\n", debugstr_w(name)); 3445c2c66affSColin Finck 3446c2c66affSColin Finck /* load the table */ 3447c2c66affSColin Finck if (TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv ) != ERROR_SUCCESS) 3448c2c66affSColin Finck continue; 3449c2c66affSColin Finck 3450c2c66affSColin Finck if (tv->view.ops->execute( &tv->view, NULL ) != ERROR_SUCCESS) 3451c2c66affSColin Finck { 3452c2c66affSColin Finck tv->view.ops->delete( &tv->view ); 3453c2c66affSColin Finck continue; 3454c2c66affSColin Finck } 3455c2c66affSColin Finck 3456c2c66affSColin Finck tv->view.ops->delete( &tv->view ); 3457c2c66affSColin Finck } 3458c2c66affSColin Finck 34591bbb87bbSwinesync if (err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM) 34601bbb87bbSwinesync { 34611bbb87bbSwinesync static const WCHAR create_query[] = L"CREATE TABLE `_TransformView` ( " 34621bbb87bbSwinesync L"`Table` CHAR(0) NOT NULL TEMPORARY, `Column` CHAR(0) NOT NULL TEMPORARY, " 34631bbb87bbSwinesync L"`Row` CHAR(0) TEMPORARY, `Data` CHAR(0) TEMPORARY, `Current` CHAR(0) TEMPORARY " 34641bbb87bbSwinesync L"PRIMARY KEY `Table`, `Column`, `Row` ) HOLD"; 34651bbb87bbSwinesync MSIQUERY *query; 34661bbb87bbSwinesync UINT r; 34671bbb87bbSwinesync 34681bbb87bbSwinesync r = MSI_DatabaseOpenViewW( db, create_query, &query ); 34691bbb87bbSwinesync if (r != ERROR_SUCCESS) 34701bbb87bbSwinesync goto end; 34711bbb87bbSwinesync 34721bbb87bbSwinesync r = MSI_ViewExecute( query, NULL ); 34731bbb87bbSwinesync if (r == ERROR_SUCCESS) 34741bbb87bbSwinesync MSI_ViewClose( query ); 34751bbb87bbSwinesync msiobj_release( &query->hdr ); 34761bbb87bbSwinesync if (r == ERROR_BAD_QUERY_SYNTAX) 34771bbb87bbSwinesync FIXME( "support adding to _TransformView\n" ); 34781bbb87bbSwinesync if (r != ERROR_SUCCESS) 34791bbb87bbSwinesync goto end; 34801bbb87bbSwinesync free_transform_view = TRUE; 34811bbb87bbSwinesync } 34821bbb87bbSwinesync 3483c2c66affSColin Finck /* 3484c2c66affSColin Finck * Apply _Tables and _Columns transforms first so that 3485c2c66affSColin Finck * the table metadata is correct, and empty tables exist. 3486c2c66affSColin Finck */ 34871bbb87bbSwinesync ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref, err_cond ); 3488c2c66affSColin Finck if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) 3489c2c66affSColin Finck goto end; 3490c2c66affSColin Finck 34911bbb87bbSwinesync ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref, err_cond ); 3492c2c66affSColin Finck if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE) 3493c2c66affSColin Finck goto end; 3494c2c66affSColin Finck 3495c2c66affSColin Finck ret = ERROR_SUCCESS; 3496c2c66affSColin Finck 3497c2c66affSColin Finck while ( !list_empty( &transforms ) ) 3498c2c66affSColin Finck { 3499c2c66affSColin Finck transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry ); 3500c2c66affSColin Finck 3501958f1addSwinesync if ( wcscmp( transform->name, szColumns ) && 3502958f1addSwinesync wcscmp( transform->name, szTables ) && 3503c2c66affSColin Finck ret == ERROR_SUCCESS ) 3504c2c66affSColin Finck { 35051bbb87bbSwinesync ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref, err_cond ); 3506c2c66affSColin Finck } 3507c2c66affSColin Finck 3508c2c66affSColin Finck list_remove( &transform->entry ); 3509c2c66affSColin Finck msi_free( transform->name ); 3510c2c66affSColin Finck msi_free( transform ); 3511c2c66affSColin Finck } 3512c2c66affSColin Finck 3513c2c66affSColin Finck if ( ret == ERROR_SUCCESS ) 3514c2c66affSColin Finck { 3515c2c66affSColin Finck append_storage_to_db( db, stg ); 3516c2c66affSColin Finck if (property_update) msi_clone_properties( db ); 3517c2c66affSColin Finck } 3518c2c66affSColin Finck 3519c2c66affSColin Finck end: 3520c2c66affSColin Finck if ( stgenum ) 3521c2c66affSColin Finck IEnumSTATSTG_Release( stgenum ); 3522c2c66affSColin Finck if ( strings ) 3523c2c66affSColin Finck msi_destroy_stringtable( strings ); 35241bbb87bbSwinesync if (ret != ERROR_SUCCESS && free_transform_view) 35251bbb87bbSwinesync { 35261bbb87bbSwinesync MSIQUERY *query; 35271bbb87bbSwinesync if (MSI_DatabaseOpenViewW( db, L"ALTER TABLE `_TransformView` FREE", 35281bbb87bbSwinesync &query ) == ERROR_SUCCESS) 35291bbb87bbSwinesync { 35301bbb87bbSwinesync if (MSI_ViewExecute( query, NULL ) == ERROR_SUCCESS) 35311bbb87bbSwinesync MSI_ViewClose( query ); 35321bbb87bbSwinesync msiobj_release( &query->hdr ); 35331bbb87bbSwinesync } 35341bbb87bbSwinesync } 3535c2c66affSColin Finck 3536c2c66affSColin Finck return ret; 3537c2c66affSColin Finck } 3538