d2d1/tests: Add hwnd render target pixel format tests.
[wine.git] / dlls / msi / table.c
blob8168e2f837db36d6b68b4307fe7f0d6902a0ad0a
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002-2005 Mike McCormack for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <assert.h>
24 #define COBJMACROS
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "msi.h"
30 #include "msiquery.h"
31 #include "objbase.h"
32 #include "objidl.h"
33 #include "winnls.h"
34 #include "msipriv.h"
35 #include "query.h"
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
41 #define MSITABLE_HASH_TABLE_SIZE 37
43 typedef struct tagMSICOLUMNHASHENTRY
45 struct tagMSICOLUMNHASHENTRY *next;
46 UINT value;
47 UINT row;
48 } MSICOLUMNHASHENTRY;
50 typedef struct tagMSICOLUMNINFO
52 LPCWSTR tablename;
53 UINT number;
54 LPCWSTR colname;
55 UINT type;
56 UINT offset;
57 MSICOLUMNHASHENTRY **hash_table;
58 } MSICOLUMNINFO;
60 struct tagMSITABLE
62 BYTE **data;
63 BOOL *data_persistent;
64 UINT row_count;
65 struct list entry;
66 MSICOLUMNINFO *colinfo;
67 UINT col_count;
68 MSICONDITION persistent;
69 LONG ref_count;
70 WCHAR name[1];
73 /* information for default tables */
74 static const MSICOLUMNINFO _Columns_cols[4] = {
75 { L"_Columns", 1, L"Table", MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, NULL },
76 { L"_Columns", 2, L"Number", MSITYPE_VALID | MSITYPE_KEY | 2, 2, NULL },
77 { L"_Columns", 3, L"Name", MSITYPE_VALID | MSITYPE_STRING | 64, 4, NULL },
78 { L"_Columns", 4, L"Type", MSITYPE_VALID | 2, 6, NULL },
81 static const MSICOLUMNINFO _Tables_cols[1] = {
82 { L"_Tables", 1, L"Name", MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, NULL },
85 #define MAX_STREAM_NAME 0x1f
87 static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref )
89 if( MSITYPE_IS_BINARY(col->type) )
90 return 2;
92 if( col->type & MSITYPE_STRING )
93 return bytes_per_strref;
95 if( (col->type & 0xff) <= 2)
96 return 2;
98 if( (col->type & 0xff) != 4 )
99 ERR("Invalid column size %u\n", col->type & 0xff);
101 return 4;
104 static int utf2mime(int x)
106 if( (x>='0') && (x<='9') )
107 return x-'0';
108 if( (x>='A') && (x<='Z') )
109 return x-'A'+10;
110 if( (x>='a') && (x<='z') )
111 return x-'a'+10+26;
112 if( x=='.' )
113 return 10+26+26;
114 if( x=='_' )
115 return 10+26+26+1;
116 return -1;
119 LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
121 DWORD count = MAX_STREAM_NAME;
122 DWORD ch, next;
123 LPWSTR out, p;
125 if( !bTable )
126 count = lstrlenW( in )+2;
127 if (!(out = msi_alloc( count*sizeof(WCHAR) ))) return NULL;
128 p = out;
130 if( bTable )
132 *p++ = 0x4840;
133 count --;
135 while( count -- )
137 ch = *in++;
138 if( !ch )
140 *p = ch;
141 return out;
143 if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
145 ch = utf2mime(ch) + 0x4800;
146 next = *in;
147 if( next && (next<0x80) )
149 next = utf2mime(next);
150 if( next != -1 )
152 next += 0x3ffffc0;
153 ch += (next<<6);
154 in++;
158 *p++ = ch;
160 ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
161 msi_free( out );
162 return NULL;
165 static int mime2utf(int x)
167 if( x<10 )
168 return x + '0';
169 if( x<(10+26))
170 return x - 10 + 'A';
171 if( x<(10+26+26))
172 return x - 10 - 26 + 'a';
173 if( x == (10+26+26) )
174 return '.';
175 return '_';
178 BOOL decode_streamname(LPCWSTR in, LPWSTR out)
180 WCHAR ch;
181 DWORD count = 0;
183 while ( (ch = *in++) )
185 if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
187 if( ch >= 0x4800 )
188 ch = mime2utf(ch-0x4800);
189 else
191 ch -= 0x3800;
192 *out++ = mime2utf(ch&0x3f);
193 count++;
194 ch = mime2utf((ch>>6)&0x3f);
197 *out++ = ch;
198 count++;
200 *out = 0;
201 return count;
204 void enum_stream_names( IStorage *stg )
206 IEnumSTATSTG *stgenum = NULL;
207 HRESULT r;
208 STATSTG stat;
209 ULONG n, count;
210 WCHAR name[0x40];
212 r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
213 if( FAILED( r ) )
214 return;
216 n = 0;
217 while( 1 )
219 count = 0;
220 r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
221 if( FAILED( r ) || !count )
222 break;
223 decode_streamname( stat.pwcsName, name );
224 TRACE( "stream %2lu -> %s %s\n", n, debugstr_w(stat.pwcsName), debugstr_w(name) );
225 CoTaskMemFree( stat.pwcsName );
226 n++;
229 IEnumSTATSTG_Release( stgenum );
232 UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table,
233 BYTE **pdata, UINT *psz )
235 HRESULT r;
236 UINT ret = ERROR_FUNCTION_FAILED;
237 VOID *data;
238 ULONG sz, count;
239 IStream *stm = NULL;
240 STATSTG stat;
241 LPWSTR encname;
243 encname = encode_streamname(table, stname);
245 TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
247 r = IStorage_OpenStream(stg, encname, NULL,
248 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
249 msi_free( encname );
250 if( FAILED( r ) )
252 WARN( "open stream failed r = %#lx - empty table?\n", r );
253 return ret;
256 r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
257 if( FAILED( r ) )
259 WARN( "open stream failed r = %#lx!\n", r );
260 goto end;
263 if( stat.cbSize.QuadPart >> 32 )
265 WARN("Too big!\n");
266 goto end;
269 sz = stat.cbSize.QuadPart;
270 data = msi_alloc( sz );
271 if( !data )
273 WARN( "couldn't allocate memory r = %#lx!\n", r );
274 ret = ERROR_NOT_ENOUGH_MEMORY;
275 goto end;
278 r = IStream_Read(stm, data, sz, &count );
279 if( FAILED( r ) || ( count != sz ) )
281 msi_free( data );
282 WARN("read stream failed r = %#lx!\n", r);
283 goto end;
286 *pdata = data;
287 *psz = sz;
288 ret = ERROR_SUCCESS;
290 end:
291 IStream_Release( stm );
293 return ret;
296 UINT write_stream_data( IStorage *stg, LPCWSTR stname,
297 LPCVOID data, UINT sz, BOOL bTable )
299 HRESULT r;
300 UINT ret = ERROR_FUNCTION_FAILED;
301 ULONG count;
302 IStream *stm = NULL;
303 ULARGE_INTEGER size;
304 LARGE_INTEGER pos;
305 LPWSTR encname;
307 encname = encode_streamname(bTable, stname );
308 r = IStorage_OpenStream( stg, encname, NULL,
309 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
310 if( FAILED(r) )
312 r = IStorage_CreateStream( stg, encname,
313 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
315 msi_free( encname );
316 if( FAILED( r ) )
318 WARN( "open stream failed r = %#lx\n", r );
319 return ret;
322 size.QuadPart = sz;
323 r = IStream_SetSize( stm, size );
324 if( FAILED( r ) )
326 WARN("Failed to SetSize\n");
327 goto end;
330 pos.QuadPart = 0;
331 r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
332 if( FAILED( r ) )
334 WARN("Failed to Seek\n");
335 goto end;
338 if (sz)
340 r = IStream_Write(stm, data, sz, &count );
341 if( FAILED( r ) || ( count != sz ) )
343 WARN("Failed to Write\n");
344 goto end;
348 ret = ERROR_SUCCESS;
350 end:
351 IStream_Release( stm );
353 return ret;
356 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
358 UINT i;
359 for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table );
362 static void free_table( MSITABLE *table )
364 UINT i;
365 for( i=0; i<table->row_count; i++ )
366 msi_free( table->data[i] );
367 msi_free( table->data );
368 msi_free( table->data_persistent );
369 msi_free_colinfo( table->colinfo, table->col_count );
370 msi_free( table->colinfo );
371 msi_free( table );
374 static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref )
376 const MSICOLUMNINFO *last_col;
378 if (!count)
379 return 0;
381 if (bytes_per_strref != LONG_STR_BYTES)
383 UINT i, size = 0;
384 for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref );
385 return size;
387 last_col = &cols[count - 1];
388 return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref );
391 /* add this table to the list of cached tables in the database */
392 static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg )
394 BYTE *rawdata = NULL;
395 UINT rawsize = 0, i, j, row_size, row_size_mem;
397 TRACE("%s\n",debugstr_w(t->name));
399 row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref );
400 row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES );
402 /* if we can't read the table, just assume that it's empty */
403 read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize );
404 if( !rawdata )
405 return ERROR_SUCCESS;
407 TRACE("Read %d bytes\n", rawsize );
409 if( rawsize % row_size )
411 WARN("Table size is invalid %d/%d\n", rawsize, row_size );
412 goto err;
415 if ((t->row_count = rawsize / row_size))
417 if (!(t->data = msi_alloc_zero( t->row_count * sizeof(USHORT *) ))) goto err;
418 if (!(t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL) ))) goto err;
421 /* transpose all the data */
422 TRACE("Transposing data from %d rows\n", t->row_count );
423 for (i = 0; i < t->row_count; i++)
425 UINT ofs = 0, ofs_mem = 0;
427 t->data[i] = msi_alloc( row_size_mem );
428 if( !t->data[i] )
429 goto err;
430 t->data_persistent[i] = TRUE;
432 for (j = 0; j < t->col_count; j++)
434 UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
435 UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref );
436 UINT k;
438 if ( n != 2 && n != 3 && n != 4 )
440 ERR("oops - unknown column width %d\n", n);
441 goto err;
443 if (t->colinfo[j].type & MSITYPE_STRING && n < m)
445 for (k = 0; k < m; k++)
447 if (k < n)
448 t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
449 else
450 t->data[i][ofs_mem + k] = 0;
453 else
455 for (k = 0; k < n; k++)
456 t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
458 ofs_mem += m;
459 ofs += n;
463 msi_free( rawdata );
464 return ERROR_SUCCESS;
465 err:
466 msi_free( rawdata );
467 return ERROR_FUNCTION_FAILED;
470 void free_cached_tables( MSIDATABASE *db )
472 while( !list_empty( &db->tables ) )
474 MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
476 list_remove( &t->entry );
477 free_table( t );
481 static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
483 MSITABLE *t;
485 LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
486 if( !wcscmp( name, t->name ) )
487 return t;
489 return NULL;
492 static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count )
494 DWORD i;
496 for (i = 0; colinfo && i < count; i++)
498 assert( i + 1 == colinfo[i].number );
499 if (i) colinfo[i].offset = colinfo[i - 1].offset +
500 bytes_per_column( db, &colinfo[i - 1], LONG_STR_BYTES );
501 else colinfo[i].offset = 0;
503 TRACE("column %d is [%s] with type %08x ofs %d\n",
504 colinfo[i].number, debugstr_w(colinfo[i].colname),
505 colinfo[i].type, colinfo[i].offset);
509 static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz )
511 const MSICOLUMNINFO *p;
512 DWORD i, n;
514 TRACE("%s\n", debugstr_w(name));
516 if (!wcscmp( name, L"_Tables" ))
518 p = _Tables_cols;
519 n = 1;
521 else if (!wcscmp( name, L"_Columns" ))
523 p = _Columns_cols;
524 n = 4;
526 else return ERROR_FUNCTION_FAILED;
528 for (i = 0; i < n; i++)
530 if (colinfo && i < *sz) colinfo[i] = p[i];
531 if (colinfo && i >= *sz) break;
533 table_calc_column_offsets( db, colinfo, n );
534 *sz = n;
535 return ERROR_SUCCESS;
538 static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz );
540 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
542 UINT r, column_count = 0;
543 MSICOLUMNINFO *columns;
545 /* get the number of columns in this table */
546 column_count = 0;
547 r = get_tablecolumns( db, name, NULL, &column_count );
548 if (r != ERROR_SUCCESS)
549 return r;
551 *pcount = column_count;
553 /* if there are no columns, there's no table */
554 if (!column_count)
555 return ERROR_INVALID_PARAMETER;
557 TRACE("table %s found\n", debugstr_w(name));
559 columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) );
560 if (!columns)
561 return ERROR_FUNCTION_FAILED;
563 r = get_tablecolumns( db, name, columns, &column_count );
564 if (r != ERROR_SUCCESS)
566 msi_free( columns );
567 return ERROR_FUNCTION_FAILED;
569 *pcols = columns;
570 return r;
573 static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret )
575 MSITABLE *table;
576 UINT r;
578 /* first, see if the table is cached */
579 table = find_cached_table( db, name );
580 if (table)
582 *table_ret = table;
583 return ERROR_SUCCESS;
586 /* nonexistent tables should be interpreted as empty tables */
587 table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) );
588 if (!table)
589 return ERROR_FUNCTION_FAILED;
591 table->row_count = 0;
592 table->data = NULL;
593 table->data_persistent = NULL;
594 table->colinfo = NULL;
595 table->col_count = 0;
596 table->persistent = MSICONDITION_TRUE;
597 lstrcpyW( table->name, name );
599 if (!wcscmp( name, L"_Tables" ) || !wcscmp( name, L"_Columns" ))
600 table->persistent = MSICONDITION_NONE;
602 r = table_get_column_info( db, name, &table->colinfo, &table->col_count );
603 if (r != ERROR_SUCCESS)
605 free_table( table );
606 return r;
608 r = read_table_from_storage( db, table, db->storage );
609 if (r != ERROR_SUCCESS)
611 free_table( table );
612 return r;
614 list_add_head( &db->tables, &table->entry );
615 *table_ret = table;
616 return ERROR_SUCCESS;
619 static UINT read_table_int( BYTE *const *data, UINT row, UINT col, UINT bytes )
621 UINT ret = 0, i;
623 for (i = 0; i < bytes; i++)
624 ret += data[row][col + i] << i * 8;
626 return ret;
629 static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz )
631 UINT r, i, n = 0, table_id, count, maxcount = *sz;
632 MSITABLE *table = NULL;
634 TRACE("%s\n", debugstr_w(szTableName));
636 /* first check if there is a default table with that name */
637 r = get_defaulttablecolumns( db, szTableName, colinfo, sz );
638 if (r == ERROR_SUCCESS && *sz)
639 return r;
641 r = get_table( db, L"_Columns", &table );
642 if (r != ERROR_SUCCESS)
644 ERR("couldn't load _Columns table\n");
645 return ERROR_FUNCTION_FAILED;
648 /* convert table and column names to IDs from the string table */
649 r = msi_string2id( db->strings, szTableName, -1, &table_id );
650 if (r != ERROR_SUCCESS)
652 WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
653 return r;
655 TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
657 /* Note: _Columns table doesn't have non-persistent data */
659 /* if maxcount is non-zero, assume it's exactly right for this table */
660 if (colinfo) memset( colinfo, 0, maxcount * sizeof(*colinfo) );
661 count = table->row_count;
662 for (i = 0; i < count; i++)
664 if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue;
665 if (colinfo)
667 UINT id = read_table_int( table->data, i, table->colinfo[2].offset, LONG_STR_BYTES );
668 UINT col = read_table_int( table->data, i, table->colinfo[1].offset, sizeof(USHORT) ) - (1 << 15);
670 /* check the column number is in range */
671 if (col < 1 || col > maxcount)
673 ERR("column %d out of range (maxcount: %d)\n", col, maxcount);
674 continue;
676 /* check if this column was already set */
677 if (colinfo[col - 1].number)
679 ERR("duplicate column %d\n", col);
680 continue;
682 colinfo[col - 1].tablename = msi_string_lookup( db->strings, table_id, NULL );
683 colinfo[col - 1].number = col;
684 colinfo[col - 1].colname = msi_string_lookup( db->strings, id, NULL );
685 colinfo[col - 1].type = read_table_int( table->data, i, table->colinfo[3].offset,
686 sizeof(USHORT) ) - (1 << 15);
687 colinfo[col - 1].offset = 0;
688 colinfo[col - 1].hash_table = NULL;
690 n++;
692 TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
694 if (colinfo && n != maxcount)
696 ERR("missing column in table %s\n", debugstr_w(szTableName));
697 msi_free_colinfo( colinfo, maxcount );
698 return ERROR_FUNCTION_FAILED;
700 table_calc_column_offsets( db, colinfo, n );
701 *sz = n;
702 return ERROR_SUCCESS;
705 UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info,
706 MSICONDITION persistent, BOOL hold )
708 UINT r, nField;
709 MSIVIEW *tv = NULL;
710 MSIRECORD *rec = NULL;
711 column_info *col;
712 MSITABLE *table;
713 UINT i;
715 /* only add tables that don't exist already */
716 if( TABLE_Exists(db, name ) )
718 WARN("table %s exists\n", debugstr_w(name));
719 return ERROR_BAD_QUERY_SYNTAX;
722 table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
723 if( !table )
724 return ERROR_FUNCTION_FAILED;
726 table->ref_count = 0;
727 table->row_count = 0;
728 table->data = NULL;
729 table->data_persistent = NULL;
730 table->colinfo = NULL;
731 table->col_count = 0;
732 table->persistent = persistent;
733 lstrcpyW( table->name, name );
735 if( hold )
736 table->ref_count++;
738 for( col = col_info; col; col = col->next )
739 table->col_count++;
741 table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) );
742 if (!table->colinfo)
744 free_table( table );
745 return ERROR_FUNCTION_FAILED;
748 for( i = 0, col = col_info; col; i++, col = col->next )
750 UINT table_id = msi_add_string( db->strings, col->table, -1, persistent );
751 UINT col_id = msi_add_string( db->strings, col->column, -1, persistent );
753 table->colinfo[ i ].tablename = msi_string_lookup( db->strings, table_id, NULL );
754 table->colinfo[ i ].number = i + 1;
755 table->colinfo[ i ].colname = msi_string_lookup( db->strings, col_id, NULL );
756 table->colinfo[ i ].type = col->type;
757 table->colinfo[ i ].offset = 0;
758 table->colinfo[ i ].hash_table = NULL;
760 table_calc_column_offsets( db, table->colinfo, table->col_count);
762 r = TABLE_CreateView( db, L"_Tables", &tv );
763 TRACE("CreateView returned %x\n", r);
764 if( r )
766 free_table( table );
767 return r;
770 r = tv->ops->execute( tv, 0 );
771 TRACE("tv execute returned %x\n", r);
772 if( r )
773 goto err;
775 rec = MSI_CreateRecord( 1 );
776 if( !rec )
777 goto err;
779 r = MSI_RecordSetStringW( rec, 1, name );
780 if( r )
781 goto err;
783 r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE );
784 TRACE("insert_row returned %x\n", r);
785 if( r )
786 goto err;
788 tv->ops->delete( tv );
789 tv = NULL;
791 msiobj_release( &rec->hdr );
792 rec = NULL;
794 if( persistent != MSICONDITION_FALSE )
796 /* add each column to the _Columns table */
797 r = TABLE_CreateView( db, L"_Columns", &tv );
798 if( r )
799 goto err;
801 r = tv->ops->execute( tv, 0 );
802 TRACE("tv execute returned %x\n", r);
803 if( r )
804 goto err;
806 rec = MSI_CreateRecord( 4 );
807 if( !rec )
808 goto err;
810 r = MSI_RecordSetStringW( rec, 1, name );
811 if( r )
812 goto err;
815 * need to set the table, column number, col name and type
816 * for each column we enter in the table
818 nField = 1;
819 for( col = col_info; col; col = col->next )
821 r = MSI_RecordSetInteger( rec, 2, nField );
822 if( r )
823 goto err;
825 r = MSI_RecordSetStringW( rec, 3, col->column );
826 if( r )
827 goto err;
829 r = MSI_RecordSetInteger( rec, 4, col->type );
830 if( r )
831 goto err;
833 r = tv->ops->insert_row( tv, rec, -1, FALSE );
834 if( r )
835 goto err;
837 nField++;
839 if( !col )
840 r = ERROR_SUCCESS;
843 err:
844 if (rec)
845 msiobj_release( &rec->hdr );
846 /* FIXME: remove values from the string table on error */
847 if( tv )
848 tv->ops->delete( tv );
850 if (r == ERROR_SUCCESS)
851 list_add_head( &db->tables, &table->entry );
852 else
853 free_table( table );
855 return r;
858 static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref )
860 BYTE *rawdata = NULL;
861 UINT rawsize, i, j, row_size, row_count;
862 UINT r = ERROR_FUNCTION_FAILED;
864 /* Nothing to do for non-persistent tables */
865 if( t->persistent == MSICONDITION_FALSE )
866 return ERROR_SUCCESS;
868 TRACE("Saving %s\n", debugstr_w( t->name ) );
870 row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref );
871 row_count = t->row_count;
872 for (i = 0; i < t->row_count; i++)
874 if (!t->data_persistent[i])
876 row_count = 1; /* yes, this is bizarre */
877 break;
880 rawsize = row_count * row_size;
881 rawdata = msi_alloc_zero( rawsize );
882 if( !rawdata )
884 r = ERROR_NOT_ENOUGH_MEMORY;
885 goto err;
888 rawsize = 0;
889 for (i = 0; i < row_count; i++)
891 UINT ofs = 0, ofs_mem = 0;
893 if (!t->data_persistent[i]) break;
895 for (j = 0; j < t->col_count; j++)
897 UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
898 UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref );
899 UINT k;
901 if (n != 2 && n != 3 && n != 4)
903 ERR("oops - unknown column width %d\n", n);
904 goto err;
906 if (t->colinfo[j].type & MSITYPE_STRING && n < m)
908 UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES );
909 if (id > 1 << bytes_per_strref * 8)
911 ERR("string id %u out of range\n", id);
912 goto err;
915 for (k = 0; k < n; k++)
917 rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k];
919 ofs_mem += m;
920 ofs += n;
922 rawsize += row_size;
925 TRACE("writing %d bytes\n", rawsize);
926 r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE );
928 err:
929 msi_free( rawdata );
930 return r;
933 static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name )
935 MSITABLE *table;
936 UINT size, offset, old_count;
937 UINT n;
939 if (!(table = find_cached_table( db, name ))) return;
940 old_count = table->col_count;
941 msi_free_colinfo( table->colinfo, table->col_count );
942 msi_free( table->colinfo );
943 table->colinfo = NULL;
945 table_get_column_info( db, name, &table->colinfo, &table->col_count );
946 if (!table->col_count) return;
948 size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES );
949 offset = table->colinfo[table->col_count - 1].offset;
951 for ( n = 0; n < table->row_count; n++ )
953 table->data[n] = msi_realloc( table->data[n], size );
954 if (old_count < table->col_count)
955 memset( &table->data[n][offset], 0, size - offset );
959 /* try to find the table name in the _Tables table */
960 BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name )
962 UINT r, table_id, i;
963 MSITABLE *table;
965 if( !wcscmp( name, L"_Tables" ) || !wcscmp( name, L"_Columns" ) ||
966 !wcscmp( name, L"_Streams" ) || !wcscmp( name, L"_Storages" ) )
967 return TRUE;
969 r = msi_string2id( db->strings, name, -1, &table_id );
970 if( r != ERROR_SUCCESS )
972 TRACE("Couldn't find id for %s\n", debugstr_w(name));
973 return FALSE;
976 r = get_table( db, L"_Tables", &table );
977 if( r != ERROR_SUCCESS )
979 ERR("table _Tables not available\n");
980 return FALSE;
983 for( i = 0; i < table->row_count; i++ )
985 if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id )
986 return TRUE;
989 return FALSE;
992 /* below is the query interface to a table */
994 typedef struct tagMSITABLEVIEW
996 MSIVIEW view;
997 MSIDATABASE *db;
998 MSITABLE *table;
999 MSICOLUMNINFO *columns;
1000 UINT num_cols;
1001 UINT row_size;
1002 WCHAR name[1];
1003 } MSITABLEVIEW;
1005 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1007 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1008 UINT offset, n;
1010 if( !tv->table )
1011 return ERROR_INVALID_PARAMETER;
1013 if( (col==0) || (col>tv->num_cols) )
1014 return ERROR_INVALID_PARAMETER;
1016 /* how many rows are there ? */
1017 if( row >= tv->table->row_count )
1018 return ERROR_NO_MORE_ITEMS;
1020 if( tv->columns[col-1].offset >= tv->row_size )
1022 ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1023 ERR("%p %p\n", tv, tv->columns );
1024 return ERROR_FUNCTION_FAILED;
1027 n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1028 if (n != 2 && n != 3 && n != 4)
1030 ERR("oops! what is %d bytes per column?\n", n );
1031 return ERROR_FUNCTION_FAILED;
1034 offset = tv->columns[col-1].offset;
1035 *val = read_table_int(tv->table->data, row, offset, n);
1037 /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1039 return ERROR_SUCCESS;
1042 static UINT get_stream_name( const MSITABLEVIEW *tv, UINT row, WCHAR **pstname )
1044 LPWSTR p, stname = NULL;
1045 UINT i, r, type, ival;
1046 DWORD len;
1047 LPCWSTR sval;
1048 MSIVIEW *view = (MSIVIEW *) tv;
1050 TRACE("%p %d\n", tv, row);
1052 len = lstrlenW( tv->name ) + 1;
1053 stname = msi_alloc( len*sizeof(WCHAR) );
1054 if ( !stname )
1056 r = ERROR_OUTOFMEMORY;
1057 goto err;
1060 lstrcpyW( stname, tv->name );
1062 for ( i = 0; i < tv->num_cols; i++ )
1064 type = tv->columns[i].type;
1065 if ( type & MSITYPE_KEY )
1067 WCHAR number[0x20];
1069 r = TABLE_fetch_int( view, row, i+1, &ival );
1070 if ( r != ERROR_SUCCESS )
1071 goto err;
1073 if ( tv->columns[i].type & MSITYPE_STRING )
1075 sval = msi_string_lookup( tv->db->strings, ival, NULL );
1076 if ( !sval )
1078 r = ERROR_INVALID_PARAMETER;
1079 goto err;
1082 else
1084 UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES );
1086 switch( n )
1088 case 2:
1089 swprintf( number, ARRAY_SIZE(number), L"%d", ival-0x8000 );
1090 break;
1091 case 4:
1092 swprintf( number, ARRAY_SIZE(number), L"%d", ival^0x80000000 );
1093 break;
1094 default:
1095 ERR( "oops - unknown column width %d\n", n );
1096 r = ERROR_FUNCTION_FAILED;
1097 goto err;
1099 sval = number;
1102 len += lstrlenW( L"." ) + lstrlenW( sval );
1103 p = msi_realloc ( stname, len*sizeof(WCHAR) );
1104 if ( !p )
1106 r = ERROR_OUTOFMEMORY;
1107 goto err;
1109 stname = p;
1111 lstrcatW( stname, L"." );
1112 lstrcatW( stname, sval );
1114 else
1115 continue;
1118 *pstname = stname;
1119 return ERROR_SUCCESS;
1121 err:
1122 msi_free( stname );
1123 *pstname = NULL;
1124 return r;
1128 * We need a special case for streams, as we need to reference column with
1129 * the name of the stream in the same table, and the table name
1130 * which may not be available at higher levels of the query
1132 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1134 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1135 UINT r;
1136 WCHAR *name;
1138 if( !view->ops->fetch_int )
1139 return ERROR_INVALID_PARAMETER;
1141 r = get_stream_name( tv, row, &name );
1142 if (r != ERROR_SUCCESS)
1144 ERR("fetching stream, error = %u\n", r);
1145 return r;
1148 r = msi_get_stream( tv->db, name, stm );
1149 if (r != ERROR_SUCCESS)
1150 ERR("fetching stream %s, error = %u\n", debugstr_w(name), r);
1152 msi_free( name );
1153 return r;
1156 /* Set a table value, i.e. preadjusted integer or string ID. */
1157 static UINT table_set_bytes( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
1159 UINT offset, n, i;
1161 if( !tv->table )
1162 return ERROR_INVALID_PARAMETER;
1164 if( (col==0) || (col>tv->num_cols) )
1165 return ERROR_INVALID_PARAMETER;
1167 if( row >= tv->table->row_count )
1168 return ERROR_INVALID_PARAMETER;
1170 if( tv->columns[col-1].offset >= tv->row_size )
1172 ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1173 ERR("%p %p\n", tv, tv->columns );
1174 return ERROR_FUNCTION_FAILED;
1177 msi_free( tv->columns[col-1].hash_table );
1178 tv->columns[col-1].hash_table = NULL;
1180 n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1181 if ( n != 2 && n != 3 && n != 4 )
1183 ERR("oops! what is %d bytes per column?\n", n );
1184 return ERROR_FUNCTION_FAILED;
1187 offset = tv->columns[col-1].offset;
1188 for ( i = 0; i < n; i++ )
1189 tv->table->data[row][offset + i] = (val >> i * 8) & 0xff;
1191 return ERROR_SUCCESS;
1194 static UINT int_to_table_storage( const MSITABLEVIEW *tv, UINT col, int val, UINT *ret )
1196 if ((tv->columns[col-1].type & MSI_DATASIZEMASK) == 2)
1198 if (val == MSI_NULL_INTEGER)
1199 *ret = 0;
1200 else if ((val + 0x8000) & 0xffff0000)
1202 ERR("value %d out of range\n", val);
1203 return ERROR_FUNCTION_FAILED;
1205 else
1206 *ret = val + 0x8000;
1208 else
1209 *ret = val ^ 0x80000000;
1211 return ERROR_SUCCESS;
1214 static UINT TABLE_set_int( MSIVIEW *view, UINT row, UINT col, int val )
1216 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1217 UINT r, table_int;
1219 TRACE("row %u, col %u, val %d.\n", row, col, val);
1221 if ((r = int_to_table_storage( tv, col, val, &table_int )))
1222 return r;
1224 if (tv->columns[col-1].type & MSITYPE_KEY)
1226 UINT key;
1228 if ((r = TABLE_fetch_int( view, row, col, &key )))
1229 return r;
1230 if (key != table_int)
1232 ERR("Cannot modify primary key %s.%s.\n",
1233 debugstr_w(tv->table->name), debugstr_w(tv->columns[col-1].colname));
1234 return ERROR_FUNCTION_FAILED;
1238 return table_set_bytes( tv, row, col, table_int );
1241 static UINT TABLE_set_string( MSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
1243 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1244 BOOL persistent;
1245 UINT id, r;
1247 TRACE("row %u, col %u, val %s.\n", row, col, debugstr_wn(val, len));
1249 persistent = (tv->table->persistent != MSICONDITION_FALSE)
1250 && tv->table->data_persistent[row];
1252 if (val)
1254 r = msi_string2id( tv->db->strings, val, len, &id );
1255 if (r != ERROR_SUCCESS)
1256 id = msi_add_string( tv->db->strings, val, len, persistent );
1258 else
1259 id = 0;
1261 if (tv->columns[col-1].type & MSITYPE_KEY)
1263 UINT key;
1265 if ((r = TABLE_fetch_int( view, row, col, &key )))
1266 return r;
1267 if (key != id)
1269 ERR("Cannot modify primary key %s.%s.\n",
1270 debugstr_w(tv->table->name), debugstr_w(tv->columns[col-1].colname));
1271 return ERROR_FUNCTION_FAILED;
1275 return table_set_bytes( tv, row, col, id );
1278 static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
1280 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1282 if (!tv->table)
1283 return ERROR_INVALID_PARAMETER;
1285 return msi_view_get_row(tv->db, view, row, rec);
1288 static UINT add_stream( MSIDATABASE *db, const WCHAR *name, IStream *data )
1290 MSIQUERY *query;
1291 MSIRECORD *rec;
1292 UINT r;
1294 TRACE("%p %s %p\n", db, debugstr_w(name), data);
1296 if (!(rec = MSI_CreateRecord( 2 )))
1297 return ERROR_OUTOFMEMORY;
1299 r = MSI_RecordSetStringW( rec, 1, name );
1300 if (r != ERROR_SUCCESS)
1301 goto done;
1303 r = MSI_RecordSetIStream( rec, 2, data );
1304 if (r != ERROR_SUCCESS)
1305 goto done;
1307 r = MSI_DatabaseOpenViewW( db, L"INSERT INTO `_Streams` (`Name`,`Data`) VALUES (?,?)", &query );
1308 if (r != ERROR_SUCCESS)
1309 goto done;
1311 r = MSI_ViewExecute( query, rec );
1312 msiobj_release( &query->hdr );
1313 if (r == ERROR_SUCCESS)
1314 goto done;
1316 msiobj_release( &rec->hdr );
1317 if (!(rec = MSI_CreateRecord( 2 )))
1318 return ERROR_OUTOFMEMORY;
1320 r = MSI_RecordSetIStream( rec, 1, data );
1321 if (r != ERROR_SUCCESS)
1322 goto done;
1324 r = MSI_RecordSetStringW( rec, 2, name );
1325 if (r != ERROR_SUCCESS)
1326 goto done;
1328 r = MSI_DatabaseOpenViewW( db, L"UPDATE `_Streams` SET `Data` = ? WHERE `Name` = ?", &query );
1329 if (r != ERROR_SUCCESS)
1330 goto done;
1332 r = MSI_ViewExecute( query, rec );
1333 msiobj_release( &query->hdr );
1335 done:
1336 msiobj_release( &rec->hdr );
1337 return r;
1340 static UINT TABLE_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream )
1342 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1343 WCHAR *name;
1344 UINT r;
1346 TRACE("row %u, col %u, stream %p.\n", row, col, stream);
1348 if ((r = get_stream_name( tv, row - 1, &name )))
1349 return r;
1351 r = add_stream( tv->db, name, stream );
1352 msi_free( name );
1353 return r;
1356 static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue )
1358 MSICOLUMNINFO columninfo;
1359 UINT r;
1361 if (!iField || iField > tv->num_cols || MSI_RecordIsNull( rec, iField ))
1362 return ERROR_FUNCTION_FAILED;
1364 columninfo = tv->columns[ iField - 1 ];
1366 if ( MSITYPE_IS_BINARY(columninfo.type) )
1368 *pvalue = 1; /* refers to the first key column */
1370 else if ( columninfo.type & MSITYPE_STRING )
1372 int len;
1373 const WCHAR *sval = msi_record_get_string( rec, iField, &len );
1374 if (sval)
1376 r = msi_string2id( tv->db->strings, sval, len, pvalue );
1377 if (r != ERROR_SUCCESS)
1378 return ERROR_NOT_FOUND;
1380 else *pvalue = 0;
1382 else
1383 return int_to_table_storage( tv, iField, MSI_RecordGetInteger( rec, iField ), pvalue );
1385 return ERROR_SUCCESS;
1388 static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
1390 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1391 UINT i, val, r = ERROR_SUCCESS;
1393 if ( !tv->table )
1394 return ERROR_INVALID_PARAMETER;
1396 /* test if any of the mask bits are invalid */
1397 if ( mask >= (1<<tv->num_cols) )
1398 return ERROR_INVALID_PARAMETER;
1400 for ( i = 0; i < tv->num_cols; i++ )
1402 BOOL persistent;
1404 /* only update the fields specified in the mask */
1405 if ( !(mask&(1<<i)) )
1406 continue;
1408 persistent = (tv->table->persistent != MSICONDITION_FALSE) &&
1409 (tv->table->data_persistent[row]);
1410 /* FIXME: should we allow updating keys? */
1412 val = 0;
1413 if ( !MSI_RecordIsNull( rec, i + 1 ) )
1415 r = get_table_value_from_record (tv, rec, i + 1, &val);
1416 if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
1418 IStream *stm;
1419 LPWSTR stname;
1421 if ( r != ERROR_SUCCESS )
1422 return ERROR_FUNCTION_FAILED;
1424 r = MSI_RecordGetIStream( rec, i + 1, &stm );
1425 if ( r != ERROR_SUCCESS )
1426 return r;
1428 r = get_stream_name( tv, row, &stname );
1429 if ( r != ERROR_SUCCESS )
1431 IStream_Release( stm );
1432 return r;
1435 r = add_stream( tv->db, stname, stm );
1436 IStream_Release( stm );
1437 msi_free ( stname );
1439 if ( r != ERROR_SUCCESS )
1440 return r;
1442 else if ( tv->columns[i].type & MSITYPE_STRING )
1444 UINT x;
1446 if ( r != ERROR_SUCCESS )
1448 int len;
1449 const WCHAR *sval = msi_record_get_string( rec, i + 1, &len );
1450 val = msi_add_string( tv->db->strings, sval, len, persistent );
1452 else
1454 TABLE_fetch_int(&tv->view, row, i + 1, &x);
1455 if (val == x)
1456 continue;
1459 else
1461 if ( r != ERROR_SUCCESS )
1462 return ERROR_FUNCTION_FAILED;
1466 r = table_set_bytes( tv, row, i+1, val );
1467 if ( r != ERROR_SUCCESS )
1468 break;
1470 return r;
1473 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary )
1475 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1476 BYTE **p, *row;
1477 BOOL *b;
1478 UINT sz;
1479 BYTE ***data_ptr;
1480 BOOL **data_persist_ptr;
1481 UINT *row_count;
1483 TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE");
1485 if( !tv->table )
1486 return ERROR_INVALID_PARAMETER;
1488 row = msi_alloc_zero( tv->row_size );
1489 if( !row )
1490 return ERROR_NOT_ENOUGH_MEMORY;
1492 row_count = &tv->table->row_count;
1493 data_ptr = &tv->table->data;
1494 data_persist_ptr = &tv->table->data_persistent;
1495 if (*num == -1)
1496 *num = tv->table->row_count;
1498 sz = (*row_count + 1) * sizeof (BYTE*);
1499 if( *data_ptr )
1500 p = msi_realloc( *data_ptr, sz );
1501 else
1502 p = msi_alloc( sz );
1503 if( !p )
1505 msi_free( row );
1506 return ERROR_NOT_ENOUGH_MEMORY;
1509 sz = (*row_count + 1) * sizeof (BOOL);
1510 if( *data_persist_ptr )
1511 b = msi_realloc( *data_persist_ptr, sz );
1512 else
1513 b = msi_alloc( sz );
1514 if( !b )
1516 msi_free( row );
1517 msi_free( p );
1518 return ERROR_NOT_ENOUGH_MEMORY;
1521 *data_ptr = p;
1522 (*data_ptr)[*row_count] = row;
1524 *data_persist_ptr = b;
1525 (*data_persist_ptr)[*row_count] = !temporary;
1527 (*row_count)++;
1529 return ERROR_SUCCESS;
1532 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1534 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1536 TRACE("%p %p\n", tv, record);
1538 TRACE("There are %d columns\n", tv->num_cols );
1540 return ERROR_SUCCESS;
1543 static UINT TABLE_close( struct tagMSIVIEW *view )
1545 TRACE("%p\n", view );
1547 return ERROR_SUCCESS;
1550 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1552 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1554 TRACE("%p %p %p\n", view, rows, cols );
1556 if( cols )
1557 *cols = tv->num_cols;
1558 if( rows )
1560 if( !tv->table )
1561 return ERROR_INVALID_PARAMETER;
1562 *rows = tv->table->row_count;
1565 return ERROR_SUCCESS;
1568 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1569 UINT n, LPCWSTR *name, UINT *type, BOOL *temporary,
1570 LPCWSTR *table_name )
1572 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1574 TRACE("%p %d %p %p\n", tv, n, name, type );
1576 if( ( n == 0 ) || ( n > tv->num_cols ) )
1577 return ERROR_INVALID_PARAMETER;
1579 if( name )
1581 *name = tv->columns[n-1].colname;
1582 if( !*name )
1583 return ERROR_FUNCTION_FAILED;
1586 if( table_name )
1588 *table_name = tv->columns[n-1].tablename;
1589 if( !*table_name )
1590 return ERROR_FUNCTION_FAILED;
1593 if( type )
1594 *type = tv->columns[n-1].type;
1596 if( temporary )
1597 *temporary = (tv->columns[n-1].type & MSITYPE_TEMPORARY) != 0;
1599 return ERROR_SUCCESS;
1602 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column );
1604 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column )
1606 UINT r, row, i;
1608 /* check there are no null values where they're not allowed */
1609 for( i = 0; i < tv->num_cols; i++ )
1611 if ( tv->columns[i].type & MSITYPE_NULLABLE )
1612 continue;
1614 if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
1615 TRACE("skipping binary column\n");
1616 else if ( tv->columns[i].type & MSITYPE_STRING )
1618 int len;
1619 const WCHAR *str = msi_record_get_string( rec, i+1, &len );
1621 if (!str || (!str[0] && !len))
1623 if (column) *column = i;
1624 return ERROR_INVALID_DATA;
1627 else
1629 UINT n;
1631 n = MSI_RecordGetInteger( rec, i+1 );
1632 if (n == MSI_NULL_INTEGER)
1634 if (column) *column = i;
1635 return ERROR_INVALID_DATA;
1640 /* check there are no duplicate keys */
1641 r = msi_table_find_row( tv, rec, &row, column );
1642 if (r == ERROR_SUCCESS)
1643 return ERROR_FUNCTION_FAILED;
1645 return ERROR_SUCCESS;
1648 static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec )
1650 UINT r, i, ivalue, x;
1652 for (i = 0; i < tv->num_cols; i++ )
1654 if (!(tv->columns[i].type & MSITYPE_KEY)) continue;
1656 r = get_table_value_from_record( tv, rec, i + 1, &ivalue );
1657 if (r != ERROR_SUCCESS)
1658 return 1;
1660 r = TABLE_fetch_int( &tv->view, row, i + 1, &x );
1661 if (r != ERROR_SUCCESS)
1663 WARN("TABLE_fetch_int should not fail here %u\n", r);
1664 return -1;
1666 if (ivalue > x)
1668 return 1;
1670 else if (ivalue == x)
1672 if (i < tv->num_cols - 1) continue;
1673 return 0;
1675 else
1676 return -1;
1678 return 1;
1681 static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec )
1683 int idx, c, low = 0, high = tv->table->row_count - 1;
1685 TRACE("%p %p\n", tv, rec);
1687 while (low <= high)
1689 idx = (low + high) / 2;
1690 c = compare_record( tv, idx, rec );
1692 if (c < 0)
1693 high = idx - 1;
1694 else if (c > 0)
1695 low = idx + 1;
1696 else
1698 TRACE("found %u\n", idx);
1699 return idx;
1702 TRACE("found %u\n", high + 1);
1703 return high + 1;
1706 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
1708 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1709 UINT i, r;
1711 TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" );
1713 /* check that the key is unique - can we find a matching row? */
1714 r = table_validate_new( tv, rec, NULL );
1715 if( r != ERROR_SUCCESS )
1716 return ERROR_FUNCTION_FAILED;
1718 if (row == -1)
1719 row = find_insert_index( tv, rec );
1721 r = table_create_new_row( view, &row, temporary );
1722 TRACE("insert_row returned %08x\n", r);
1723 if( r != ERROR_SUCCESS )
1724 return r;
1726 /* shift the rows to make room for the new row */
1727 for (i = tv->table->row_count - 1; i > row; i--)
1729 memmove(&(tv->table->data[i][0]),
1730 &(tv->table->data[i - 1][0]), tv->row_size);
1731 tv->table->data_persistent[i] = tv->table->data_persistent[i - 1];
1734 /* Re-set the persistence flag */
1735 tv->table->data_persistent[row] = !temporary;
1736 return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
1739 static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row )
1741 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1742 UINT r, num_rows, num_cols, i;
1744 TRACE("%p %d\n", tv, row);
1746 if ( !tv->table )
1747 return ERROR_INVALID_PARAMETER;
1749 r = TABLE_get_dimensions( view, &num_rows, &num_cols );
1750 if ( r != ERROR_SUCCESS )
1751 return r;
1753 if ( row >= num_rows )
1754 return ERROR_FUNCTION_FAILED;
1756 num_rows = tv->table->row_count;
1757 tv->table->row_count--;
1759 /* reset the hash tables */
1760 for (i = 0; i < tv->num_cols; i++)
1762 msi_free( tv->columns[i].hash_table );
1763 tv->columns[i].hash_table = NULL;
1766 for (i = row + 1; i < num_rows; i++)
1768 memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size);
1769 tv->table->data_persistent[i - 1] = tv->table->data_persistent[i];
1772 msi_free(tv->table->data[num_rows - 1]);
1774 return ERROR_SUCCESS;
1777 static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row)
1779 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1780 UINT r, new_row;
1782 /* FIXME: MsiViewFetch should set rec index 0 to some ID that
1783 * sets the fetched record apart from other records
1786 if (!tv->table)
1787 return ERROR_INVALID_PARAMETER;
1789 r = msi_table_find_row(tv, rec, &new_row, NULL);
1790 if (r != ERROR_SUCCESS)
1792 ERR("can't find row to modify\n");
1793 return ERROR_FUNCTION_FAILED;
1796 /* the row cannot be changed */
1797 if (row != new_row)
1798 return ERROR_FUNCTION_FAILED;
1800 return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1);
1803 static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
1805 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1806 UINT r, row;
1808 if (!tv->table)
1809 return ERROR_INVALID_PARAMETER;
1811 r = msi_table_find_row(tv, rec, &row, NULL);
1812 if (r == ERROR_SUCCESS)
1813 return TABLE_set_row(view, row, rec, (1 << tv->num_cols) - 1);
1814 else
1815 return TABLE_insert_row( view, rec, -1, FALSE );
1818 static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row )
1820 MSIRECORD *curr;
1821 UINT r, i, count;
1823 r = TABLE_get_row(view, row, &curr);
1824 if (r != ERROR_SUCCESS)
1825 return r;
1827 /* Close the original record */
1828 MSI_CloseRecord(&rec->hdr);
1830 count = MSI_RecordGetFieldCount(rec);
1831 for (i = 0; i < count; i++)
1832 MSI_RecordCopyField(curr, i + 1, rec, i + 1);
1834 msiobj_release(&curr->hdr);
1835 return ERROR_SUCCESS;
1838 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1839 MSIRECORD *rec, UINT row)
1841 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1842 UINT r, frow, column;
1844 TRACE("%p %d %p\n", view, eModifyMode, rec );
1846 switch (eModifyMode)
1848 case MSIMODIFY_DELETE:
1849 r = TABLE_delete_row( view, row );
1850 break;
1851 case MSIMODIFY_VALIDATE_NEW:
1852 r = table_validate_new( tv, rec, &column );
1853 if (r != ERROR_SUCCESS)
1855 tv->view.error = MSIDBERROR_DUPLICATEKEY;
1856 tv->view.error_column = tv->columns[column].colname;
1857 r = ERROR_INVALID_DATA;
1859 break;
1861 case MSIMODIFY_INSERT:
1862 r = table_validate_new( tv, rec, NULL );
1863 if (r != ERROR_SUCCESS)
1864 break;
1865 r = TABLE_insert_row( view, rec, -1, FALSE );
1866 break;
1868 case MSIMODIFY_INSERT_TEMPORARY:
1869 r = table_validate_new( tv, rec, NULL );
1870 if (r != ERROR_SUCCESS)
1871 break;
1872 r = TABLE_insert_row( view, rec, -1, TRUE );
1873 break;
1875 case MSIMODIFY_REFRESH:
1876 r = msi_refresh_record( view, rec, row );
1877 break;
1879 case MSIMODIFY_UPDATE:
1880 r = msi_table_update( view, rec, row );
1881 break;
1883 case MSIMODIFY_ASSIGN:
1884 r = msi_table_assign( view, rec );
1885 break;
1887 case MSIMODIFY_MERGE:
1888 /* check row that matches this record */
1889 r = msi_table_find_row( tv, rec, &frow, &column );
1890 if (r != ERROR_SUCCESS)
1892 r = table_validate_new( tv, rec, NULL );
1893 if (r == ERROR_SUCCESS)
1894 r = TABLE_insert_row( view, rec, -1, FALSE );
1896 break;
1898 case MSIMODIFY_REPLACE:
1899 case MSIMODIFY_VALIDATE:
1900 case MSIMODIFY_VALIDATE_FIELD:
1901 case MSIMODIFY_VALIDATE_DELETE:
1902 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1903 r = ERROR_CALL_NOT_IMPLEMENTED;
1904 break;
1906 default:
1907 r = ERROR_INVALID_DATA;
1910 return r;
1913 static UINT TABLE_delete( struct tagMSIVIEW *view )
1915 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1917 TRACE("%p\n", view );
1919 tv->table = NULL;
1920 tv->columns = NULL;
1922 msi_free( tv );
1924 return ERROR_SUCCESS;
1927 static UINT TABLE_add_ref(struct tagMSIVIEW *view)
1929 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1931 TRACE( "%p, %ld\n", view, tv->table->ref_count );
1932 return InterlockedIncrement(&tv->table->ref_count);
1935 static UINT TABLE_remove_column(struct tagMSIVIEW *view, UINT number)
1937 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1938 MSIRECORD *rec = NULL;
1939 MSIVIEW *columns = NULL;
1940 UINT row, r;
1942 if (tv->table->col_count != number)
1943 return ERROR_BAD_QUERY_SYNTAX;
1945 if (tv->table->colinfo[number-1].type & MSITYPE_TEMPORARY)
1947 UINT size = tv->table->colinfo[number-1].offset;
1948 tv->table->col_count--;
1949 tv->table->colinfo = msi_realloc( tv->table->colinfo, sizeof(*tv->table->colinfo) * tv->table->col_count );
1951 for (row = 0; row < tv->table->row_count; row++)
1952 tv->table->data[row] = msi_realloc( tv->table->data[row], size );
1953 return ERROR_SUCCESS;
1956 rec = MSI_CreateRecord(2);
1957 if (!rec)
1958 return ERROR_OUTOFMEMORY;
1960 MSI_RecordSetStringW(rec, 1, tv->name);
1961 MSI_RecordSetInteger(rec, 2, number);
1963 r = TABLE_CreateView(tv->db, L"_Columns", &columns);
1964 if (r != ERROR_SUCCESS)
1966 msiobj_release(&rec->hdr);
1967 return r;
1970 r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL);
1971 if (r != ERROR_SUCCESS)
1972 goto done;
1974 r = TABLE_delete_row(columns, row);
1975 if (r != ERROR_SUCCESS)
1976 goto done;
1978 msi_update_table_columns(tv->db, tv->name);
1980 done:
1981 msiobj_release(&rec->hdr);
1982 columns->ops->delete(columns);
1983 return r;
1986 static UINT TABLE_release(struct tagMSIVIEW *view)
1988 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1989 INT ref = tv->table->ref_count;
1990 UINT r;
1991 INT i;
1993 TRACE("%p %d\n", view, ref);
1995 ref = InterlockedDecrement(&tv->table->ref_count);
1996 if (ref == 0)
1998 for (i = tv->table->col_count - 1; i >= 0; i--)
2000 if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
2002 r = TABLE_remove_column(view, tv->table->colinfo[i].number);
2003 if (r != ERROR_SUCCESS)
2004 break;
2006 else
2008 break;
2012 if (!tv->table->col_count)
2014 list_remove(&tv->table->entry);
2015 free_table(tv->table);
2016 TABLE_delete(view);
2020 return ref;
2023 static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR column,
2024 INT type, BOOL hold)
2026 UINT i, r, table_id, col_id, size, offset;
2027 BOOL temporary = type & MSITYPE_TEMPORARY;
2028 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2029 MSICOLUMNINFO *colinfo;
2031 if (temporary && !hold && !tv->table->ref_count)
2032 return ERROR_SUCCESS;
2034 if (!temporary && tv->table->col_count &&
2035 tv->table->colinfo[tv->table->col_count-1].type & MSITYPE_TEMPORARY)
2036 return ERROR_BAD_QUERY_SYNTAX;
2038 for (i = 0; i < tv->table->col_count; i++)
2040 if (!wcscmp(tv->table->colinfo[i].colname, column))
2041 return ERROR_BAD_QUERY_SYNTAX;
2044 colinfo = msi_realloc(tv->table->colinfo, sizeof(*tv->table->colinfo) * (tv->table->col_count + 1));
2045 if (!colinfo)
2046 return ERROR_OUTOFMEMORY;
2047 tv->table->colinfo = colinfo;
2049 r = msi_string2id( tv->db->strings, tv->name, -1, &table_id );
2050 if (r != ERROR_SUCCESS)
2051 return r;
2052 col_id = msi_add_string( tv->db->strings, column, -1, !temporary );
2054 colinfo[tv->table->col_count].tablename = msi_string_lookup( tv->db->strings, table_id, NULL );
2055 colinfo[tv->table->col_count].number = tv->table->col_count + 1;
2056 colinfo[tv->table->col_count].colname = msi_string_lookup( tv->db->strings, col_id, NULL );
2057 colinfo[tv->table->col_count].type = type;
2058 colinfo[tv->table->col_count].offset = 0;
2059 colinfo[tv->table->col_count].hash_table = NULL;
2060 tv->table->col_count++;
2062 table_calc_column_offsets( tv->db, tv->table->colinfo, tv->table->col_count);
2064 size = msi_table_get_row_size( tv->db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES );
2065 offset = tv->table->colinfo[tv->table->col_count - 1].offset;
2066 for (i = 0; i < tv->table->row_count; i++)
2068 BYTE *data = msi_realloc( tv->table->data[i], size );
2069 if (!data)
2071 tv->table->col_count--;
2072 return ERROR_OUTOFMEMORY;
2075 tv->table->data[i] = data;
2076 memset(data + offset, 0, size - offset);
2079 if (!temporary)
2081 MSIVIEW *columns;
2082 MSIRECORD *rec;
2084 rec = MSI_CreateRecord(4);
2085 if (!rec)
2087 tv->table->col_count--;
2088 return ERROR_OUTOFMEMORY;
2091 MSI_RecordSetStringW(rec, 1, tv->name);
2092 MSI_RecordSetInteger(rec, 2, tv->table->col_count);
2093 MSI_RecordSetStringW(rec, 3, column);
2094 MSI_RecordSetInteger(rec, 4, type);
2096 r = TABLE_CreateView(tv->db, L"_Columns", &columns);
2097 if (r != ERROR_SUCCESS)
2099 tv->table->col_count--;
2100 msiobj_release(&rec->hdr);
2101 return r;
2104 r = TABLE_insert_row(columns, rec, -1, FALSE);
2105 columns->ops->delete(columns);
2106 msiobj_release(&rec->hdr);
2107 if (r != ERROR_SUCCESS)
2109 tv->table->col_count--;
2110 return r;
2113 if (hold)
2114 TABLE_add_ref(view);
2115 return ERROR_SUCCESS;
2118 static UINT TABLE_drop(struct tagMSIVIEW *view)
2120 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2121 MSIVIEW *tables = NULL;
2122 MSIRECORD *rec = NULL;
2123 UINT r, row;
2124 INT i;
2126 TRACE("dropping table %s\n", debugstr_w(tv->name));
2128 for (i = tv->table->col_count - 1; i >= 0; i--)
2130 r = TABLE_remove_column(view, tv->table->colinfo[i].number);
2131 if (r != ERROR_SUCCESS)
2132 return r;
2135 rec = MSI_CreateRecord(1);
2136 if (!rec)
2137 return ERROR_OUTOFMEMORY;
2139 MSI_RecordSetStringW(rec, 1, tv->name);
2141 r = TABLE_CreateView(tv->db, L"_Tables", &tables);
2142 if (r != ERROR_SUCCESS)
2144 msiobj_release(&rec->hdr);
2145 return r;
2148 r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row, NULL);
2149 if (r != ERROR_SUCCESS)
2150 goto done;
2152 r = TABLE_delete_row(tables, row);
2153 if (r != ERROR_SUCCESS)
2154 goto done;
2156 list_remove(&tv->table->entry);
2157 free_table(tv->table);
2159 done:
2160 msiobj_release(&rec->hdr);
2161 tables->ops->delete(tables);
2163 return r;
2166 static const MSIVIEWOPS table_ops =
2168 TABLE_fetch_int,
2169 TABLE_fetch_stream,
2170 TABLE_set_int,
2171 TABLE_set_string,
2172 TABLE_set_stream,
2173 TABLE_set_row,
2174 TABLE_insert_row,
2175 TABLE_delete_row,
2176 TABLE_execute,
2177 TABLE_close,
2178 TABLE_get_dimensions,
2179 TABLE_get_column_info,
2180 TABLE_modify,
2181 TABLE_delete,
2182 TABLE_add_ref,
2183 TABLE_release,
2184 TABLE_add_column,
2185 NULL,
2186 TABLE_drop,
2189 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
2191 MSITABLEVIEW *tv ;
2192 UINT r, sz;
2194 TRACE("%p %s %p\n", db, debugstr_w(name), view );
2196 if ( !wcscmp( name, L"_Streams" ) )
2197 return STREAMS_CreateView( db, view );
2198 else if ( !wcscmp( name, L"_Storages" ) )
2199 return STORAGES_CreateView( db, view );
2201 sz = FIELD_OFFSET( MSITABLEVIEW, name[lstrlenW( name ) + 1] );
2202 tv = msi_alloc_zero( sz );
2203 if( !tv )
2204 return ERROR_FUNCTION_FAILED;
2206 r = get_table( db, name, &tv->table );
2207 if( r != ERROR_SUCCESS )
2209 msi_free( tv );
2210 WARN("table not found\n");
2211 return r;
2214 TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count);
2216 /* fill the structure */
2217 tv->view.ops = &table_ops;
2218 tv->db = db;
2219 tv->columns = tv->table->colinfo;
2220 tv->num_cols = tv->table->col_count;
2221 tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES );
2223 TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
2225 *view = (MSIVIEW*) tv;
2226 lstrcpyW( tv->name, name );
2228 return ERROR_SUCCESS;
2231 static WCHAR* create_key_string(MSITABLEVIEW *tv, MSIRECORD *rec)
2233 DWORD i, p, len, key_len = 0;
2234 WCHAR *key;
2236 for (i = 0; i < tv->num_cols; i++)
2238 if (!(tv->columns[i].type & MSITYPE_KEY))
2239 continue;
2240 if (MSI_RecordGetStringW( rec, i+1, NULL, &len ) == ERROR_SUCCESS)
2241 key_len += len;
2242 key_len++;
2245 key = msi_alloc( key_len * sizeof(WCHAR) );
2246 if(!key)
2247 return NULL;
2249 p = 0;
2250 for (i = 0; i < tv->num_cols; i++)
2252 if (!(tv->columns[i].type & MSITYPE_KEY))
2253 continue;
2254 if (p)
2255 key[p++] = '\t';
2256 len = key_len - p;
2257 if (MSI_RecordGetStringW( rec, i+1, key + p, &len ) == ERROR_SUCCESS)
2258 p += len;
2260 return key;
2263 static UINT msi_record_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR name, DWORD *len )
2265 UINT p = 0, i, r;
2266 DWORD l;
2268 l = wcslen( tv->name );
2269 if (name && *len > l)
2270 memcpy(name, tv->name, l * sizeof(WCHAR));
2271 p += l;
2273 for ( i = 0; i < tv->num_cols; i++ )
2275 if (!(tv->columns[i].type & MSITYPE_KEY))
2276 continue;
2278 if (name && *len > p + 1)
2279 name[p] = '.';
2280 p++;
2282 l = (*len > p ? *len - p : 0);
2283 r = MSI_RecordGetStringW( rec, i + 1, name ? name + p : NULL, &l );
2284 if (r != ERROR_SUCCESS)
2285 return r;
2286 p += l;
2289 if (name && *len > p)
2290 name[p] = 0;
2292 *len = p;
2293 return ERROR_SUCCESS;
2296 static UINT TransformView_fetch_int( MSIVIEW *view, UINT row, UINT col, UINT *val )
2298 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2300 if (!tv->table || col > tv->table->col_count)
2302 *val = 0;
2303 return ERROR_SUCCESS;
2305 return TABLE_fetch_int( view, row, col, val );
2308 static UINT TransformView_fetch_stream( MSIVIEW *view, UINT row, UINT col, IStream **stm )
2310 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2312 if (!tv->table || col > tv->table->col_count)
2314 *stm = NULL;
2315 return ERROR_SUCCESS;
2317 return TABLE_fetch_stream( view, row, col, stm );
2320 static UINT TransformView_set_row( MSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
2322 static const WCHAR query_pfx[] =
2323 L"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`, `Data`, `Current`) VALUES (1, '";
2325 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2326 WCHAR buf[256], *query;
2327 MSIRECORD *old_rec;
2328 MSIQUERY *q;
2329 WCHAR *key;
2330 UINT i, p, r, qlen;
2331 DWORD len;
2333 if (!wcscmp( tv->name, L"_Columns" ))
2335 ERR( "trying to modify existing column\n" );
2336 return ERROR_INSTALL_TRANSFORM_FAILURE;
2339 if (!wcscmp( tv->name, L"_Tables" ))
2341 ERR( "trying to modify existing table\n" );
2342 return ERROR_INSTALL_TRANSFORM_FAILURE;
2345 key = create_key_string( tv, rec );
2346 if (!key)
2347 return ERROR_OUTOFMEMORY;
2349 r = msi_view_get_row( tv->db, view, row, &old_rec );
2350 if (r != ERROR_SUCCESS)
2351 old_rec = NULL;
2353 for (i = 0; i < tv->num_cols; i++)
2355 if (!(mask & (1 << i)))
2356 continue;
2357 if (tv->columns[i].type & MSITYPE_KEY)
2358 continue;
2360 qlen = p = ARRAY_SIZE( query_pfx ) - 1;
2361 qlen += wcslen( tv->name ) + 3; /* strlen("','") */
2362 qlen += wcslen( tv->columns[i].colname ) + 3;
2363 qlen += wcslen( key ) + 3;
2364 if (MSITYPE_IS_BINARY( tv->columns[i].type ))
2365 r = msi_record_stream_name( tv, rec, NULL, &len );
2366 else
2367 r = MSI_RecordGetStringW( rec, i + 1, NULL, &len );
2368 if (r != ERROR_SUCCESS)
2370 if (old_rec)
2371 msiobj_release( &old_rec->hdr );
2372 msi_free( key );
2373 return r;
2375 qlen += len + 3;
2376 if (old_rec && (r = MSI_RecordGetStringW( old_rec, i+1, NULL, &len )))
2378 msiobj_release( &old_rec->hdr );
2379 msi_free( key );
2380 return r;
2382 qlen += len + 3; /* strlen("')") + 1 */
2384 if (qlen > ARRAY_SIZE(buf))
2386 query = msi_alloc( qlen * sizeof(WCHAR) );
2387 if (!query)
2389 if (old_rec)
2390 msiobj_release( &old_rec->hdr );
2391 msi_free( key );
2392 return ERROR_OUTOFMEMORY;
2395 else
2397 query = buf;
2400 memcpy( query, query_pfx, p * sizeof(WCHAR) );
2401 len = wcslen( tv->name );
2402 memcpy( query + p, tv->name, len * sizeof(WCHAR) );
2403 p += len;
2404 query[p++] = '\'';
2405 query[p++] = ',';
2406 query[p++] = '\'';
2407 len = wcslen( tv->columns[i].colname );
2408 memcpy( query + p, tv->columns[i].colname, len * sizeof(WCHAR) );
2409 p += len;
2410 query[p++] = '\'';
2411 query[p++] = ',';
2412 query[p++] = '\'';
2413 len = wcslen( key );
2414 memcpy( query + p, key, len * sizeof(WCHAR) );
2415 p += len;
2416 query[p++] = '\'';
2417 query[p++] = ',';
2418 query[p++] = '\'';
2419 len = qlen - p;
2420 if (MSITYPE_IS_BINARY( tv->columns[i].type ))
2421 msi_record_stream_name( tv, rec, query + p, &len );
2422 else
2423 MSI_RecordGetStringW( rec, i + 1, query + p, &len );
2424 p += len;
2425 query[p++] = '\'';
2426 query[p++] = ',';
2427 query[p++] = '\'';
2428 if (old_rec)
2430 len = qlen - p;
2431 MSI_RecordGetStringW( old_rec, i + 1, query + p, &len );
2432 p += len;
2434 query[p++] = '\'';
2435 query[p++] = ')';
2436 query[p++] = 0;
2438 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2439 if (query != buf)
2440 msi_free( query );
2441 if (r != ERROR_SUCCESS)
2443 if (old_rec)
2444 msiobj_release( &old_rec->hdr );
2445 msi_free( key );
2446 return r;
2449 r = MSI_ViewExecute( q, NULL );
2450 msiobj_release( &q->hdr );
2451 if (r != ERROR_SUCCESS)
2453 if (old_rec)
2454 msiobj_release( &old_rec->hdr );
2455 msi_free( key );
2456 return r;
2460 if (old_rec)
2461 msiobj_release( &old_rec->hdr );
2462 msi_free( key );
2463 return ERROR_SUCCESS;
2466 static UINT TransformView_create_table( MSITABLEVIEW *tv, MSIRECORD *rec )
2468 static const WCHAR query_fmt[] =
2469 L"INSERT INTO `_TransformView` (`Table`, `Column`, `new`) VALUES ('%s', 'CREATE', 1)";
2471 WCHAR buf[256], *query = buf;
2472 const WCHAR *name;
2473 MSIQUERY *q;
2474 int len;
2475 UINT r;
2477 name = msi_record_get_string( rec, 1, &len );
2478 if (!name)
2479 return ERROR_INSTALL_TRANSFORM_FAILURE;
2481 len = _snwprintf( NULL, 0, query_fmt, name ) + 1;
2482 if (len > ARRAY_SIZE(buf))
2484 query = msi_alloc( len * sizeof(WCHAR) );
2485 if (!query)
2486 return ERROR_OUTOFMEMORY;
2488 swprintf( query, len, query_fmt, name );
2490 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2491 if (query != buf)
2492 msi_free( query );
2493 if (r != ERROR_SUCCESS)
2494 return r;
2496 r = MSI_ViewExecute( q, NULL );
2497 msiobj_release( &q->hdr );
2498 return r;
2501 static UINT TransformView_add_column( MSITABLEVIEW *tv, MSIRECORD *rec )
2503 static const WCHAR query_pfx[] =
2504 L"INSERT INTO `_TransformView` (`new`, `Table`, `Current`, `Column`, `Data`) VALUES (1, '";
2506 WCHAR buf[256], *query = buf;
2507 UINT i, p, r, qlen;
2508 DWORD len;
2509 MSIQUERY *q;
2511 qlen = p = wcslen( query_pfx );
2512 for (i = 1; i <= 4; i++)
2514 r = MSI_RecordGetStringW( rec, i, NULL, &len );
2515 if (r != ERROR_SUCCESS)
2516 return r;
2517 qlen += len + 3; /* strlen( "','" ) */
2520 if (qlen > ARRAY_SIZE(buf))
2522 query = msi_alloc( len * sizeof(WCHAR) );
2523 qlen = len;
2524 if (!query)
2525 return ERROR_OUTOFMEMORY;
2528 memcpy( query, query_pfx, p * sizeof(WCHAR) );
2529 for (i = 1; i <= 4; i++)
2531 len = qlen - p;
2532 MSI_RecordGetStringW( rec, i, query + p, &len );
2533 p += len;
2534 query[p++] = '\'';
2535 if (i != 4)
2537 query[p++] = ',';
2538 query[p++] = '\'';
2541 query[p++] = ')';
2542 query[p++] = 0;
2544 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2545 if (query != buf)
2546 msi_free( query );
2547 if (r != ERROR_SUCCESS)
2548 return r;
2550 r = MSI_ViewExecute( q, NULL );
2551 msiobj_release( &q->hdr );
2552 return r;
2555 static UINT TransformView_insert_row( MSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
2557 static const WCHAR query_fmt[] =
2558 L"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`) VALUES (1, '%s', 'INSERT', '%s')";
2560 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2561 WCHAR buf[256], *query = buf;
2562 MSIQUERY *q;
2563 WCHAR *key;
2564 int len;
2565 UINT r;
2567 if (!wcscmp(tv->name, L"_Tables"))
2568 return TransformView_create_table( tv, rec );
2570 if (!wcscmp(tv->name, L"_Columns"))
2571 return TransformView_add_column( tv, rec );
2573 key = create_key_string( tv, rec );
2574 if (!key)
2575 return ERROR_OUTOFMEMORY;
2577 len = _snwprintf( NULL, 0, query_fmt, tv->name, key ) + 1;
2578 if (len > ARRAY_SIZE(buf))
2580 query = msi_alloc( len * sizeof(WCHAR) );
2581 if (!query)
2583 msi_free( key );
2584 return ERROR_OUTOFMEMORY;
2587 swprintf( query, len, query_fmt, tv->name, key );
2588 msi_free( key );
2590 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2591 if (query != buf)
2592 msi_free( query );
2593 if (r != ERROR_SUCCESS)
2594 return r;
2596 r = MSI_ViewExecute( q, NULL );
2597 msiobj_release( &q->hdr );
2598 if (r != ERROR_SUCCESS)
2599 return r;
2601 return TransformView_set_row( view, row, rec, ~0 );
2604 static UINT TransformView_drop_table( MSITABLEVIEW *tv, UINT row )
2606 static const WCHAR query_pfx[] = L"INSERT INTO `_TransformView` ( `new`, `Table`, `Column` ) VALUES ( 1, '";
2607 static const WCHAR query_sfx[] = L"', 'DROP' )";
2609 WCHAR buf[256], *query = buf;
2610 UINT r, table_id, len;
2611 const WCHAR *table;
2612 int table_len;
2613 MSIQUERY *q;
2615 r = TABLE_fetch_int( &tv->view, row, 1, &table_id );
2616 if (r != ERROR_SUCCESS)
2617 return r;
2619 table = msi_string_lookup( tv->db->strings, table_id, &table_len );
2620 if (!table)
2621 return ERROR_INSTALL_TRANSFORM_FAILURE;
2623 len = ARRAY_SIZE(query_pfx) - 1 + table_len + ARRAY_SIZE(query_sfx);
2624 if (len > ARRAY_SIZE(buf))
2626 query = msi_alloc( len * sizeof(WCHAR) );
2627 if (!query)
2628 return ERROR_OUTOFMEMORY;
2631 memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) );
2632 len = ARRAY_SIZE(query_pfx) - 1;
2633 memcpy( query + len, table, table_len * sizeof(WCHAR) );
2634 len += table_len;
2635 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2637 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2638 if (query != buf)
2639 msi_free( query );
2640 if (r != ERROR_SUCCESS)
2641 return r;
2643 r = MSI_ViewExecute( q, NULL );
2644 msiobj_release( &q->hdr );
2645 return r;
2648 static UINT TransformView_delete_row( MSIVIEW *view, UINT row )
2650 static const WCHAR query_pfx[] = L"INSERT INTO `_TransformView` ( `new`, `Table`, `Column`, `Row`) VALUES ( 1, '";
2651 static const WCHAR query_column[] = L"', 'DELETE', '";
2652 static const WCHAR query_sfx[] = L"')";
2654 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2655 WCHAR *key, buf[256], *query = buf;
2656 UINT r, len, name_len, key_len;
2657 MSIRECORD *rec;
2658 MSIQUERY *q;
2660 if (!wcscmp( tv->name, L"_Columns" ))
2662 ERR("trying to remove column\n");
2663 return ERROR_INSTALL_TRANSFORM_FAILURE;
2666 if (!wcscmp( tv->name, L"_Tables" ))
2667 return TransformView_drop_table( tv, row );
2669 r = msi_view_get_row( tv->db, view, row, &rec );
2670 if (r != ERROR_SUCCESS)
2671 return r;
2673 key = create_key_string( tv, rec );
2674 msiobj_release( &rec->hdr );
2675 if (!key)
2676 return ERROR_OUTOFMEMORY;
2678 name_len = wcslen( tv->name );
2679 key_len = wcslen( key );
2680 len = ARRAY_SIZE(query_pfx) + name_len + ARRAY_SIZE(query_column) + key_len + ARRAY_SIZE(query_sfx) - 2;
2681 if (len > ARRAY_SIZE(buf))
2683 query = msi_alloc( len * sizeof(WCHAR) );
2684 if (!query)
2686 msi_free( tv );
2687 msi_free( key );
2688 return ERROR_OUTOFMEMORY;
2692 memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) );
2693 len = ARRAY_SIZE(query_pfx) - 1;
2694 memcpy( query + len, tv->name, name_len * sizeof(WCHAR) );
2695 len += name_len;
2696 memcpy( query + len, query_column, ARRAY_SIZE(query_column) * sizeof(WCHAR) );
2697 len += ARRAY_SIZE(query_column) - 1;
2698 memcpy( query + len, key, key_len * sizeof(WCHAR) );
2699 len += key_len;
2700 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2701 msi_free( key );
2703 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2704 if (query != buf)
2705 msi_free( query );
2706 if (r != ERROR_SUCCESS)
2707 return r;
2709 r = MSI_ViewExecute( q, NULL );
2710 msiobj_release( &q->hdr );
2711 return r;
2714 static UINT TransformView_execute( MSIVIEW *view, MSIRECORD *record )
2716 return ERROR_SUCCESS;
2719 static UINT TransformView_close( MSIVIEW *view )
2721 return ERROR_SUCCESS;
2724 static UINT TransformView_get_dimensions( MSIVIEW *view, UINT *rows, UINT *cols )
2726 return TABLE_get_dimensions( view, rows, cols );
2729 static UINT TransformView_get_column_info( MSIVIEW *view, UINT n, LPCWSTR *name, UINT *type,
2730 BOOL *temporary, LPCWSTR *table_name )
2732 return TABLE_get_column_info( view, n, name, type, temporary, table_name );
2735 static UINT TransformView_delete( MSIVIEW *view )
2737 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2738 if (!tv->table || tv->columns != tv->table->colinfo)
2739 msi_free( tv->columns );
2740 return TABLE_delete( view );
2743 static const MSIVIEWOPS transform_view_ops =
2745 TransformView_fetch_int,
2746 TransformView_fetch_stream,
2747 NULL,
2748 NULL,
2749 NULL,
2750 TransformView_set_row,
2751 TransformView_insert_row,
2752 TransformView_delete_row,
2753 TransformView_execute,
2754 TransformView_close,
2755 TransformView_get_dimensions,
2756 TransformView_get_column_info,
2757 NULL,
2758 TransformView_delete,
2759 NULL,
2760 NULL,
2761 NULL,
2762 NULL,
2763 NULL
2766 UINT TransformView_Create( MSIDATABASE *db, string_table *st, LPCWSTR name, MSIVIEW **view )
2768 static const WCHAR query_pfx[] = L"SELECT `Column`, `Data`, `Current` FROM `_TransformView` WHERE `Table`='";
2769 static const WCHAR query_sfx[] = L"' AND `Row` IS NULL AND `Current` IS NOT NULL AND `new` = 1";
2771 WCHAR buf[256], *query = buf;
2772 UINT r, len, name_len, size, add_col;
2773 MSICOLUMNINFO *colinfo;
2774 MSITABLEVIEW *tv;
2775 MSIRECORD *rec;
2776 MSIQUERY *q;
2778 name_len = wcslen( name );
2780 r = TABLE_CreateView( db, name, view );
2781 if (r == ERROR_INVALID_PARAMETER)
2783 /* table does not exist */
2784 size = FIELD_OFFSET( MSITABLEVIEW, name[name_len + 1] );
2785 tv = msi_alloc_zero( size );
2786 if (!tv)
2787 return ERROR_OUTOFMEMORY;
2789 tv->db = db;
2790 memcpy( tv->name, name, name_len * sizeof(WCHAR) );
2791 *view = (MSIVIEW*)tv;
2793 else if (r != ERROR_SUCCESS)
2795 return r;
2797 else
2799 tv = (MSITABLEVIEW*)*view;
2802 tv->view.ops = &transform_view_ops;
2804 len = ARRAY_SIZE(query_pfx) + name_len + ARRAY_SIZE(query_sfx) - 1;
2805 if (len > ARRAY_SIZE(buf))
2807 query = msi_alloc( len * sizeof(WCHAR) );
2808 if (!query)
2810 msi_free( tv );
2811 return ERROR_OUTOFMEMORY;
2814 memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) );
2815 len = ARRAY_SIZE(query_pfx) - 1;
2816 memcpy( query + len, name, name_len * sizeof(WCHAR) );
2817 len += name_len;
2818 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2820 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2821 if (query != buf)
2822 msi_free( query );
2823 if (r != ERROR_SUCCESS)
2825 msi_free( tv );
2826 return r;
2829 r = MSI_ViewExecute( q, NULL );
2830 if (r != ERROR_SUCCESS)
2832 msi_free( tv );
2833 return r;
2836 r = q->view->ops->get_dimensions( q->view, &add_col, NULL );
2837 if (r != ERROR_SUCCESS)
2839 MSI_ViewClose( q );
2840 msiobj_release( &q->hdr );
2841 msi_free( tv );
2842 return r;
2844 if (!add_col)
2846 MSI_ViewClose( q );
2847 msiobj_release( &q->hdr );
2848 return ERROR_SUCCESS;
2851 colinfo = msi_alloc_zero( (add_col + tv->num_cols) * sizeof(*colinfo) );
2852 if (!colinfo)
2854 MSI_ViewClose( q );
2855 msiobj_release( &q->hdr );
2856 msi_free( tv );
2857 return r;
2860 while (MSI_ViewFetch( q, &rec ) == ERROR_SUCCESS)
2862 int name_len;
2863 const WCHAR *name = msi_record_get_string( rec, 1, &name_len );
2864 const WCHAR *type = msi_record_get_string( rec, 2, NULL );
2865 UINT name_id, idx;
2867 idx = _wtoi( msi_record_get_string(rec, 3, NULL) );
2868 colinfo[idx - 1].number = idx;
2869 colinfo[idx - 1].type = _wtoi( type );
2871 r = msi_string2id( st, name, name_len, &name_id );
2872 if (r == ERROR_SUCCESS)
2873 colinfo[idx - 1].colname = msi_string_lookup( st, name_id, NULL );
2874 else
2875 ERR( "column name %s is not defined in strings table\n", wine_dbgstr_w(name) );
2876 msiobj_release( &rec->hdr );
2878 MSI_ViewClose( q );
2879 msiobj_release( &q->hdr );
2881 memcpy( colinfo, tv->columns, tv->num_cols * sizeof(*colinfo) );
2882 tv->columns = colinfo;
2883 tv->num_cols += add_col;
2884 return ERROR_SUCCESS;
2887 UINT MSI_CommitTables( MSIDATABASE *db )
2889 UINT r, bytes_per_strref;
2890 HRESULT hr;
2891 MSITABLE *table = NULL;
2893 TRACE("%p\n",db);
2895 r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref );
2896 if( r != ERROR_SUCCESS )
2898 WARN("failed to save string table r=%08x\n",r);
2899 return r;
2902 LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
2904 r = save_table( db, table, bytes_per_strref );
2905 if( r != ERROR_SUCCESS )
2907 WARN("failed to save table %s (r=%08x)\n",
2908 debugstr_w(table->name), r);
2909 return r;
2913 hr = IStorage_Commit( db->storage, 0 );
2914 if (FAILED( hr ))
2916 WARN( "failed to commit changes %#lx\n", hr );
2917 r = ERROR_FUNCTION_FAILED;
2919 return r;
2922 MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
2924 MSITABLE *t;
2925 UINT r;
2927 TRACE("%p %s\n", db, debugstr_w(table));
2929 if (!table)
2930 return MSICONDITION_ERROR;
2932 r = get_table( db, table, &t );
2933 if (r != ERROR_SUCCESS)
2934 return MSICONDITION_NONE;
2936 return t->persistent;
2939 static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes)
2941 UINT ret = 0, i;
2943 for (i = 0; i < bytes; i++)
2944 ret += (data[col + i] << i * 8);
2946 return ret;
2949 static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname )
2951 UINT r;
2952 DWORD len;
2953 WCHAR *name;
2955 TRACE("%p %p\n", tv, rec);
2957 r = msi_record_stream_name( tv, rec, NULL, &len );
2958 if (r != ERROR_SUCCESS)
2959 return r;
2960 len++;
2962 name = msi_alloc( len * sizeof(WCHAR) );
2963 if (!name)
2964 return ERROR_OUTOFMEMORY;
2966 r = msi_record_stream_name( tv, rec, name, &len );
2967 if (r != ERROR_SUCCESS)
2969 msi_free( name );
2970 return r;
2973 *pstname = encode_streamname( FALSE, name );
2974 msi_free( name );
2975 return ERROR_SUCCESS;
2978 static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st,
2979 IStorage *stg, const BYTE *rawdata, UINT bytes_per_strref )
2981 UINT i, val, ofs = 0;
2982 USHORT mask;
2983 MSICOLUMNINFO *columns = tv->columns;
2984 MSIRECORD *rec;
2986 mask = rawdata[0] | (rawdata[1] << 8);
2987 rawdata += 2;
2989 rec = MSI_CreateRecord( tv->num_cols );
2990 if( !rec )
2991 return rec;
2993 TRACE("row ->\n");
2994 for( i=0; i<tv->num_cols; i++ )
2996 if ( (mask&1) && (i>=(mask>>8)) )
2997 break;
2998 /* all keys must be present */
2999 if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
3000 continue;
3002 if( MSITYPE_IS_BINARY(tv->columns[i].type) )
3004 LPWSTR encname;
3005 IStream *stm = NULL;
3006 UINT r;
3008 ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref );
3010 r = msi_record_encoded_stream_name( tv, rec, &encname );
3011 if ( r != ERROR_SUCCESS )
3013 msiobj_release( &rec->hdr );
3014 return NULL;
3016 r = IStorage_OpenStream( stg, encname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
3017 if ( r != ERROR_SUCCESS )
3019 msiobj_release( &rec->hdr );
3020 msi_free( encname );
3021 return NULL;
3024 MSI_RecordSetStream( rec, i+1, stm );
3025 TRACE(" field %d [%s]\n", i+1, debugstr_w(encname));
3026 msi_free( encname );
3028 else if( columns[i].type & MSITYPE_STRING )
3030 int len;
3031 const WCHAR *sval;
3033 val = read_raw_int(rawdata, ofs, bytes_per_strref);
3034 sval = msi_string_lookup( st, val, &len );
3035 msi_record_set_string( rec, i+1, sval, len );
3036 TRACE(" field %d [%s]\n", i+1, debugstr_wn(sval, len));
3037 ofs += bytes_per_strref;
3039 else
3041 UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref );
3042 switch( n )
3044 case 2:
3045 val = read_raw_int(rawdata, ofs, n);
3046 if (val)
3047 MSI_RecordSetInteger( rec, i+1, val-0x8000 );
3048 TRACE(" field %d [0x%04x]\n", i+1, val );
3049 break;
3050 case 4:
3051 val = read_raw_int(rawdata, ofs, n);
3052 if (val)
3053 MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
3054 TRACE(" field %d [0x%08x]\n", i+1, val );
3055 break;
3056 default:
3057 ERR("oops - unknown column width %d\n", n);
3058 break;
3060 ofs += n;
3063 return rec;
3066 static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize )
3068 UINT i;
3069 for (i = 0; i < rawsize / 2; i++)
3071 int len;
3072 const WCHAR *sval = msi_string_lookup( st, rawdata[i], &len );
3073 MESSAGE(" %04x %s\n", rawdata[i], debugstr_wn(sval, len) );
3077 static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec )
3079 UINT i, r, *data;
3081 data = msi_alloc( tv->num_cols *sizeof (UINT) );
3082 for( i=0; i<tv->num_cols; i++ )
3084 data[i] = 0;
3086 if ( ~tv->columns[i].type & MSITYPE_KEY )
3087 continue;
3089 /* turn the transform column value into a row value */
3090 if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
3091 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
3093 int len;
3094 const WCHAR *str = msi_record_get_string( rec, i+1, &len );
3095 if (str)
3097 r = msi_string2id( tv->db->strings, str, len, &data[i] );
3099 /* if there's no matching string in the string table,
3100 these keys can't match any record, so fail now. */
3101 if (r != ERROR_SUCCESS)
3103 msi_free( data );
3104 return NULL;
3107 else data[i] = 0;
3109 else
3111 if (int_to_table_storage( tv, i + 1, MSI_RecordGetInteger( rec, i + 1 ), &data[i] ))
3113 msi_free( data );
3114 return NULL;
3118 return data;
3121 static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column )
3123 UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
3125 for( i=0; i<tv->num_cols; i++ )
3127 if ( ~tv->columns[i].type & MSITYPE_KEY )
3128 continue;
3130 /* turn the transform column value into a row value */
3131 r = TABLE_fetch_int( &tv->view, row, i+1, &x );
3132 if ( r != ERROR_SUCCESS )
3134 ERR("TABLE_fetch_int shouldn't fail here\n");
3135 break;
3138 /* if this key matches, move to the next column */
3139 if ( x != data[i] )
3141 ret = ERROR_FUNCTION_FAILED;
3142 break;
3144 if (column) *column = i;
3145 ret = ERROR_SUCCESS;
3147 return ret;
3150 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column )
3152 UINT i, r = ERROR_FUNCTION_FAILED, *data;
3154 data = msi_record_to_row( tv, rec );
3155 if( !data )
3156 return r;
3157 for( i = 0; i < tv->table->row_count; i++ )
3159 r = msi_row_matches( tv, i, data, column );
3160 if( r == ERROR_SUCCESS )
3162 *row = i;
3163 break;
3166 msi_free( data );
3167 return r;
3170 typedef struct
3172 struct list entry;
3173 LPWSTR name;
3174 } TRANSFORMDATA;
3176 static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
3177 string_table *st, TRANSFORMDATA *transform,
3178 UINT bytes_per_strref, int err_cond )
3180 BYTE *rawdata = NULL;
3181 MSITABLEVIEW *tv = NULL;
3182 UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0;
3183 MSIRECORD *rec = NULL;
3184 WCHAR coltable[32];
3185 const WCHAR *name;
3187 if (!transform)
3188 return ERROR_SUCCESS;
3190 name = transform->name;
3192 coltable[0] = 0;
3193 TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
3195 /* read the transform data */
3196 read_stream_data( stg, name, TRUE, &rawdata, &rawsize );
3197 if ( !rawdata )
3199 TRACE("table %s empty\n", debugstr_w(name) );
3200 return ERROR_INVALID_TABLE;
3203 /* create a table view */
3204 if ( err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM )
3205 r = TransformView_Create( db, st, name, (MSIVIEW**) &tv );
3206 else
3207 r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
3208 if( r != ERROR_SUCCESS )
3209 goto err;
3211 r = tv->view.ops->execute( &tv->view, NULL );
3212 if( r != ERROR_SUCCESS )
3213 goto err;
3215 TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
3216 debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
3218 /* interpret the data */
3219 for (n = 0; n < rawsize;)
3221 mask = rawdata[n] | (rawdata[n + 1] << 8);
3222 if (mask & 1)
3225 * if the low bit is set, columns are continuous and
3226 * the number of columns is specified in the high byte
3228 sz = 2;
3229 num_cols = mask >> 8;
3230 if (num_cols > tv->num_cols)
3232 ERR("excess columns in transform: %u > %u\n", num_cols, tv->num_cols);
3233 break;
3236 for (i = 0; i < num_cols; i++)
3238 if( (tv->columns[i].type & MSITYPE_STRING) &&
3239 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
3240 sz += bytes_per_strref;
3241 else
3242 sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
3245 else
3248 * If the low bit is not set, mask is a bitmask.
3249 * Excepting for key fields, which are always present,
3250 * each bit indicates that a field is present in the transform record.
3252 * mask == 0 is a special case ... only the keys will be present
3253 * and it means that this row should be deleted.
3255 sz = 2;
3256 num_cols = tv->num_cols;
3257 for (i = 0; i < num_cols; i++)
3259 if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask))
3261 if ((tv->columns[i].type & MSITYPE_STRING) &&
3262 !MSITYPE_IS_BINARY(tv->columns[i].type))
3263 sz += bytes_per_strref;
3264 else
3265 sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
3270 /* check we didn't run of the end of the table */
3271 if (n + sz > rawsize)
3273 ERR("borked.\n");
3274 dump_table( st, (USHORT *)rawdata, rawsize );
3275 break;
3278 rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref );
3279 if (rec)
3281 WCHAR table[32];
3282 DWORD sz = 32;
3283 UINT number = MSI_NULL_INTEGER;
3284 UINT row = 0;
3286 if (!wcscmp( name, L"_Columns" ))
3288 MSI_RecordGetStringW( rec, 1, table, &sz );
3289 number = MSI_RecordGetInteger( rec, 2 );
3292 * Native msi seems writes nul into the Number (2nd) column of
3293 * the _Columns table when there are new columns
3295 if ( number == MSI_NULL_INTEGER )
3297 /* reset the column number on a new table */
3298 if (wcscmp( coltable, table ))
3300 colcol = 0;
3301 lstrcpyW( coltable, table );
3304 /* fix nul column numbers */
3305 MSI_RecordSetInteger( rec, 2, ++colcol );
3309 if (TRACE_ON(msidb)) dump_record( rec );
3311 if (tv->table)
3312 r = msi_table_find_row( tv, rec, &row, NULL );
3313 else
3314 r = ERROR_FUNCTION_FAILED;
3315 if (r == ERROR_SUCCESS)
3317 if (!mask)
3319 TRACE("deleting row [%d]:\n", row);
3320 r = tv->view.ops->delete_row( &tv->view, row );
3321 if (r != ERROR_SUCCESS)
3322 WARN("failed to delete row %u\n", r);
3324 else if (mask & 1)
3326 TRACE("modifying full row [%d]:\n", row);
3327 r = tv->view.ops->set_row( &tv->view, row, rec, (1 << tv->num_cols) - 1 );
3328 if (r != ERROR_SUCCESS)
3329 WARN("failed to modify row %u\n", r);
3331 else
3333 TRACE("modifying masked row [%d]:\n", row);
3334 r = tv->view.ops->set_row( &tv->view, row, rec, mask );
3335 if (r != ERROR_SUCCESS)
3336 WARN("failed to modify row %u\n", r);
3339 else
3341 TRACE("inserting row\n");
3342 r = tv->view.ops->insert_row( &tv->view, rec, -1, FALSE );
3343 if (r != ERROR_SUCCESS)
3344 WARN("failed to insert row %u\n", r);
3347 if (!(err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM) &&
3348 !wcscmp( name, L"_Columns" ))
3349 msi_update_table_columns( db, table );
3351 msiobj_release( &rec->hdr );
3354 n += sz;
3357 err:
3358 /* no need to free the table, it's associated with the database */
3359 msi_free( rawdata );
3360 if( tv )
3361 tv->view.ops->delete( &tv->view );
3363 return ERROR_SUCCESS;
3367 * msi_table_apply_transform
3369 * Enumerate the table transforms in a transform storage and apply each one.
3371 UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg, int err_cond )
3373 struct list transforms;
3374 IEnumSTATSTG *stgenum = NULL;
3375 TRANSFORMDATA *transform;
3376 TRANSFORMDATA *tables = NULL, *columns = NULL;
3377 HRESULT hr;
3378 STATSTG stat;
3379 string_table *strings;
3380 UINT ret = ERROR_FUNCTION_FAILED;
3381 UINT bytes_per_strref;
3382 BOOL property_update = FALSE;
3383 MSIVIEW *transform_view = NULL;
3385 TRACE("%p %p\n", db, stg );
3387 strings = msi_load_string_table( stg, &bytes_per_strref );
3388 if( !strings )
3389 goto end;
3391 hr = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
3392 if (FAILED( hr ))
3393 goto end;
3395 list_init(&transforms);
3397 while ( TRUE )
3399 MSITABLEVIEW *tv = NULL;
3400 WCHAR name[0x40];
3401 ULONG count = 0;
3403 hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
3404 if (FAILED( hr ) || !count)
3405 break;
3407 decode_streamname( stat.pwcsName, name );
3408 CoTaskMemFree( stat.pwcsName );
3409 if ( name[0] != 0x4840 )
3410 continue;
3412 if ( !wcscmp( name+1, L"_StringPool" ) ||
3413 !wcscmp( name+1, L"_StringData" ) )
3414 continue;
3416 transform = msi_alloc_zero( sizeof(TRANSFORMDATA) );
3417 if ( !transform )
3418 break;
3420 list_add_tail( &transforms, &transform->entry );
3422 transform->name = strdupW( name + 1 );
3424 if ( !wcscmp( transform->name, L"_Tables" ) )
3425 tables = transform;
3426 else if (!wcscmp( transform->name, L"_Columns" ) )
3427 columns = transform;
3428 else if (!wcscmp( transform->name, L"Property" ))
3429 property_update = TRUE;
3431 TRACE("transform contains stream %s\n", debugstr_w(name));
3433 /* load the table */
3434 if (TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv ) != ERROR_SUCCESS)
3435 continue;
3437 if (tv->view.ops->execute( &tv->view, NULL ) != ERROR_SUCCESS)
3439 tv->view.ops->delete( &tv->view );
3440 continue;
3443 tv->view.ops->delete( &tv->view );
3446 if (err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM)
3448 static const WCHAR create_query[] = L"CREATE TABLE `_TransformView` ( "
3449 L"`Table` CHAR(0) NOT NULL TEMPORARY, `Column` CHAR(0) NOT NULL TEMPORARY, "
3450 L"`Row` CHAR(0) TEMPORARY, `Data` CHAR(0) TEMPORARY, `Current` CHAR(0) TEMPORARY "
3451 L"PRIMARY KEY `Table`, `Column`, `Row` ) HOLD";
3453 MSIQUERY *query;
3454 UINT r;
3456 r = MSI_DatabaseOpenViewW( db, create_query, &query );
3457 if (r != ERROR_SUCCESS)
3458 goto end;
3460 r = MSI_ViewExecute( query, NULL );
3461 if (r == ERROR_SUCCESS)
3462 MSI_ViewClose( query );
3463 msiobj_release( &query->hdr );
3464 if (r != ERROR_BAD_QUERY_SYNTAX && r != ERROR_SUCCESS)
3465 goto end;
3467 if (TABLE_CreateView(db, L"_TransformView", &transform_view) != ERROR_SUCCESS)
3468 goto end;
3470 if (r == ERROR_BAD_QUERY_SYNTAX)
3471 transform_view->ops->add_ref( transform_view );
3473 r = transform_view->ops->add_column( transform_view, L"new",
3474 MSITYPE_TEMPORARY | MSITYPE_NULLABLE | 0x402 /* INT */, FALSE );
3475 if (r != ERROR_SUCCESS)
3476 goto end;
3480 * Apply _Tables and _Columns transforms first so that
3481 * the table metadata is correct, and empty tables exist.
3483 ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref, err_cond );
3484 if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
3485 goto end;
3487 ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref, err_cond );
3488 if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
3489 goto end;
3491 ret = ERROR_SUCCESS;
3493 while ( !list_empty( &transforms ) )
3495 transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry );
3497 if ( wcscmp( transform->name, L"_Columns" ) &&
3498 wcscmp( transform->name, L"_Tables" ) &&
3499 ret == ERROR_SUCCESS )
3501 ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref, err_cond );
3504 list_remove( &transform->entry );
3505 msi_free( transform->name );
3506 msi_free( transform );
3509 if ( ret == ERROR_SUCCESS )
3511 append_storage_to_db( db, stg );
3512 if (property_update) msi_clone_properties( db );
3515 end:
3516 if ( stgenum )
3517 IEnumSTATSTG_Release( stgenum );
3518 if ( strings )
3519 msi_destroy_stringtable( strings );
3520 if (transform_view)
3522 struct tagMSITABLE *table = ((MSITABLEVIEW*)transform_view)->table;
3524 if (ret != ERROR_SUCCESS)
3525 transform_view->ops->release( transform_view );
3527 if (!wcscmp(table->colinfo[table->col_count - 1].colname, L"new"))
3528 TABLE_remove_column( transform_view, table->colinfo[table->col_count - 1].number );
3529 transform_view->ops->delete( transform_view );
3532 return ret;