mfplat: Read queue subscriber within the critical section.
[wine/zf.git] / dlls / msi / table.c
blobbfddd6125e9f0ed031289070e64203c81762f128
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 INT 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 %2d -> %s %s\n", n,
225 debugstr_w(stat.pwcsName), debugstr_w(name) );
226 CoTaskMemFree( stat.pwcsName );
227 n++;
230 IEnumSTATSTG_Release( stgenum );
233 UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table,
234 BYTE **pdata, UINT *psz )
236 HRESULT r;
237 UINT ret = ERROR_FUNCTION_FAILED;
238 VOID *data;
239 ULONG sz, count;
240 IStream *stm = NULL;
241 STATSTG stat;
242 LPWSTR encname;
244 encname = encode_streamname(table, stname);
246 TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
248 r = IStorage_OpenStream(stg, encname, NULL,
249 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
250 msi_free( encname );
251 if( FAILED( r ) )
253 WARN("open stream failed r = %08x - empty table?\n", r);
254 return ret;
257 r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
258 if( FAILED( r ) )
260 WARN("open stream failed r = %08x!\n", r);
261 goto end;
264 if( stat.cbSize.QuadPart >> 32 )
266 WARN("Too big!\n");
267 goto end;
270 sz = stat.cbSize.QuadPart;
271 data = msi_alloc( sz );
272 if( !data )
274 WARN("couldn't allocate memory r=%08x!\n", r);
275 ret = ERROR_NOT_ENOUGH_MEMORY;
276 goto end;
279 r = IStream_Read(stm, data, sz, &count );
280 if( FAILED( r ) || ( count != sz ) )
282 msi_free( data );
283 WARN("read stream failed r = %08x!\n", r);
284 goto end;
287 *pdata = data;
288 *psz = sz;
289 ret = ERROR_SUCCESS;
291 end:
292 IStream_Release( stm );
294 return ret;
297 UINT write_stream_data( IStorage *stg, LPCWSTR stname,
298 LPCVOID data, UINT sz, BOOL bTable )
300 HRESULT r;
301 UINT ret = ERROR_FUNCTION_FAILED;
302 ULONG count;
303 IStream *stm = NULL;
304 ULARGE_INTEGER size;
305 LARGE_INTEGER pos;
306 LPWSTR encname;
308 encname = encode_streamname(bTable, stname );
309 r = IStorage_OpenStream( stg, encname, NULL,
310 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
311 if( FAILED(r) )
313 r = IStorage_CreateStream( stg, encname,
314 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
316 msi_free( encname );
317 if( FAILED( r ) )
319 WARN("open stream failed r = %08x\n", r);
320 return ret;
323 size.QuadPart = sz;
324 r = IStream_SetSize( stm, size );
325 if( FAILED( r ) )
327 WARN("Failed to SetSize\n");
328 goto end;
331 pos.QuadPart = 0;
332 r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
333 if( FAILED( r ) )
335 WARN("Failed to Seek\n");
336 goto end;
339 if (sz)
341 r = IStream_Write(stm, data, sz, &count );
342 if( FAILED( r ) || ( count != sz ) )
344 WARN("Failed to Write\n");
345 goto end;
349 ret = ERROR_SUCCESS;
351 end:
352 IStream_Release( stm );
354 return ret;
357 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
359 UINT i;
360 for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table );
363 static void free_table( MSITABLE *table )
365 UINT i;
366 for( i=0; i<table->row_count; i++ )
367 msi_free( table->data[i] );
368 msi_free( table->data );
369 msi_free( table->data_persistent );
370 msi_free_colinfo( table->colinfo, table->col_count );
371 msi_free( table->colinfo );
372 msi_free( table );
375 static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref )
377 const MSICOLUMNINFO *last_col;
379 if (!count)
380 return 0;
382 if (bytes_per_strref != LONG_STR_BYTES)
384 UINT i, size = 0;
385 for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref );
386 return size;
388 last_col = &cols[count - 1];
389 return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref );
392 /* add this table to the list of cached tables in the database */
393 static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg )
395 BYTE *rawdata = NULL;
396 UINT rawsize = 0, i, j, row_size, row_size_mem;
398 TRACE("%s\n",debugstr_w(t->name));
400 row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref );
401 row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES );
403 /* if we can't read the table, just assume that it's empty */
404 read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize );
405 if( !rawdata )
406 return ERROR_SUCCESS;
408 TRACE("Read %d bytes\n", rawsize );
410 if( rawsize % row_size )
412 WARN("Table size is invalid %d/%d\n", rawsize, row_size );
413 goto err;
416 if ((t->row_count = rawsize / row_size))
418 if (!(t->data = msi_alloc_zero( t->row_count * sizeof(USHORT *) ))) goto err;
419 if (!(t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL) ))) goto err;
422 /* transpose all the data */
423 TRACE("Transposing data from %d rows\n", t->row_count );
424 for (i = 0; i < t->row_count; i++)
426 UINT ofs = 0, ofs_mem = 0;
428 t->data[i] = msi_alloc( row_size_mem );
429 if( !t->data[i] )
430 goto err;
431 t->data_persistent[i] = TRUE;
433 for (j = 0; j < t->col_count; j++)
435 UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
436 UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref );
437 UINT k;
439 if ( n != 2 && n != 3 && n != 4 )
441 ERR("oops - unknown column width %d\n", n);
442 goto err;
444 if (t->colinfo[j].type & MSITYPE_STRING && n < m)
446 for (k = 0; k < m; k++)
448 if (k < n)
449 t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
450 else
451 t->data[i][ofs_mem + k] = 0;
454 else
456 for (k = 0; k < n; k++)
457 t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
459 ofs_mem += m;
460 ofs += n;
464 msi_free( rawdata );
465 return ERROR_SUCCESS;
466 err:
467 msi_free( rawdata );
468 return ERROR_FUNCTION_FAILED;
471 void free_cached_tables( MSIDATABASE *db )
473 while( !list_empty( &db->tables ) )
475 MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
477 list_remove( &t->entry );
478 free_table( t );
482 static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
484 MSITABLE *t;
486 LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
487 if( !wcscmp( name, t->name ) )
488 return t;
490 return NULL;
493 static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count )
495 DWORD i;
497 for (i = 0; colinfo && i < count; i++)
499 assert( i + 1 == colinfo[i].number );
500 if (i) colinfo[i].offset = colinfo[i - 1].offset +
501 bytes_per_column( db, &colinfo[i - 1], LONG_STR_BYTES );
502 else colinfo[i].offset = 0;
504 TRACE("column %d is [%s] with type %08x ofs %d\n",
505 colinfo[i].number, debugstr_w(colinfo[i].colname),
506 colinfo[i].type, colinfo[i].offset);
510 static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz )
512 const MSICOLUMNINFO *p;
513 DWORD i, n;
515 TRACE("%s\n", debugstr_w(name));
517 if (!wcscmp( name, L"_Tables" ))
519 p = _Tables_cols;
520 n = 1;
522 else if (!wcscmp( name, L"_Columns" ))
524 p = _Columns_cols;
525 n = 4;
527 else return ERROR_FUNCTION_FAILED;
529 for (i = 0; i < n; i++)
531 if (colinfo && i < *sz) colinfo[i] = p[i];
532 if (colinfo && i >= *sz) break;
534 table_calc_column_offsets( db, colinfo, n );
535 *sz = n;
536 return ERROR_SUCCESS;
539 static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz );
541 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
543 UINT r, column_count = 0;
544 MSICOLUMNINFO *columns;
546 /* get the number of columns in this table */
547 column_count = 0;
548 r = get_tablecolumns( db, name, NULL, &column_count );
549 if (r != ERROR_SUCCESS)
550 return r;
552 *pcount = column_count;
554 /* if there are no columns, there's no table */
555 if (!column_count)
556 return ERROR_INVALID_PARAMETER;
558 TRACE("table %s found\n", debugstr_w(name));
560 columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) );
561 if (!columns)
562 return ERROR_FUNCTION_FAILED;
564 r = get_tablecolumns( db, name, columns, &column_count );
565 if (r != ERROR_SUCCESS)
567 msi_free( columns );
568 return ERROR_FUNCTION_FAILED;
570 *pcols = columns;
571 return r;
574 static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret )
576 MSITABLE *table;
577 UINT r;
579 /* first, see if the table is cached */
580 table = find_cached_table( db, name );
581 if (table)
583 *table_ret = table;
584 return ERROR_SUCCESS;
587 /* nonexistent tables should be interpreted as empty tables */
588 table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) );
589 if (!table)
590 return ERROR_FUNCTION_FAILED;
592 table->row_count = 0;
593 table->data = NULL;
594 table->data_persistent = NULL;
595 table->colinfo = NULL;
596 table->col_count = 0;
597 table->persistent = MSICONDITION_TRUE;
598 lstrcpyW( table->name, name );
600 if (!wcscmp( name, L"_Tables" ) || !wcscmp( name, L"_Columns" ))
601 table->persistent = MSICONDITION_NONE;
603 r = table_get_column_info( db, name, &table->colinfo, &table->col_count );
604 if (r != ERROR_SUCCESS)
606 free_table( table );
607 return r;
609 r = read_table_from_storage( db, table, db->storage );
610 if (r != ERROR_SUCCESS)
612 free_table( table );
613 return r;
615 list_add_head( &db->tables, &table->entry );
616 *table_ret = table;
617 return ERROR_SUCCESS;
620 static UINT read_table_int( BYTE *const *data, UINT row, UINT col, UINT bytes )
622 UINT ret = 0, i;
624 for (i = 0; i < bytes; i++)
625 ret += data[row][col + i] << i * 8;
627 return ret;
630 static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz )
632 UINT r, i, n = 0, table_id, count, maxcount = *sz;
633 MSITABLE *table = NULL;
635 TRACE("%s\n", debugstr_w(szTableName));
637 /* first check if there is a default table with that name */
638 r = get_defaulttablecolumns( db, szTableName, colinfo, sz );
639 if (r == ERROR_SUCCESS && *sz)
640 return r;
642 r = get_table( db, L"_Columns", &table );
643 if (r != ERROR_SUCCESS)
645 ERR("couldn't load _Columns table\n");
646 return ERROR_FUNCTION_FAILED;
649 /* convert table and column names to IDs from the string table */
650 r = msi_string2id( db->strings, szTableName, -1, &table_id );
651 if (r != ERROR_SUCCESS)
653 WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
654 return r;
656 TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
658 /* Note: _Columns table doesn't have non-persistent data */
660 /* if maxcount is non-zero, assume it's exactly right for this table */
661 if (colinfo) memset( colinfo, 0, maxcount * sizeof(*colinfo) );
662 count = table->row_count;
663 for (i = 0; i < count; i++)
665 if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue;
666 if (colinfo)
668 UINT id = read_table_int( table->data, i, table->colinfo[2].offset, LONG_STR_BYTES );
669 UINT col = read_table_int( table->data, i, table->colinfo[1].offset, sizeof(USHORT) ) - (1 << 15);
671 /* check the column number is in range */
672 if (col < 1 || col > maxcount)
674 ERR("column %d out of range (maxcount: %d)\n", col, maxcount);
675 continue;
677 /* check if this column was already set */
678 if (colinfo[col - 1].number)
680 ERR("duplicate column %d\n", col);
681 continue;
683 colinfo[col - 1].tablename = msi_string_lookup( db->strings, table_id, NULL );
684 colinfo[col - 1].number = col;
685 colinfo[col - 1].colname = msi_string_lookup( db->strings, id, NULL );
686 colinfo[col - 1].type = read_table_int( table->data, i, table->colinfo[3].offset,
687 sizeof(USHORT) ) - (1 << 15);
688 colinfo[col - 1].offset = 0;
689 colinfo[col - 1].hash_table = NULL;
691 n++;
693 TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
695 if (colinfo && n != maxcount)
697 ERR("missing column in table %s\n", debugstr_w(szTableName));
698 msi_free_colinfo( colinfo, maxcount );
699 return ERROR_FUNCTION_FAILED;
701 table_calc_column_offsets( db, colinfo, n );
702 *sz = n;
703 return ERROR_SUCCESS;
706 UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info,
707 MSICONDITION persistent, BOOL hold )
709 UINT r, nField;
710 MSIVIEW *tv = NULL;
711 MSIRECORD *rec = NULL;
712 column_info *col;
713 MSITABLE *table;
714 UINT i;
716 /* only add tables that don't exist already */
717 if( TABLE_Exists(db, name ) )
719 WARN("table %s exists\n", debugstr_w(name));
720 return ERROR_BAD_QUERY_SYNTAX;
723 table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
724 if( !table )
725 return ERROR_FUNCTION_FAILED;
727 table->ref_count = 0;
728 table->row_count = 0;
729 table->data = NULL;
730 table->data_persistent = NULL;
731 table->colinfo = NULL;
732 table->col_count = 0;
733 table->persistent = persistent;
734 lstrcpyW( table->name, name );
736 if( hold )
737 table->ref_count++;
739 for( col = col_info; col; col = col->next )
740 table->col_count++;
742 table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) );
743 if (!table->colinfo)
745 free_table( table );
746 return ERROR_FUNCTION_FAILED;
749 for( i = 0, col = col_info; col; i++, col = col->next )
751 UINT table_id = msi_add_string( db->strings, col->table, -1, persistent );
752 UINT col_id = msi_add_string( db->strings, col->column, -1, persistent );
754 table->colinfo[ i ].tablename = msi_string_lookup( db->strings, table_id, NULL );
755 table->colinfo[ i ].number = i + 1;
756 table->colinfo[ i ].colname = msi_string_lookup( db->strings, col_id, NULL );
757 table->colinfo[ i ].type = col->type;
758 table->colinfo[ i ].offset = 0;
759 table->colinfo[ i ].hash_table = NULL;
761 table_calc_column_offsets( db, table->colinfo, table->col_count);
763 r = TABLE_CreateView( db, L"_Tables", &tv );
764 TRACE("CreateView returned %x\n", r);
765 if( r )
767 free_table( table );
768 return r;
771 r = tv->ops->execute( tv, 0 );
772 TRACE("tv execute returned %x\n", r);
773 if( r )
774 goto err;
776 rec = MSI_CreateRecord( 1 );
777 if( !rec )
778 goto err;
780 r = MSI_RecordSetStringW( rec, 1, name );
781 if( r )
782 goto err;
784 r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE );
785 TRACE("insert_row returned %x\n", r);
786 if( r )
787 goto err;
789 tv->ops->delete( tv );
790 tv = NULL;
792 msiobj_release( &rec->hdr );
793 rec = NULL;
795 if( persistent != MSICONDITION_FALSE )
797 /* add each column to the _Columns table */
798 r = TABLE_CreateView( db, L"_Columns", &tv );
799 if( r )
800 goto err;
802 r = tv->ops->execute( tv, 0 );
803 TRACE("tv execute returned %x\n", r);
804 if( r )
805 goto err;
807 rec = MSI_CreateRecord( 4 );
808 if( !rec )
809 goto err;
811 r = MSI_RecordSetStringW( rec, 1, name );
812 if( r )
813 goto err;
816 * need to set the table, column number, col name and type
817 * for each column we enter in the table
819 nField = 1;
820 for( col = col_info; col; col = col->next )
822 r = MSI_RecordSetInteger( rec, 2, nField );
823 if( r )
824 goto err;
826 r = MSI_RecordSetStringW( rec, 3, col->column );
827 if( r )
828 goto err;
830 r = MSI_RecordSetInteger( rec, 4, col->type );
831 if( r )
832 goto err;
834 r = tv->ops->insert_row( tv, rec, -1, FALSE );
835 if( r )
836 goto err;
838 nField++;
840 if( !col )
841 r = ERROR_SUCCESS;
844 err:
845 if (rec)
846 msiobj_release( &rec->hdr );
847 /* FIXME: remove values from the string table on error */
848 if( tv )
849 tv->ops->delete( tv );
851 if (r == ERROR_SUCCESS)
852 list_add_head( &db->tables, &table->entry );
853 else
854 free_table( table );
856 return r;
859 static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref )
861 BYTE *rawdata = NULL;
862 UINT rawsize, i, j, row_size, row_count;
863 UINT r = ERROR_FUNCTION_FAILED;
865 /* Nothing to do for non-persistent tables */
866 if( t->persistent == MSICONDITION_FALSE )
867 return ERROR_SUCCESS;
869 TRACE("Saving %s\n", debugstr_w( t->name ) );
871 row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref );
872 row_count = t->row_count;
873 for (i = 0; i < t->row_count; i++)
875 if (!t->data_persistent[i])
877 row_count = 1; /* yes, this is bizarre */
878 break;
881 rawsize = row_count * row_size;
882 rawdata = msi_alloc_zero( rawsize );
883 if( !rawdata )
885 r = ERROR_NOT_ENOUGH_MEMORY;
886 goto err;
889 rawsize = 0;
890 for (i = 0; i < row_count; i++)
892 UINT ofs = 0, ofs_mem = 0;
894 if (!t->data_persistent[i]) break;
896 for (j = 0; j < t->col_count; j++)
898 UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
899 UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref );
900 UINT k;
902 if (n != 2 && n != 3 && n != 4)
904 ERR("oops - unknown column width %d\n", n);
905 goto err;
907 if (t->colinfo[j].type & MSITYPE_STRING && n < m)
909 UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES );
910 if (id > 1 << bytes_per_strref * 8)
912 ERR("string id %u out of range\n", id);
913 goto err;
916 for (k = 0; k < n; k++)
918 rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k];
920 ofs_mem += m;
921 ofs += n;
923 rawsize += row_size;
926 TRACE("writing %d bytes\n", rawsize);
927 r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE );
929 err:
930 msi_free( rawdata );
931 return r;
934 static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name )
936 MSITABLE *table;
937 UINT size, offset, old_count;
938 UINT n;
940 if (!(table = find_cached_table( db, name ))) return;
941 old_count = table->col_count;
942 msi_free_colinfo( table->colinfo, table->col_count );
943 msi_free( table->colinfo );
944 table->colinfo = NULL;
946 table_get_column_info( db, name, &table->colinfo, &table->col_count );
947 if (!table->col_count) return;
949 size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES );
950 offset = table->colinfo[table->col_count - 1].offset;
952 for ( n = 0; n < table->row_count; n++ )
954 table->data[n] = msi_realloc( table->data[n], size );
955 if (old_count < table->col_count)
956 memset( &table->data[n][offset], 0, size - offset );
960 /* try to find the table name in the _Tables table */
961 BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name )
963 UINT r, table_id, i;
964 MSITABLE *table;
966 if( !wcscmp( name, L"_Tables" ) || !wcscmp( name, L"_Columns" ) ||
967 !wcscmp( name, L"_Streams" ) || !wcscmp( name, L"_Storages" ) )
968 return TRUE;
970 r = msi_string2id( db->strings, name, -1, &table_id );
971 if( r != ERROR_SUCCESS )
973 TRACE("Couldn't find id for %s\n", debugstr_w(name));
974 return FALSE;
977 r = get_table( db, L"_Tables", &table );
978 if( r != ERROR_SUCCESS )
980 ERR("table _Tables not available\n");
981 return FALSE;
984 for( i = 0; i < table->row_count; i++ )
986 if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id )
987 return TRUE;
990 return FALSE;
993 /* below is the query interface to a table */
995 typedef struct tagMSITABLEVIEW
997 MSIVIEW view;
998 MSIDATABASE *db;
999 MSITABLE *table;
1000 MSICOLUMNINFO *columns;
1001 UINT num_cols;
1002 UINT row_size;
1003 WCHAR name[1];
1004 } MSITABLEVIEW;
1006 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1008 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1009 UINT offset, n;
1011 if( !tv->table )
1012 return ERROR_INVALID_PARAMETER;
1014 if( (col==0) || (col>tv->num_cols) )
1015 return ERROR_INVALID_PARAMETER;
1017 /* how many rows are there ? */
1018 if( row >= tv->table->row_count )
1019 return ERROR_NO_MORE_ITEMS;
1021 if( tv->columns[col-1].offset >= tv->row_size )
1023 ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1024 ERR("%p %p\n", tv, tv->columns );
1025 return ERROR_FUNCTION_FAILED;
1028 n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1029 if (n != 2 && n != 3 && n != 4)
1031 ERR("oops! what is %d bytes per column?\n", n );
1032 return ERROR_FUNCTION_FAILED;
1035 offset = tv->columns[col-1].offset;
1036 *val = read_table_int(tv->table->data, row, offset, n);
1038 /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1040 return ERROR_SUCCESS;
1043 static UINT get_stream_name( const MSITABLEVIEW *tv, UINT row, WCHAR **pstname )
1045 LPWSTR p, stname = NULL;
1046 UINT i, r, type, ival;
1047 DWORD len;
1048 LPCWSTR sval;
1049 MSIVIEW *view = (MSIVIEW *) tv;
1051 TRACE("%p %d\n", tv, row);
1053 len = lstrlenW( tv->name ) + 1;
1054 stname = msi_alloc( len*sizeof(WCHAR) );
1055 if ( !stname )
1057 r = ERROR_OUTOFMEMORY;
1058 goto err;
1061 lstrcpyW( stname, tv->name );
1063 for ( i = 0; i < tv->num_cols; i++ )
1065 type = tv->columns[i].type;
1066 if ( type & MSITYPE_KEY )
1068 WCHAR number[0x20];
1070 r = TABLE_fetch_int( view, row, i+1, &ival );
1071 if ( r != ERROR_SUCCESS )
1072 goto err;
1074 if ( tv->columns[i].type & MSITYPE_STRING )
1076 sval = msi_string_lookup( tv->db->strings, ival, NULL );
1077 if ( !sval )
1079 r = ERROR_INVALID_PARAMETER;
1080 goto err;
1083 else
1085 UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES );
1087 switch( n )
1089 case 2:
1090 swprintf( number, ARRAY_SIZE(number), L"%d", ival-0x8000 );
1091 break;
1092 case 4:
1093 swprintf( number, ARRAY_SIZE(number), L"%d", ival^0x80000000 );
1094 break;
1095 default:
1096 ERR( "oops - unknown column width %d\n", n );
1097 r = ERROR_FUNCTION_FAILED;
1098 goto err;
1100 sval = number;
1103 len += lstrlenW( L"." ) + lstrlenW( sval );
1104 p = msi_realloc ( stname, len*sizeof(WCHAR) );
1105 if ( !p )
1107 r = ERROR_OUTOFMEMORY;
1108 goto err;
1110 stname = p;
1112 lstrcatW( stname, L"." );
1113 lstrcatW( stname, sval );
1115 else
1116 continue;
1119 *pstname = stname;
1120 return ERROR_SUCCESS;
1122 err:
1123 msi_free( stname );
1124 *pstname = NULL;
1125 return r;
1129 * We need a special case for streams, as we need to reference column with
1130 * the name of the stream in the same table, and the table name
1131 * which may not be available at higher levels of the query
1133 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1135 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1136 UINT r;
1137 WCHAR *name;
1139 if( !view->ops->fetch_int )
1140 return ERROR_INVALID_PARAMETER;
1142 r = get_stream_name( tv, row, &name );
1143 if (r != ERROR_SUCCESS)
1145 ERR("fetching stream, error = %u\n", r);
1146 return r;
1149 r = msi_get_stream( tv->db, name, stm );
1150 if (r != ERROR_SUCCESS)
1151 ERR("fetching stream %s, error = %u\n", debugstr_w(name), r);
1153 msi_free( name );
1154 return r;
1157 /* Set a table value, i.e. preadjusted integer or string ID. */
1158 static UINT table_set_bytes( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
1160 UINT offset, n, i;
1162 if( !tv->table )
1163 return ERROR_INVALID_PARAMETER;
1165 if( (col==0) || (col>tv->num_cols) )
1166 return ERROR_INVALID_PARAMETER;
1168 if( row >= tv->table->row_count )
1169 return ERROR_INVALID_PARAMETER;
1171 if( tv->columns[col-1].offset >= tv->row_size )
1173 ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1174 ERR("%p %p\n", tv, tv->columns );
1175 return ERROR_FUNCTION_FAILED;
1178 msi_free( tv->columns[col-1].hash_table );
1179 tv->columns[col-1].hash_table = NULL;
1181 n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1182 if ( n != 2 && n != 3 && n != 4 )
1184 ERR("oops! what is %d bytes per column?\n", n );
1185 return ERROR_FUNCTION_FAILED;
1188 offset = tv->columns[col-1].offset;
1189 for ( i = 0; i < n; i++ )
1190 tv->table->data[row][offset + i] = (val >> i * 8) & 0xff;
1192 return ERROR_SUCCESS;
1195 static UINT int_to_table_storage( const MSITABLEVIEW *tv, UINT col, int val, UINT *ret )
1197 if ((tv->columns[col-1].type & MSI_DATASIZEMASK) == 2)
1199 if (val == MSI_NULL_INTEGER)
1200 *ret = 0;
1201 else if ((val + 0x8000) & 0xffff0000)
1203 ERR("value %d out of range\n", val);
1204 return ERROR_FUNCTION_FAILED;
1206 else
1207 *ret = val + 0x8000;
1209 else
1210 *ret = val ^ 0x80000000;
1212 return ERROR_SUCCESS;
1215 static UINT TABLE_set_int( MSIVIEW *view, UINT row, UINT col, int val )
1217 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1218 UINT r, table_int;
1220 TRACE("row %u, col %u, val %d.\n", row, col, val);
1222 if ((r = int_to_table_storage( tv, col, val, &table_int )))
1223 return r;
1225 if (tv->columns[col-1].type & MSITYPE_KEY)
1227 UINT key;
1229 if ((r = TABLE_fetch_int( view, row, col, &key )))
1230 return r;
1231 if (key != table_int)
1233 ERR("Cannot modify primary key %s.%s.\n",
1234 debugstr_w(tv->table->name), debugstr_w(tv->columns[col-1].colname));
1235 return ERROR_FUNCTION_FAILED;
1239 return table_set_bytes( tv, row, col, table_int );
1242 static UINT TABLE_set_string( MSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
1244 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1245 BOOL persistent;
1246 UINT id, r;
1248 TRACE("row %u, col %u, val %s.\n", row, col, debugstr_wn(val, len));
1250 persistent = (tv->table->persistent != MSICONDITION_FALSE)
1251 && tv->table->data_persistent[row];
1253 if (val)
1255 r = msi_string2id( tv->db->strings, val, len, &id );
1256 if (r != ERROR_SUCCESS)
1257 id = msi_add_string( tv->db->strings, val, len, persistent );
1259 else
1260 id = 0;
1262 if (tv->columns[col-1].type & MSITYPE_KEY)
1264 UINT key;
1266 if ((r = TABLE_fetch_int( view, row, col, &key )))
1267 return r;
1268 if (key != id)
1270 ERR("Cannot modify primary key %s.%s.\n",
1271 debugstr_w(tv->table->name), debugstr_w(tv->columns[col-1].colname));
1272 return ERROR_FUNCTION_FAILED;
1276 return table_set_bytes( tv, row, col, id );
1279 static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
1281 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1283 if (!tv->table)
1284 return ERROR_INVALID_PARAMETER;
1286 return msi_view_get_row(tv->db, view, row, rec);
1289 static UINT add_stream( MSIDATABASE *db, const WCHAR *name, IStream *data )
1291 MSIQUERY *query;
1292 MSIRECORD *rec;
1293 UINT r;
1295 TRACE("%p %s %p\n", db, debugstr_w(name), data);
1297 if (!(rec = MSI_CreateRecord( 2 )))
1298 return ERROR_OUTOFMEMORY;
1300 r = MSI_RecordSetStringW( rec, 1, name );
1301 if (r != ERROR_SUCCESS)
1302 goto done;
1304 r = MSI_RecordSetIStream( rec, 2, data );
1305 if (r != ERROR_SUCCESS)
1306 goto done;
1308 r = MSI_DatabaseOpenViewW( db, L"INSERT INTO `_Streams` (`Name`,`Data`) VALUES (?,?)", &query );
1309 if (r != ERROR_SUCCESS)
1310 goto done;
1312 r = MSI_ViewExecute( query, rec );
1313 msiobj_release( &query->hdr );
1314 if (r == ERROR_SUCCESS)
1315 goto done;
1317 msiobj_release( &rec->hdr );
1318 if (!(rec = MSI_CreateRecord( 2 )))
1319 return ERROR_OUTOFMEMORY;
1321 r = MSI_RecordSetIStream( rec, 1, data );
1322 if (r != ERROR_SUCCESS)
1323 goto done;
1325 r = MSI_RecordSetStringW( rec, 2, name );
1326 if (r != ERROR_SUCCESS)
1327 goto done;
1329 r = MSI_DatabaseOpenViewW( db, L"UPDATE `_Streams` SET `Data` = ? WHERE `Name` = ?", &query );
1330 if (r != ERROR_SUCCESS)
1331 goto done;
1333 r = MSI_ViewExecute( query, rec );
1334 msiobj_release( &query->hdr );
1336 done:
1337 msiobj_release( &rec->hdr );
1338 return r;
1341 static UINT TABLE_set_stream( MSIVIEW *view, UINT row, UINT col, IStream *stream )
1343 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1344 WCHAR *name;
1345 UINT r;
1347 TRACE("row %u, col %u, stream %p.\n", row, col, stream);
1349 if ((r = get_stream_name( tv, row - 1, &name )))
1350 return r;
1352 r = add_stream( tv->db, name, stream );
1353 msi_free( name );
1354 return r;
1357 static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue )
1359 MSICOLUMNINFO columninfo;
1360 UINT r;
1362 if (!iField || iField > tv->num_cols || MSI_RecordIsNull( rec, iField ))
1363 return ERROR_FUNCTION_FAILED;
1365 columninfo = tv->columns[ iField - 1 ];
1367 if ( MSITYPE_IS_BINARY(columninfo.type) )
1369 *pvalue = 1; /* refers to the first key column */
1371 else if ( columninfo.type & MSITYPE_STRING )
1373 int len;
1374 const WCHAR *sval = msi_record_get_string( rec, iField, &len );
1375 if (sval)
1377 r = msi_string2id( tv->db->strings, sval, len, pvalue );
1378 if (r != ERROR_SUCCESS)
1379 return ERROR_NOT_FOUND;
1381 else *pvalue = 0;
1383 else
1384 return int_to_table_storage( tv, iField, MSI_RecordGetInteger( rec, iField ), pvalue );
1386 return ERROR_SUCCESS;
1389 static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
1391 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1392 UINT i, val, r = ERROR_SUCCESS;
1394 if ( !tv->table )
1395 return ERROR_INVALID_PARAMETER;
1397 /* test if any of the mask bits are invalid */
1398 if ( mask >= (1<<tv->num_cols) )
1399 return ERROR_INVALID_PARAMETER;
1401 for ( i = 0; i < tv->num_cols; i++ )
1403 BOOL persistent;
1405 /* only update the fields specified in the mask */
1406 if ( !(mask&(1<<i)) )
1407 continue;
1409 persistent = (tv->table->persistent != MSICONDITION_FALSE) &&
1410 (tv->table->data_persistent[row]);
1411 /* FIXME: should we allow updating keys? */
1413 val = 0;
1414 if ( !MSI_RecordIsNull( rec, i + 1 ) )
1416 r = get_table_value_from_record (tv, rec, i + 1, &val);
1417 if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
1419 IStream *stm;
1420 LPWSTR stname;
1422 if ( r != ERROR_SUCCESS )
1423 return ERROR_FUNCTION_FAILED;
1425 r = MSI_RecordGetIStream( rec, i + 1, &stm );
1426 if ( r != ERROR_SUCCESS )
1427 return r;
1429 r = get_stream_name( tv, row, &stname );
1430 if ( r != ERROR_SUCCESS )
1432 IStream_Release( stm );
1433 return r;
1436 r = add_stream( tv->db, stname, stm );
1437 IStream_Release( stm );
1438 msi_free ( stname );
1440 if ( r != ERROR_SUCCESS )
1441 return r;
1443 else if ( tv->columns[i].type & MSITYPE_STRING )
1445 UINT x;
1447 if ( r != ERROR_SUCCESS )
1449 int len;
1450 const WCHAR *sval = msi_record_get_string( rec, i + 1, &len );
1451 val = msi_add_string( tv->db->strings, sval, len, persistent );
1453 else
1455 TABLE_fetch_int(&tv->view, row, i + 1, &x);
1456 if (val == x)
1457 continue;
1460 else
1462 if ( r != ERROR_SUCCESS )
1463 return ERROR_FUNCTION_FAILED;
1467 r = table_set_bytes( tv, row, i+1, val );
1468 if ( r != ERROR_SUCCESS )
1469 break;
1471 return r;
1474 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary )
1476 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1477 BYTE **p, *row;
1478 BOOL *b;
1479 UINT sz;
1480 BYTE ***data_ptr;
1481 BOOL **data_persist_ptr;
1482 UINT *row_count;
1484 TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE");
1486 if( !tv->table )
1487 return ERROR_INVALID_PARAMETER;
1489 row = msi_alloc_zero( tv->row_size );
1490 if( !row )
1491 return ERROR_NOT_ENOUGH_MEMORY;
1493 row_count = &tv->table->row_count;
1494 data_ptr = &tv->table->data;
1495 data_persist_ptr = &tv->table->data_persistent;
1496 if (*num == -1)
1497 *num = tv->table->row_count;
1499 sz = (*row_count + 1) * sizeof (BYTE*);
1500 if( *data_ptr )
1501 p = msi_realloc( *data_ptr, sz );
1502 else
1503 p = msi_alloc( sz );
1504 if( !p )
1506 msi_free( row );
1507 return ERROR_NOT_ENOUGH_MEMORY;
1510 sz = (*row_count + 1) * sizeof (BOOL);
1511 if( *data_persist_ptr )
1512 b = msi_realloc( *data_persist_ptr, sz );
1513 else
1514 b = msi_alloc( sz );
1515 if( !b )
1517 msi_free( row );
1518 msi_free( p );
1519 return ERROR_NOT_ENOUGH_MEMORY;
1522 *data_ptr = p;
1523 (*data_ptr)[*row_count] = row;
1525 *data_persist_ptr = b;
1526 (*data_persist_ptr)[*row_count] = !temporary;
1528 (*row_count)++;
1530 return ERROR_SUCCESS;
1533 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1535 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1537 TRACE("%p %p\n", tv, record);
1539 TRACE("There are %d columns\n", tv->num_cols );
1541 return ERROR_SUCCESS;
1544 static UINT TABLE_close( struct tagMSIVIEW *view )
1546 TRACE("%p\n", view );
1548 return ERROR_SUCCESS;
1551 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1553 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1555 TRACE("%p %p %p\n", view, rows, cols );
1557 if( cols )
1558 *cols = tv->num_cols;
1559 if( rows )
1561 if( !tv->table )
1562 return ERROR_INVALID_PARAMETER;
1563 *rows = tv->table->row_count;
1566 return ERROR_SUCCESS;
1569 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1570 UINT n, LPCWSTR *name, UINT *type, BOOL *temporary,
1571 LPCWSTR *table_name )
1573 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1575 TRACE("%p %d %p %p\n", tv, n, name, type );
1577 if( ( n == 0 ) || ( n > tv->num_cols ) )
1578 return ERROR_INVALID_PARAMETER;
1580 if( name )
1582 *name = tv->columns[n-1].colname;
1583 if( !*name )
1584 return ERROR_FUNCTION_FAILED;
1587 if( table_name )
1589 *table_name = tv->columns[n-1].tablename;
1590 if( !*table_name )
1591 return ERROR_FUNCTION_FAILED;
1594 if( type )
1595 *type = tv->columns[n-1].type;
1597 if( temporary )
1598 *temporary = (tv->columns[n-1].type & MSITYPE_TEMPORARY) != 0;
1600 return ERROR_SUCCESS;
1603 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column );
1605 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column )
1607 UINT r, row, i;
1609 /* check there are no null values where they're not allowed */
1610 for( i = 0; i < tv->num_cols; i++ )
1612 if ( tv->columns[i].type & MSITYPE_NULLABLE )
1613 continue;
1615 if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
1616 TRACE("skipping binary column\n");
1617 else if ( tv->columns[i].type & MSITYPE_STRING )
1619 int len;
1620 const WCHAR *str = msi_record_get_string( rec, i+1, &len );
1622 if (!str || (!str[0] && !len))
1624 if (column) *column = i;
1625 return ERROR_INVALID_DATA;
1628 else
1630 UINT n;
1632 n = MSI_RecordGetInteger( rec, i+1 );
1633 if (n == MSI_NULL_INTEGER)
1635 if (column) *column = i;
1636 return ERROR_INVALID_DATA;
1641 /* check there are no duplicate keys */
1642 r = msi_table_find_row( tv, rec, &row, column );
1643 if (r == ERROR_SUCCESS)
1644 return ERROR_FUNCTION_FAILED;
1646 return ERROR_SUCCESS;
1649 static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec )
1651 UINT r, i, ivalue, x;
1653 for (i = 0; i < tv->num_cols; i++ )
1655 if (!(tv->columns[i].type & MSITYPE_KEY)) continue;
1657 r = get_table_value_from_record( tv, rec, i + 1, &ivalue );
1658 if (r != ERROR_SUCCESS)
1659 return 1;
1661 r = TABLE_fetch_int( &tv->view, row, i + 1, &x );
1662 if (r != ERROR_SUCCESS)
1664 WARN("TABLE_fetch_int should not fail here %u\n", r);
1665 return -1;
1667 if (ivalue > x)
1669 return 1;
1671 else if (ivalue == x)
1673 if (i < tv->num_cols - 1) continue;
1674 return 0;
1676 else
1677 return -1;
1679 return 1;
1682 static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec )
1684 int idx, c, low = 0, high = tv->table->row_count - 1;
1686 TRACE("%p %p\n", tv, rec);
1688 while (low <= high)
1690 idx = (low + high) / 2;
1691 c = compare_record( tv, idx, rec );
1693 if (c < 0)
1694 high = idx - 1;
1695 else if (c > 0)
1696 low = idx + 1;
1697 else
1699 TRACE("found %u\n", idx);
1700 return idx;
1703 TRACE("found %u\n", high + 1);
1704 return high + 1;
1707 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
1709 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1710 UINT i, r;
1712 TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" );
1714 /* check that the key is unique - can we find a matching row? */
1715 r = table_validate_new( tv, rec, NULL );
1716 if( r != ERROR_SUCCESS )
1717 return ERROR_FUNCTION_FAILED;
1719 if (row == -1)
1720 row = find_insert_index( tv, rec );
1722 r = table_create_new_row( view, &row, temporary );
1723 TRACE("insert_row returned %08x\n", r);
1724 if( r != ERROR_SUCCESS )
1725 return r;
1727 /* shift the rows to make room for the new row */
1728 for (i = tv->table->row_count - 1; i > row; i--)
1730 memmove(&(tv->table->data[i][0]),
1731 &(tv->table->data[i - 1][0]), tv->row_size);
1732 tv->table->data_persistent[i] = tv->table->data_persistent[i - 1];
1735 /* Re-set the persistence flag */
1736 tv->table->data_persistent[row] = !temporary;
1737 return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
1740 static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row )
1742 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1743 UINT r, num_rows, num_cols, i;
1745 TRACE("%p %d\n", tv, row);
1747 if ( !tv->table )
1748 return ERROR_INVALID_PARAMETER;
1750 r = TABLE_get_dimensions( view, &num_rows, &num_cols );
1751 if ( r != ERROR_SUCCESS )
1752 return r;
1754 if ( row >= num_rows )
1755 return ERROR_FUNCTION_FAILED;
1757 num_rows = tv->table->row_count;
1758 tv->table->row_count--;
1760 /* reset the hash tables */
1761 for (i = 0; i < tv->num_cols; i++)
1763 msi_free( tv->columns[i].hash_table );
1764 tv->columns[i].hash_table = NULL;
1767 for (i = row + 1; i < num_rows; i++)
1769 memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size);
1770 tv->table->data_persistent[i - 1] = tv->table->data_persistent[i];
1773 msi_free(tv->table->data[num_rows - 1]);
1775 return ERROR_SUCCESS;
1778 static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row)
1780 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1781 UINT r, new_row;
1783 /* FIXME: MsiViewFetch should set rec index 0 to some ID that
1784 * sets the fetched record apart from other records
1787 if (!tv->table)
1788 return ERROR_INVALID_PARAMETER;
1790 r = msi_table_find_row(tv, rec, &new_row, NULL);
1791 if (r != ERROR_SUCCESS)
1793 ERR("can't find row to modify\n");
1794 return ERROR_FUNCTION_FAILED;
1797 /* the row cannot be changed */
1798 if (row != new_row)
1799 return ERROR_FUNCTION_FAILED;
1801 return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1);
1804 static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
1806 MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1807 UINT r, row;
1809 if (!tv->table)
1810 return ERROR_INVALID_PARAMETER;
1812 r = msi_table_find_row(tv, rec, &row, NULL);
1813 if (r == ERROR_SUCCESS)
1814 return TABLE_set_row(view, row, rec, (1 << tv->num_cols) - 1);
1815 else
1816 return TABLE_insert_row( view, rec, -1, FALSE );
1819 static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row )
1821 MSIRECORD *curr;
1822 UINT r, i, count;
1824 r = TABLE_get_row(view, row, &curr);
1825 if (r != ERROR_SUCCESS)
1826 return r;
1828 /* Close the original record */
1829 MSI_CloseRecord(&rec->hdr);
1831 count = MSI_RecordGetFieldCount(rec);
1832 for (i = 0; i < count; i++)
1833 MSI_RecordCopyField(curr, i + 1, rec, i + 1);
1835 msiobj_release(&curr->hdr);
1836 return ERROR_SUCCESS;
1839 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1840 MSIRECORD *rec, UINT row)
1842 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1843 UINT r, frow, column;
1845 TRACE("%p %d %p\n", view, eModifyMode, rec );
1847 switch (eModifyMode)
1849 case MSIMODIFY_DELETE:
1850 r = TABLE_delete_row( view, row );
1851 break;
1852 case MSIMODIFY_VALIDATE_NEW:
1853 r = table_validate_new( tv, rec, &column );
1854 if (r != ERROR_SUCCESS)
1856 tv->view.error = MSIDBERROR_DUPLICATEKEY;
1857 tv->view.error_column = tv->columns[column].colname;
1858 r = ERROR_INVALID_DATA;
1860 break;
1862 case MSIMODIFY_INSERT:
1863 r = table_validate_new( tv, rec, NULL );
1864 if (r != ERROR_SUCCESS)
1865 break;
1866 r = TABLE_insert_row( view, rec, -1, FALSE );
1867 break;
1869 case MSIMODIFY_INSERT_TEMPORARY:
1870 r = table_validate_new( tv, rec, NULL );
1871 if (r != ERROR_SUCCESS)
1872 break;
1873 r = TABLE_insert_row( view, rec, -1, TRUE );
1874 break;
1876 case MSIMODIFY_REFRESH:
1877 r = msi_refresh_record( view, rec, row );
1878 break;
1880 case MSIMODIFY_UPDATE:
1881 r = msi_table_update( view, rec, row );
1882 break;
1884 case MSIMODIFY_ASSIGN:
1885 r = msi_table_assign( view, rec );
1886 break;
1888 case MSIMODIFY_MERGE:
1889 /* check row that matches this record */
1890 r = msi_table_find_row( tv, rec, &frow, &column );
1891 if (r != ERROR_SUCCESS)
1893 r = table_validate_new( tv, rec, NULL );
1894 if (r == ERROR_SUCCESS)
1895 r = TABLE_insert_row( view, rec, -1, FALSE );
1897 break;
1899 case MSIMODIFY_REPLACE:
1900 case MSIMODIFY_VALIDATE:
1901 case MSIMODIFY_VALIDATE_FIELD:
1902 case MSIMODIFY_VALIDATE_DELETE:
1903 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1904 r = ERROR_CALL_NOT_IMPLEMENTED;
1905 break;
1907 default:
1908 r = ERROR_INVALID_DATA;
1911 return r;
1914 static UINT TABLE_delete( struct tagMSIVIEW *view )
1916 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1918 TRACE("%p\n", view );
1920 tv->table = NULL;
1921 tv->columns = NULL;
1923 msi_free( tv );
1925 return ERROR_SUCCESS;
1928 static UINT TABLE_add_ref(struct tagMSIVIEW *view)
1930 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1932 TRACE("%p %d\n", view, tv->table->ref_count);
1933 return InterlockedIncrement(&tv->table->ref_count);
1936 static UINT TABLE_remove_column(struct tagMSIVIEW *view, UINT number)
1938 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1939 MSIRECORD *rec = NULL;
1940 MSIVIEW *columns = NULL;
1941 UINT row, r;
1943 if (tv->table->col_count != number)
1944 return ERROR_BAD_QUERY_SYNTAX;
1946 if (tv->table->colinfo[number-1].type & MSITYPE_TEMPORARY)
1948 UINT size = tv->table->colinfo[number-1].offset;
1949 tv->table->col_count--;
1950 tv->table->colinfo = msi_realloc( tv->table->colinfo, sizeof(*tv->table->colinfo) * tv->table->col_count );
1952 for (row = 0; row < tv->table->row_count; row++)
1953 tv->table->data[row] = msi_realloc( tv->table->data[row], size );
1954 return ERROR_SUCCESS;
1957 rec = MSI_CreateRecord(2);
1958 if (!rec)
1959 return ERROR_OUTOFMEMORY;
1961 MSI_RecordSetStringW(rec, 1, tv->name);
1962 MSI_RecordSetInteger(rec, 2, number);
1964 r = TABLE_CreateView(tv->db, L"_Columns", &columns);
1965 if (r != ERROR_SUCCESS)
1967 msiobj_release(&rec->hdr);
1968 return r;
1971 r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL);
1972 if (r != ERROR_SUCCESS)
1973 goto done;
1975 r = TABLE_delete_row(columns, row);
1976 if (r != ERROR_SUCCESS)
1977 goto done;
1979 msi_update_table_columns(tv->db, tv->name);
1981 done:
1982 msiobj_release(&rec->hdr);
1983 columns->ops->delete(columns);
1984 return r;
1987 static UINT TABLE_release(struct tagMSIVIEW *view)
1989 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1990 INT ref = tv->table->ref_count;
1991 UINT r;
1992 INT i;
1994 TRACE("%p %d\n", view, ref);
1996 ref = InterlockedDecrement(&tv->table->ref_count);
1997 if (ref == 0)
1999 for (i = tv->table->col_count - 1; i >= 0; i--)
2001 if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
2003 r = TABLE_remove_column(view, tv->table->colinfo[i].number);
2004 if (r != ERROR_SUCCESS)
2005 break;
2007 else
2009 break;
2013 if (!tv->table->col_count)
2015 list_remove(&tv->table->entry);
2016 free_table(tv->table);
2017 TABLE_delete(view);
2021 return ref;
2024 static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR column,
2025 INT type, BOOL hold)
2027 UINT i, r, table_id, col_id, size, offset;
2028 BOOL temporary = type & MSITYPE_TEMPORARY;
2029 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2030 MSICOLUMNINFO *colinfo;
2032 if (temporary && !hold && !tv->table->ref_count)
2033 return ERROR_SUCCESS;
2035 if (!temporary && tv->table->col_count &&
2036 tv->table->colinfo[tv->table->col_count-1].type & MSITYPE_TEMPORARY)
2037 return ERROR_BAD_QUERY_SYNTAX;
2039 for (i = 0; i < tv->table->col_count; i++)
2041 if (!wcscmp(tv->table->colinfo[i].colname, column))
2042 return ERROR_BAD_QUERY_SYNTAX;
2045 colinfo = msi_realloc(tv->table->colinfo, sizeof(*tv->table->colinfo) * (tv->table->col_count + 1));
2046 if (!colinfo)
2047 return ERROR_OUTOFMEMORY;
2048 tv->table->colinfo = colinfo;
2050 r = msi_string2id( tv->db->strings, tv->name, -1, &table_id );
2051 if (r != ERROR_SUCCESS)
2052 return r;
2053 col_id = msi_add_string( tv->db->strings, column, -1, !temporary );
2055 colinfo[tv->table->col_count].tablename = msi_string_lookup( tv->db->strings, table_id, NULL );
2056 colinfo[tv->table->col_count].number = tv->table->col_count + 1;
2057 colinfo[tv->table->col_count].colname = msi_string_lookup( tv->db->strings, col_id, NULL );
2058 colinfo[tv->table->col_count].type = type;
2059 colinfo[tv->table->col_count].offset = 0;
2060 colinfo[tv->table->col_count].hash_table = NULL;
2061 tv->table->col_count++;
2063 table_calc_column_offsets( tv->db, tv->table->colinfo, tv->table->col_count);
2065 size = msi_table_get_row_size( tv->db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES );
2066 offset = tv->table->colinfo[tv->table->col_count - 1].offset;
2067 for (i = 0; i < tv->table->row_count; i++)
2069 BYTE *data = msi_realloc( tv->table->data[i], size );
2070 if (!data)
2072 tv->table->col_count--;
2073 return ERROR_OUTOFMEMORY;
2076 tv->table->data[i] = data;
2077 memset(data + offset, 0, size - offset);
2080 if (!temporary)
2082 MSIVIEW *columns;
2083 MSIRECORD *rec;
2085 rec = MSI_CreateRecord(4);
2086 if (!rec)
2088 tv->table->col_count--;
2089 return ERROR_OUTOFMEMORY;
2092 MSI_RecordSetStringW(rec, 1, tv->name);
2093 MSI_RecordSetInteger(rec, 2, tv->table->col_count);
2094 MSI_RecordSetStringW(rec, 3, column);
2095 MSI_RecordSetInteger(rec, 4, type);
2097 r = TABLE_CreateView(tv->db, L"_Columns", &columns);
2098 if (r != ERROR_SUCCESS)
2100 tv->table->col_count--;
2101 msiobj_release(&rec->hdr);
2102 return r;
2105 r = TABLE_insert_row(columns, rec, -1, FALSE);
2106 columns->ops->delete(columns);
2107 msiobj_release(&rec->hdr);
2108 if (r != ERROR_SUCCESS)
2110 tv->table->col_count--;
2111 return r;
2114 if (hold)
2115 TABLE_add_ref(view);
2116 return ERROR_SUCCESS;
2119 static UINT TABLE_drop(struct tagMSIVIEW *view)
2121 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2122 MSIVIEW *tables = NULL;
2123 MSIRECORD *rec = NULL;
2124 UINT r, row;
2125 INT i;
2127 TRACE("dropping table %s\n", debugstr_w(tv->name));
2129 for (i = tv->table->col_count - 1; i >= 0; i--)
2131 r = TABLE_remove_column(view, tv->table->colinfo[i].number);
2132 if (r != ERROR_SUCCESS)
2133 return r;
2136 rec = MSI_CreateRecord(1);
2137 if (!rec)
2138 return ERROR_OUTOFMEMORY;
2140 MSI_RecordSetStringW(rec, 1, tv->name);
2142 r = TABLE_CreateView(tv->db, L"_Tables", &tables);
2143 if (r != ERROR_SUCCESS)
2145 msiobj_release(&rec->hdr);
2146 return r;
2149 r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row, NULL);
2150 if (r != ERROR_SUCCESS)
2151 goto done;
2153 r = TABLE_delete_row(tables, row);
2154 if (r != ERROR_SUCCESS)
2155 goto done;
2157 list_remove(&tv->table->entry);
2158 free_table(tv->table);
2160 done:
2161 msiobj_release(&rec->hdr);
2162 tables->ops->delete(tables);
2164 return r;
2167 static const MSIVIEWOPS table_ops =
2169 TABLE_fetch_int,
2170 TABLE_fetch_stream,
2171 TABLE_set_int,
2172 TABLE_set_string,
2173 TABLE_set_stream,
2174 TABLE_set_row,
2175 TABLE_insert_row,
2176 TABLE_delete_row,
2177 TABLE_execute,
2178 TABLE_close,
2179 TABLE_get_dimensions,
2180 TABLE_get_column_info,
2181 TABLE_modify,
2182 TABLE_delete,
2183 TABLE_add_ref,
2184 TABLE_release,
2185 TABLE_add_column,
2186 NULL,
2187 TABLE_drop,
2190 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
2192 MSITABLEVIEW *tv ;
2193 UINT r, sz;
2195 TRACE("%p %s %p\n", db, debugstr_w(name), view );
2197 if ( !wcscmp( name, L"_Streams" ) )
2198 return STREAMS_CreateView( db, view );
2199 else if ( !wcscmp( name, L"_Storages" ) )
2200 return STORAGES_CreateView( db, view );
2202 sz = FIELD_OFFSET( MSITABLEVIEW, name[lstrlenW( name ) + 1] );
2203 tv = msi_alloc_zero( sz );
2204 if( !tv )
2205 return ERROR_FUNCTION_FAILED;
2207 r = get_table( db, name, &tv->table );
2208 if( r != ERROR_SUCCESS )
2210 msi_free( tv );
2211 WARN("table not found\n");
2212 return r;
2215 TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count);
2217 /* fill the structure */
2218 tv->view.ops = &table_ops;
2219 tv->db = db;
2220 tv->columns = tv->table->colinfo;
2221 tv->num_cols = tv->table->col_count;
2222 tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES );
2224 TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
2226 *view = (MSIVIEW*) tv;
2227 lstrcpyW( tv->name, name );
2229 return ERROR_SUCCESS;
2232 static WCHAR* create_key_string(MSITABLEVIEW *tv, MSIRECORD *rec)
2234 DWORD i, p, len, key_len = 0;
2235 WCHAR *key;
2237 for (i = 0; i < tv->num_cols; i++)
2239 if (!(tv->columns[i].type & MSITYPE_KEY))
2240 continue;
2241 if (MSI_RecordGetStringW( rec, i+1, NULL, &len ) == ERROR_SUCCESS)
2242 key_len += len;
2243 key_len++;
2246 key = msi_alloc( key_len * sizeof(WCHAR) );
2247 if(!key)
2248 return NULL;
2250 p = 0;
2251 for (i = 0; i < tv->num_cols; i++)
2253 if (!(tv->columns[i].type & MSITYPE_KEY))
2254 continue;
2255 if (p)
2256 key[p++] = '\t';
2257 len = key_len - p;
2258 if (MSI_RecordGetStringW( rec, i+1, key + p, &len ) == ERROR_SUCCESS)
2259 p += len;
2261 return key;
2264 static UINT msi_record_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR name, UINT *len )
2266 UINT p = 0, l, i, r;
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, len, qlen;
2332 if (!wcscmp( tv->name, L"_Columns" ))
2334 ERR( "trying to modify existing column\n" );
2335 return ERROR_INSTALL_TRANSFORM_FAILURE;
2338 if (!wcscmp( tv->name, L"_Tables" ))
2340 ERR( "trying to modify existing table\n" );
2341 return ERROR_INSTALL_TRANSFORM_FAILURE;
2344 key = create_key_string( tv, rec );
2345 if (!key)
2346 return ERROR_OUTOFMEMORY;
2348 r = msi_view_get_row( tv->db, view, row, &old_rec );
2349 if (r != ERROR_SUCCESS)
2350 old_rec = NULL;
2352 for (i = 0; i < tv->num_cols; i++)
2354 if (!(mask & (1 << i)))
2355 continue;
2356 if (tv->columns[i].type & MSITYPE_KEY)
2357 continue;
2359 qlen = p = ARRAY_SIZE( query_pfx ) - 1;
2360 qlen += wcslen( tv->name ) + 3; /* strlen("','") */
2361 qlen += wcslen( tv->columns[i].colname ) + 3;
2362 qlen += wcslen( key ) + 3;
2363 if (MSITYPE_IS_BINARY( tv->columns[i].type ))
2364 r = msi_record_stream_name( tv, rec, NULL, &len );
2365 else
2366 r = MSI_RecordGetStringW( rec, i + 1, NULL, &len );
2367 if (r != ERROR_SUCCESS)
2369 if (old_rec)
2370 msiobj_release( &old_rec->hdr );
2371 msi_free( key );
2372 return r;
2374 qlen += len + 3;
2375 if (old_rec && (r = MSI_RecordGetStringW( old_rec, i+1, NULL, &len )))
2377 msiobj_release( &old_rec->hdr );
2378 msi_free( key );
2379 return r;
2381 qlen += len + 3; /* strlen("')") + 1 */
2383 if (qlen > ARRAY_SIZE(buf))
2385 query = msi_alloc( qlen * sizeof(WCHAR) );
2386 if (!query)
2388 if (old_rec)
2389 msiobj_release( &old_rec->hdr );
2390 msi_free( key );
2391 return ERROR_OUTOFMEMORY;
2394 else
2396 query = buf;
2399 memcpy( query, query_pfx, p * sizeof(WCHAR) );
2400 len = wcslen( tv->name );
2401 memcpy( query + p, tv->name, len * sizeof(WCHAR) );
2402 p += len;
2403 query[p++] = '\'';
2404 query[p++] = ',';
2405 query[p++] = '\'';
2406 len = wcslen( tv->columns[i].colname );
2407 memcpy( query + p, tv->columns[i].colname, len * sizeof(WCHAR) );
2408 p += len;
2409 query[p++] = '\'';
2410 query[p++] = ',';
2411 query[p++] = '\'';
2412 len = wcslen( key );
2413 memcpy( query + p, key, len * sizeof(WCHAR) );
2414 p += len;
2415 query[p++] = '\'';
2416 query[p++] = ',';
2417 query[p++] = '\'';
2418 len = qlen - p;
2419 if (MSITYPE_IS_BINARY( tv->columns[i].type ))
2420 msi_record_stream_name( tv, rec, query + p, &len );
2421 else
2422 MSI_RecordGetStringW( rec, i + 1, query + p, &len );
2423 p += len;
2424 query[p++] = '\'';
2425 query[p++] = ',';
2426 query[p++] = '\'';
2427 if (old_rec)
2429 len = qlen - p;
2430 MSI_RecordGetStringW( old_rec, i + 1, query + p, &len );
2431 p += len;
2433 query[p++] = '\'';
2434 query[p++] = ')';
2435 query[p++] = 0;
2437 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2438 if (query != buf)
2439 msi_free( query );
2440 if (r != ERROR_SUCCESS)
2442 if (old_rec)
2443 msiobj_release( &old_rec->hdr );
2444 msi_free( key );
2445 return r;
2448 r = MSI_ViewExecute( q, NULL );
2449 msiobj_release( &q->hdr );
2450 if (r != ERROR_SUCCESS)
2452 if (old_rec)
2453 msiobj_release( &old_rec->hdr );
2454 msi_free( key );
2455 return r;
2459 if (old_rec)
2460 msiobj_release( &old_rec->hdr );
2461 msi_free( key );
2462 return ERROR_SUCCESS;
2465 static UINT TransformView_create_table( MSITABLEVIEW *tv, MSIRECORD *rec )
2467 static const WCHAR query_fmt[] =
2468 L"INSERT INTO `_TransformView` (`Table`, `Column`, `new`) VALUES ('%s', 'CREATE', 1)";
2470 WCHAR buf[256], *query = buf;
2471 const WCHAR *name;
2472 MSIQUERY *q;
2473 int len;
2474 UINT r;
2476 name = msi_record_get_string( rec, 1, &len );
2477 if (!name)
2478 return ERROR_INSTALL_TRANSFORM_FAILURE;
2480 len = _snwprintf( NULL, 0, query_fmt, name ) + 1;
2481 if (len > ARRAY_SIZE(buf))
2483 query = msi_alloc( len * sizeof(WCHAR) );
2484 if (!query)
2485 return ERROR_OUTOFMEMORY;
2487 swprintf( query, len, query_fmt, name );
2489 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2490 if (query != buf)
2491 msi_free( query );
2492 if (r != ERROR_SUCCESS)
2493 return r;
2495 r = MSI_ViewExecute( q, NULL );
2496 msiobj_release( &q->hdr );
2497 return r;
2500 static UINT TransformView_add_column( MSITABLEVIEW *tv, MSIRECORD *rec )
2502 static const WCHAR query_pfx[] =
2503 L"INSERT INTO `_TransformView` (`new`, `Table`, `Current`, `Column`, `Data`) VALUES (1, '";
2505 WCHAR buf[256], *query = buf;
2506 UINT i, p, len, r, qlen;
2507 MSIQUERY *q;
2509 qlen = p = wcslen( query_pfx );
2510 for (i = 1; i <= 4; i++)
2512 r = MSI_RecordGetStringW( rec, i, NULL, &len );
2513 if (r != ERROR_SUCCESS)
2514 return r;
2515 qlen += len + 3; /* strlen( "','" ) */
2518 if (qlen > ARRAY_SIZE(buf))
2520 query = msi_alloc( len * sizeof(WCHAR) );
2521 qlen = len;
2522 if (!query)
2523 return ERROR_OUTOFMEMORY;
2526 memcpy( query, query_pfx, p * sizeof(WCHAR) );
2527 for (i = 1; i <= 4; i++)
2529 len = qlen - p;
2530 MSI_RecordGetStringW( rec, i, query + p, &len );
2531 p += len;
2532 query[p++] = '\'';
2533 if (i != 4)
2535 query[p++] = ',';
2536 query[p++] = '\'';
2539 query[p++] = ')';
2540 query[p++] = 0;
2542 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2543 if (query != buf)
2544 msi_free( query );
2545 if (r != ERROR_SUCCESS)
2546 return r;
2548 r = MSI_ViewExecute( q, NULL );
2549 msiobj_release( &q->hdr );
2550 return r;
2553 static UINT TransformView_insert_row( MSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
2555 static const WCHAR query_fmt[] =
2556 L"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`) VALUES (1, '%s', 'INSERT', '%s')";
2558 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2559 WCHAR buf[256], *query = buf;
2560 MSIQUERY *q;
2561 WCHAR *key;
2562 int len;
2563 UINT r;
2565 if (!wcscmp(tv->name, L"_Tables"))
2566 return TransformView_create_table( tv, rec );
2568 if (!wcscmp(tv->name, L"_Columns"))
2569 return TransformView_add_column( tv, rec );
2571 key = create_key_string( tv, rec );
2572 if (!key)
2573 return ERROR_OUTOFMEMORY;
2575 len = _snwprintf( NULL, 0, query_fmt, tv->name, key ) + 1;
2576 if (len > ARRAY_SIZE(buf))
2578 query = msi_alloc( len * sizeof(WCHAR) );
2579 if (!query)
2581 msi_free( key );
2582 return ERROR_OUTOFMEMORY;
2585 swprintf( query, len, query_fmt, tv->name, key );
2586 msi_free( key );
2588 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2589 if (query != buf)
2590 msi_free( query );
2591 if (r != ERROR_SUCCESS)
2592 return r;
2594 r = MSI_ViewExecute( q, NULL );
2595 msiobj_release( &q->hdr );
2596 if (r != ERROR_SUCCESS)
2597 return r;
2599 return TransformView_set_row( view, row, rec, ~0 );
2602 static UINT TransformView_drop_table( MSITABLEVIEW *tv, UINT row )
2604 static const WCHAR query_pfx[] = L"INSERT INTO `_TransformView` ( `new`, `Table`, `Column` ) VALUES ( 1, '";
2605 static const WCHAR query_sfx[] = L"', 'DROP' )";
2607 WCHAR buf[256], *query = buf;
2608 UINT r, table_id, len;
2609 const WCHAR *table;
2610 int table_len;
2611 MSIQUERY *q;
2613 r = TABLE_fetch_int( &tv->view, row, 1, &table_id );
2614 if (r != ERROR_SUCCESS)
2615 return r;
2617 table = msi_string_lookup( tv->db->strings, table_id, &table_len );
2618 if (!table)
2619 return ERROR_INSTALL_TRANSFORM_FAILURE;
2621 len = ARRAY_SIZE(query_pfx) - 1 + table_len + ARRAY_SIZE(query_sfx);
2622 if (len > ARRAY_SIZE(buf))
2624 query = msi_alloc( len * sizeof(WCHAR) );
2625 if (!query)
2626 return ERROR_OUTOFMEMORY;
2629 memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) );
2630 len = ARRAY_SIZE(query_pfx) - 1;
2631 memcpy( query + len, table, table_len * sizeof(WCHAR) );
2632 len += table_len;
2633 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2635 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2636 if (query != buf)
2637 msi_free( query );
2638 if (r != ERROR_SUCCESS)
2639 return r;
2641 r = MSI_ViewExecute( q, NULL );
2642 msiobj_release( &q->hdr );
2643 return r;
2646 static UINT TransformView_delete_row( MSIVIEW *view, UINT row )
2648 static const WCHAR query_pfx[] = L"INSERT INTO `_TransformView` ( `new`, `Table`, `Column`, `Row`) VALUES ( 1, '";
2649 static const WCHAR query_column[] = L"', 'DELETE', '";
2650 static const WCHAR query_sfx[] = L"')";
2652 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2653 WCHAR *key, buf[256], *query = buf;
2654 UINT r, len, name_len, key_len;
2655 MSIRECORD *rec;
2656 MSIQUERY *q;
2658 if (!wcscmp( tv->name, L"_Columns" ))
2660 ERR("trying to remove column\n");
2661 return ERROR_INSTALL_TRANSFORM_FAILURE;
2664 if (!wcscmp( tv->name, L"_Tables" ))
2665 return TransformView_drop_table( tv, row );
2667 r = msi_view_get_row( tv->db, view, row, &rec );
2668 if (r != ERROR_SUCCESS)
2669 return r;
2671 key = create_key_string( tv, rec );
2672 msiobj_release( &rec->hdr );
2673 if (!key)
2674 return ERROR_OUTOFMEMORY;
2676 name_len = wcslen( tv->name );
2677 key_len = wcslen( key );
2678 len = ARRAY_SIZE(query_pfx) + name_len + ARRAY_SIZE(query_column) + key_len + ARRAY_SIZE(query_sfx) - 2;
2679 if (len > ARRAY_SIZE(buf))
2681 query = msi_alloc( len * sizeof(WCHAR) );
2682 if (!query)
2684 msi_free( tv );
2685 msi_free( key );
2686 return ERROR_OUTOFMEMORY;
2690 memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) );
2691 len = ARRAY_SIZE(query_pfx) - 1;
2692 memcpy( query + len, tv->name, name_len * sizeof(WCHAR) );
2693 len += name_len;
2694 memcpy( query + len, query_column, ARRAY_SIZE(query_column) * sizeof(WCHAR) );
2695 len += ARRAY_SIZE(query_column) - 1;
2696 memcpy( query + len, key, key_len * sizeof(WCHAR) );
2697 len += key_len;
2698 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2699 msi_free( key );
2701 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2702 if (query != buf)
2703 msi_free( query );
2704 if (r != ERROR_SUCCESS)
2705 return r;
2707 r = MSI_ViewExecute( q, NULL );
2708 msiobj_release( &q->hdr );
2709 return r;
2712 static UINT TransformView_execute( MSIVIEW *view, MSIRECORD *record )
2714 return ERROR_SUCCESS;
2717 static UINT TransformView_close( MSIVIEW *view )
2719 return ERROR_SUCCESS;
2722 static UINT TransformView_get_dimensions( MSIVIEW *view, UINT *rows, UINT *cols )
2724 return TABLE_get_dimensions( view, rows, cols );
2727 static UINT TransformView_get_column_info( MSIVIEW *view, UINT n, LPCWSTR *name, UINT *type,
2728 BOOL *temporary, LPCWSTR *table_name )
2730 return TABLE_get_column_info( view, n, name, type, temporary, table_name );
2733 static UINT TransformView_delete( MSIVIEW *view )
2735 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2736 if (!tv->table || tv->columns != tv->table->colinfo)
2737 msi_free( tv->columns );
2738 return TABLE_delete( view );
2741 static const MSIVIEWOPS transform_view_ops =
2743 TransformView_fetch_int,
2744 TransformView_fetch_stream,
2745 NULL,
2746 NULL,
2747 NULL,
2748 TransformView_set_row,
2749 TransformView_insert_row,
2750 TransformView_delete_row,
2751 TransformView_execute,
2752 TransformView_close,
2753 TransformView_get_dimensions,
2754 TransformView_get_column_info,
2755 NULL,
2756 TransformView_delete,
2757 NULL,
2758 NULL,
2759 NULL,
2760 NULL,
2761 NULL
2764 UINT TransformView_Create( MSIDATABASE *db, string_table *st, LPCWSTR name, MSIVIEW **view )
2766 static const WCHAR query_pfx[] = L"SELECT `Column`, `Data`, `Current` FROM `_TransformView` WHERE `Table`='";
2767 static const WCHAR query_sfx[] = L"' AND `Row` IS NULL AND `Current` IS NOT NULL AND `new` = 1";
2769 WCHAR buf[256], *query = buf;
2770 UINT r, len, name_len, size, add_col;
2771 MSICOLUMNINFO *colinfo;
2772 MSITABLEVIEW *tv;
2773 MSIRECORD *rec;
2774 MSIQUERY *q;
2776 name_len = wcslen( name );
2778 r = TABLE_CreateView( db, name, view );
2779 if (r == ERROR_INVALID_PARAMETER)
2781 /* table does not exist */
2782 size = FIELD_OFFSET( MSITABLEVIEW, name[name_len + 1] );
2783 tv = msi_alloc_zero( size );
2784 if (!tv)
2785 return ERROR_OUTOFMEMORY;
2787 tv->db = db;
2788 memcpy( tv->name, name, name_len * sizeof(WCHAR) );
2789 *view = (MSIVIEW*)tv;
2791 else if (r != ERROR_SUCCESS)
2793 return r;
2795 else
2797 tv = (MSITABLEVIEW*)*view;
2800 tv->view.ops = &transform_view_ops;
2802 len = ARRAY_SIZE(query_pfx) + name_len + ARRAY_SIZE(query_sfx) - 1;
2803 if (len > ARRAY_SIZE(buf))
2805 query = msi_alloc( len * sizeof(WCHAR) );
2806 if (!query)
2808 msi_free( tv );
2809 return ERROR_OUTOFMEMORY;
2812 memcpy( query, query_pfx, ARRAY_SIZE(query_pfx) * sizeof(WCHAR) );
2813 len = ARRAY_SIZE(query_pfx) - 1;
2814 memcpy( query + len, name, name_len * sizeof(WCHAR) );
2815 len += name_len;
2816 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2818 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2819 if (query != buf)
2820 msi_free( query );
2821 if (r != ERROR_SUCCESS)
2823 msi_free( tv );
2824 return r;
2827 r = MSI_ViewExecute( q, NULL );
2828 if (r != ERROR_SUCCESS)
2830 msi_free( tv );
2831 return r;
2834 r = q->view->ops->get_dimensions( q->view, &add_col, NULL );
2835 if (r != ERROR_SUCCESS)
2837 MSI_ViewClose( q );
2838 msiobj_release( &q->hdr );
2839 msi_free( tv );
2840 return r;
2842 if (!add_col)
2844 MSI_ViewClose( q );
2845 msiobj_release( &q->hdr );
2846 return ERROR_SUCCESS;
2849 colinfo = msi_alloc_zero( (add_col + tv->num_cols) * sizeof(*colinfo) );
2850 if (!colinfo)
2852 MSI_ViewClose( q );
2853 msiobj_release( &q->hdr );
2854 msi_free( tv );
2855 return r;
2858 while (MSI_ViewFetch( q, &rec ) == ERROR_SUCCESS)
2860 int name_len;
2861 const WCHAR *name = msi_record_get_string( rec, 1, &name_len );
2862 const WCHAR *type = msi_record_get_string( rec, 2, NULL );
2863 UINT name_id, idx;
2865 idx = _wtoi( msi_record_get_string(rec, 3, NULL) );
2866 colinfo[idx - 1].number = idx;
2867 colinfo[idx - 1].type = _wtoi( type );
2869 r = msi_string2id( st, name, name_len, &name_id );
2870 if (r == ERROR_SUCCESS)
2871 colinfo[idx - 1].colname = msi_string_lookup( st, name_id, NULL );
2872 else
2873 ERR( "column name %s is not defined in strings table\n", wine_dbgstr_w(name) );
2874 msiobj_release( &rec->hdr );
2876 MSI_ViewClose( q );
2877 msiobj_release( &q->hdr );
2879 memcpy( colinfo, tv->columns, tv->num_cols * sizeof(*colinfo) );
2880 tv->columns = colinfo;
2881 tv->num_cols += add_col;
2882 return ERROR_SUCCESS;
2885 UINT MSI_CommitTables( MSIDATABASE *db )
2887 UINT r, bytes_per_strref;
2888 HRESULT hr;
2889 MSITABLE *table = NULL;
2891 TRACE("%p\n",db);
2893 r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref );
2894 if( r != ERROR_SUCCESS )
2896 WARN("failed to save string table r=%08x\n",r);
2897 return r;
2900 LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
2902 r = save_table( db, table, bytes_per_strref );
2903 if( r != ERROR_SUCCESS )
2905 WARN("failed to save table %s (r=%08x)\n",
2906 debugstr_w(table->name), r);
2907 return r;
2911 hr = IStorage_Commit( db->storage, 0 );
2912 if (FAILED( hr ))
2914 WARN("failed to commit changes 0x%08x\n", hr);
2915 r = ERROR_FUNCTION_FAILED;
2917 return r;
2920 MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
2922 MSITABLE *t;
2923 UINT r;
2925 TRACE("%p %s\n", db, debugstr_w(table));
2927 if (!table)
2928 return MSICONDITION_ERROR;
2930 r = get_table( db, table, &t );
2931 if (r != ERROR_SUCCESS)
2932 return MSICONDITION_NONE;
2934 return t->persistent;
2937 static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes)
2939 UINT ret = 0, i;
2941 for (i = 0; i < bytes; i++)
2942 ret += (data[col + i] << i * 8);
2944 return ret;
2947 static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname )
2949 UINT r, len;
2950 WCHAR *name;
2952 TRACE("%p %p\n", tv, rec);
2954 r = msi_record_stream_name( tv, rec, NULL, &len );
2955 if (r != ERROR_SUCCESS)
2956 return r;
2957 len++;
2959 name = msi_alloc( len * sizeof(WCHAR) );
2960 if (!name)
2961 return ERROR_OUTOFMEMORY;
2963 r = msi_record_stream_name( tv, rec, name, &len );
2964 if (r != ERROR_SUCCESS)
2966 msi_free( name );
2967 return r;
2970 *pstname = encode_streamname( FALSE, name );
2971 msi_free( name );
2972 return ERROR_SUCCESS;
2975 static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st,
2976 IStorage *stg, const BYTE *rawdata, UINT bytes_per_strref )
2978 UINT i, val, ofs = 0;
2979 USHORT mask;
2980 MSICOLUMNINFO *columns = tv->columns;
2981 MSIRECORD *rec;
2983 mask = rawdata[0] | (rawdata[1] << 8);
2984 rawdata += 2;
2986 rec = MSI_CreateRecord( tv->num_cols );
2987 if( !rec )
2988 return rec;
2990 TRACE("row ->\n");
2991 for( i=0; i<tv->num_cols; i++ )
2993 if ( (mask&1) && (i>=(mask>>8)) )
2994 break;
2995 /* all keys must be present */
2996 if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
2997 continue;
2999 if( MSITYPE_IS_BINARY(tv->columns[i].type) )
3001 LPWSTR encname;
3002 IStream *stm = NULL;
3003 UINT r;
3005 ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref );
3007 r = msi_record_encoded_stream_name( tv, rec, &encname );
3008 if ( r != ERROR_SUCCESS )
3010 msiobj_release( &rec->hdr );
3011 return NULL;
3013 r = IStorage_OpenStream( stg, encname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
3014 if ( r != ERROR_SUCCESS )
3016 msiobj_release( &rec->hdr );
3017 msi_free( encname );
3018 return NULL;
3021 MSI_RecordSetStream( rec, i+1, stm );
3022 TRACE(" field %d [%s]\n", i+1, debugstr_w(encname));
3023 msi_free( encname );
3025 else if( columns[i].type & MSITYPE_STRING )
3027 int len;
3028 const WCHAR *sval;
3030 val = read_raw_int(rawdata, ofs, bytes_per_strref);
3031 sval = msi_string_lookup( st, val, &len );
3032 msi_record_set_string( rec, i+1, sval, len );
3033 TRACE(" field %d [%s]\n", i+1, debugstr_wn(sval, len));
3034 ofs += bytes_per_strref;
3036 else
3038 UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref );
3039 switch( n )
3041 case 2:
3042 val = read_raw_int(rawdata, ofs, n);
3043 if (val)
3044 MSI_RecordSetInteger( rec, i+1, val-0x8000 );
3045 TRACE(" field %d [0x%04x]\n", i+1, val );
3046 break;
3047 case 4:
3048 val = read_raw_int(rawdata, ofs, n);
3049 if (val)
3050 MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
3051 TRACE(" field %d [0x%08x]\n", i+1, val );
3052 break;
3053 default:
3054 ERR("oops - unknown column width %d\n", n);
3055 break;
3057 ofs += n;
3060 return rec;
3063 static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize )
3065 UINT i;
3066 for (i = 0; i < rawsize / 2; i++)
3068 int len;
3069 const WCHAR *sval = msi_string_lookup( st, rawdata[i], &len );
3070 MESSAGE(" %04x %s\n", rawdata[i], debugstr_wn(sval, len) );
3074 static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec )
3076 UINT i, r, *data;
3078 data = msi_alloc( tv->num_cols *sizeof (UINT) );
3079 for( i=0; i<tv->num_cols; i++ )
3081 data[i] = 0;
3083 if ( ~tv->columns[i].type & MSITYPE_KEY )
3084 continue;
3086 /* turn the transform column value into a row value */
3087 if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
3088 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
3090 int len;
3091 const WCHAR *str = msi_record_get_string( rec, i+1, &len );
3092 if (str)
3094 r = msi_string2id( tv->db->strings, str, len, &data[i] );
3096 /* if there's no matching string in the string table,
3097 these keys can't match any record, so fail now. */
3098 if (r != ERROR_SUCCESS)
3100 msi_free( data );
3101 return NULL;
3104 else data[i] = 0;
3106 else
3108 if (int_to_table_storage( tv, i + 1, MSI_RecordGetInteger( rec, i + 1 ), &data[i] ))
3110 msi_free( data );
3111 return NULL;
3115 return data;
3118 static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column )
3120 UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
3122 for( i=0; i<tv->num_cols; i++ )
3124 if ( ~tv->columns[i].type & MSITYPE_KEY )
3125 continue;
3127 /* turn the transform column value into a row value */
3128 r = TABLE_fetch_int( &tv->view, row, i+1, &x );
3129 if ( r != ERROR_SUCCESS )
3131 ERR("TABLE_fetch_int shouldn't fail here\n");
3132 break;
3135 /* if this key matches, move to the next column */
3136 if ( x != data[i] )
3138 ret = ERROR_FUNCTION_FAILED;
3139 break;
3141 if (column) *column = i;
3142 ret = ERROR_SUCCESS;
3144 return ret;
3147 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column )
3149 UINT i, r = ERROR_FUNCTION_FAILED, *data;
3151 data = msi_record_to_row( tv, rec );
3152 if( !data )
3153 return r;
3154 for( i = 0; i < tv->table->row_count; i++ )
3156 r = msi_row_matches( tv, i, data, column );
3157 if( r == ERROR_SUCCESS )
3159 *row = i;
3160 break;
3163 msi_free( data );
3164 return r;
3167 typedef struct
3169 struct list entry;
3170 LPWSTR name;
3171 } TRANSFORMDATA;
3173 static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
3174 string_table *st, TRANSFORMDATA *transform,
3175 UINT bytes_per_strref, int err_cond )
3177 BYTE *rawdata = NULL;
3178 MSITABLEVIEW *tv = NULL;
3179 UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0;
3180 MSIRECORD *rec = NULL;
3181 WCHAR coltable[32];
3182 const WCHAR *name;
3184 if (!transform)
3185 return ERROR_SUCCESS;
3187 name = transform->name;
3189 coltable[0] = 0;
3190 TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
3192 /* read the transform data */
3193 read_stream_data( stg, name, TRUE, &rawdata, &rawsize );
3194 if ( !rawdata )
3196 TRACE("table %s empty\n", debugstr_w(name) );
3197 return ERROR_INVALID_TABLE;
3200 /* create a table view */
3201 if ( err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM )
3202 r = TransformView_Create( db, st, name, (MSIVIEW**) &tv );
3203 else
3204 r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
3205 if( r != ERROR_SUCCESS )
3206 goto err;
3208 r = tv->view.ops->execute( &tv->view, NULL );
3209 if( r != ERROR_SUCCESS )
3210 goto err;
3212 TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
3213 debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
3215 /* interpret the data */
3216 for (n = 0; n < rawsize;)
3218 mask = rawdata[n] | (rawdata[n + 1] << 8);
3219 if (mask & 1)
3222 * if the low bit is set, columns are continuous and
3223 * the number of columns is specified in the high byte
3225 sz = 2;
3226 num_cols = mask >> 8;
3227 if (num_cols > tv->num_cols)
3229 ERR("excess columns in transform: %u > %u\n", num_cols, tv->num_cols);
3230 break;
3233 for (i = 0; i < num_cols; i++)
3235 if( (tv->columns[i].type & MSITYPE_STRING) &&
3236 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
3237 sz += bytes_per_strref;
3238 else
3239 sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
3242 else
3245 * If the low bit is not set, mask is a bitmask.
3246 * Excepting for key fields, which are always present,
3247 * each bit indicates that a field is present in the transform record.
3249 * mask == 0 is a special case ... only the keys will be present
3250 * and it means that this row should be deleted.
3252 sz = 2;
3253 num_cols = tv->num_cols;
3254 for (i = 0; i < num_cols; i++)
3256 if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask))
3258 if ((tv->columns[i].type & MSITYPE_STRING) &&
3259 !MSITYPE_IS_BINARY(tv->columns[i].type))
3260 sz += bytes_per_strref;
3261 else
3262 sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
3267 /* check we didn't run of the end of the table */
3268 if (n + sz > rawsize)
3270 ERR("borked.\n");
3271 dump_table( st, (USHORT *)rawdata, rawsize );
3272 break;
3275 rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref );
3276 if (rec)
3278 WCHAR table[32];
3279 DWORD sz = 32;
3280 UINT number = MSI_NULL_INTEGER;
3281 UINT row = 0;
3283 if (!wcscmp( name, L"_Columns" ))
3285 MSI_RecordGetStringW( rec, 1, table, &sz );
3286 number = MSI_RecordGetInteger( rec, 2 );
3289 * Native msi seems writes nul into the Number (2nd) column of
3290 * the _Columns table when there are new columns
3292 if ( number == MSI_NULL_INTEGER )
3294 /* reset the column number on a new table */
3295 if (wcscmp( coltable, table ))
3297 colcol = 0;
3298 lstrcpyW( coltable, table );
3301 /* fix nul column numbers */
3302 MSI_RecordSetInteger( rec, 2, ++colcol );
3306 if (TRACE_ON(msidb)) dump_record( rec );
3308 if (tv->table)
3309 r = msi_table_find_row( tv, rec, &row, NULL );
3310 else
3311 r = ERROR_FUNCTION_FAILED;
3312 if (r == ERROR_SUCCESS)
3314 if (!mask)
3316 TRACE("deleting row [%d]:\n", row);
3317 r = tv->view.ops->delete_row( &tv->view, row );
3318 if (r != ERROR_SUCCESS)
3319 WARN("failed to delete row %u\n", r);
3321 else if (mask & 1)
3323 TRACE("modifying full row [%d]:\n", row);
3324 r = tv->view.ops->set_row( &tv->view, row, rec, (1 << tv->num_cols) - 1 );
3325 if (r != ERROR_SUCCESS)
3326 WARN("failed to modify row %u\n", r);
3328 else
3330 TRACE("modifying masked row [%d]:\n", row);
3331 r = tv->view.ops->set_row( &tv->view, row, rec, mask );
3332 if (r != ERROR_SUCCESS)
3333 WARN("failed to modify row %u\n", r);
3336 else
3338 TRACE("inserting row\n");
3339 r = tv->view.ops->insert_row( &tv->view, rec, -1, FALSE );
3340 if (r != ERROR_SUCCESS)
3341 WARN("failed to insert row %u\n", r);
3344 if (!(err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM) &&
3345 !wcscmp( name, L"_Columns" ))
3346 msi_update_table_columns( db, table );
3348 msiobj_release( &rec->hdr );
3351 n += sz;
3354 err:
3355 /* no need to free the table, it's associated with the database */
3356 msi_free( rawdata );
3357 if( tv )
3358 tv->view.ops->delete( &tv->view );
3360 return ERROR_SUCCESS;
3364 * msi_table_apply_transform
3366 * Enumerate the table transforms in a transform storage and apply each one.
3368 UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg, int err_cond )
3370 struct list transforms;
3371 IEnumSTATSTG *stgenum = NULL;
3372 TRANSFORMDATA *transform;
3373 TRANSFORMDATA *tables = NULL, *columns = NULL;
3374 HRESULT hr;
3375 STATSTG stat;
3376 string_table *strings;
3377 UINT ret = ERROR_FUNCTION_FAILED;
3378 UINT bytes_per_strref;
3379 BOOL property_update = FALSE;
3380 MSIVIEW *transform_view = NULL;
3382 TRACE("%p %p\n", db, stg );
3384 strings = msi_load_string_table( stg, &bytes_per_strref );
3385 if( !strings )
3386 goto end;
3388 hr = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
3389 if (FAILED( hr ))
3390 goto end;
3392 list_init(&transforms);
3394 while ( TRUE )
3396 MSITABLEVIEW *tv = NULL;
3397 WCHAR name[0x40];
3398 ULONG count = 0;
3400 hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
3401 if (FAILED( hr ) || !count)
3402 break;
3404 decode_streamname( stat.pwcsName, name );
3405 CoTaskMemFree( stat.pwcsName );
3406 if ( name[0] != 0x4840 )
3407 continue;
3409 if ( !wcscmp( name+1, L"_StringPool" ) ||
3410 !wcscmp( name+1, L"_StringData" ) )
3411 continue;
3413 transform = msi_alloc_zero( sizeof(TRANSFORMDATA) );
3414 if ( !transform )
3415 break;
3417 list_add_tail( &transforms, &transform->entry );
3419 transform->name = strdupW( name + 1 );
3421 if ( !wcscmp( transform->name, L"_Tables" ) )
3422 tables = transform;
3423 else if (!wcscmp( transform->name, L"_Columns" ) )
3424 columns = transform;
3425 else if (!wcscmp( transform->name, L"Property" ))
3426 property_update = TRUE;
3428 TRACE("transform contains stream %s\n", debugstr_w(name));
3430 /* load the table */
3431 if (TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv ) != ERROR_SUCCESS)
3432 continue;
3434 if (tv->view.ops->execute( &tv->view, NULL ) != ERROR_SUCCESS)
3436 tv->view.ops->delete( &tv->view );
3437 continue;
3440 tv->view.ops->delete( &tv->view );
3443 if (err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM)
3445 static const WCHAR create_query[] = L"CREATE TABLE `_TransformView` ( "
3446 L"`Table` CHAR(0) NOT NULL TEMPORARY, `Column` CHAR(0) NOT NULL TEMPORARY, "
3447 L"`Row` CHAR(0) TEMPORARY, `Data` CHAR(0) TEMPORARY, `Current` CHAR(0) TEMPORARY "
3448 L"PRIMARY KEY `Table`, `Column`, `Row` ) HOLD";
3450 MSIQUERY *query;
3451 UINT r;
3453 r = MSI_DatabaseOpenViewW( db, create_query, &query );
3454 if (r != ERROR_SUCCESS)
3455 goto end;
3457 r = MSI_ViewExecute( query, NULL );
3458 if (r == ERROR_SUCCESS)
3459 MSI_ViewClose( query );
3460 msiobj_release( &query->hdr );
3461 if (r != ERROR_BAD_QUERY_SYNTAX && r != ERROR_SUCCESS)
3462 goto end;
3464 if (TABLE_CreateView(db, L"_TransformView", &transform_view) != ERROR_SUCCESS)
3465 goto end;
3467 if (r == ERROR_BAD_QUERY_SYNTAX)
3468 transform_view->ops->add_ref( transform_view );
3470 r = transform_view->ops->add_column( transform_view, L"new",
3471 MSITYPE_TEMPORARY | MSITYPE_NULLABLE | 0x402 /* INT */, FALSE );
3472 if (r != ERROR_SUCCESS)
3473 goto end;
3477 * Apply _Tables and _Columns transforms first so that
3478 * the table metadata is correct, and empty tables exist.
3480 ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref, err_cond );
3481 if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
3482 goto end;
3484 ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref, err_cond );
3485 if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
3486 goto end;
3488 ret = ERROR_SUCCESS;
3490 while ( !list_empty( &transforms ) )
3492 transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry );
3494 if ( wcscmp( transform->name, L"_Columns" ) &&
3495 wcscmp( transform->name, L"_Tables" ) &&
3496 ret == ERROR_SUCCESS )
3498 ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref, err_cond );
3501 list_remove( &transform->entry );
3502 msi_free( transform->name );
3503 msi_free( transform );
3506 if ( ret == ERROR_SUCCESS )
3508 append_storage_to_db( db, stg );
3509 if (property_update) msi_clone_properties( db );
3512 end:
3513 if ( stgenum )
3514 IEnumSTATSTG_Release( stgenum );
3515 if ( strings )
3516 msi_destroy_stringtable( strings );
3517 if (transform_view)
3519 struct tagMSITABLE *table = ((MSITABLEVIEW*)transform_view)->table;
3521 if (ret != ERROR_SUCCESS)
3522 transform_view->ops->release( transform_view );
3524 if (!wcscmp(table->colinfo[table->col_count - 1].colname, L"new"))
3525 TABLE_remove_column( transform_view, table->colinfo[table->col_count - 1].number );
3526 transform_view->ops->delete( transform_view );
3529 return ret;