msi: Use fetch_int to reduce code duplication and access to table data.
[wine/wine-gecko.git] / dlls / msi / table.c
blobbba93dc3107944f5d876dbbc70e89c8fb4132dc9
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
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winerror.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "objbase.h"
34 #include "objidl.h"
35 #include "winnls.h"
36 #include "msipriv.h"
37 #include "query.h"
39 #include "wine/debug.h"
40 #include "wine/unicode.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
44 #define MSITABLE_HASH_TABLE_SIZE 37
46 typedef struct tagMSICOLUMNHASHENTRY
48 struct tagMSICOLUMNHASHENTRY *next;
49 UINT value;
50 UINT row;
51 } MSICOLUMNHASHENTRY;
53 typedef struct tagMSICOLUMNINFO
55 LPCWSTR tablename;
56 UINT number;
57 LPCWSTR colname;
58 UINT type;
59 UINT offset;
60 MSICOLUMNHASHENTRY **hash_table;
61 } MSICOLUMNINFO;
63 struct tagMSITABLE
65 USHORT **data;
66 UINT row_count;
67 USHORT **nonpersistent_data;
68 UINT nonpersistent_row_count;
69 struct list entry;
70 MSICOLUMNINFO *colinfo;
71 UINT col_count;
72 BOOL persistent;
73 WCHAR name[1];
76 typedef struct tagMSITRANSFORM {
77 struct list entry;
78 IStorage *stg;
79 } MSITRANSFORM;
81 static const WCHAR szStringData[] = {
82 '_','S','t','r','i','n','g','D','a','t','a',0 };
83 static const WCHAR szStringPool[] = {
84 '_','S','t','r','i','n','g','P','o','o','l',0 };
86 /* information for default tables */
87 static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
88 static const WCHAR szTable[] = { 'T','a','b','l','e',0 };
89 static const WCHAR szName[] = { 'N','a','m','e',0 };
90 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
91 static const WCHAR szNumber[] = { 'N','u','m','b','e','r',0 };
92 static const WCHAR szType[] = { 'T','y','p','e',0 };
94 /* These tables are written into (the .hash_table part).
95 * Do not mark them const.
97 static MSICOLUMNINFO _Columns_cols[4] = {
98 { szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
99 { szColumns, 2, szNumber, MSITYPE_VALID | 2, 2 },
100 { szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4 },
101 { szColumns, 4, szType, MSITYPE_VALID | 2, 6 },
103 static MSICOLUMNINFO _Tables_cols[1] = {
104 { szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 0 },
107 #define MAX_STREAM_NAME 0x1f
109 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
110 MSICOLUMNINFO **pcols, UINT *pcount );
111 static void table_calc_column_offsets( MSICOLUMNINFO *colinfo, DWORD count );
112 static UINT get_tablecolumns( MSIDATABASE *db,
113 LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
114 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count );
116 static inline UINT bytes_per_column( const MSICOLUMNINFO *col )
118 if( col->type & MSITYPE_STRING )
119 return 2;
120 if( (col->type & 0xff) > 4 )
121 ERR("Invalid column size!\n");
122 return col->type & 0xff;
125 static int utf2mime(int x)
127 if( (x>='0') && (x<='9') )
128 return x-'0';
129 if( (x>='A') && (x<='Z') )
130 return x-'A'+10;
131 if( (x>='a') && (x<='z') )
132 return x-'a'+10+26;
133 if( x=='.' )
134 return 10+26+26;
135 if( x=='_' )
136 return 10+26+26+1;
137 return -1;
140 LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
142 DWORD count = MAX_STREAM_NAME;
143 DWORD ch, next;
144 LPWSTR out, p;
146 if( !bTable )
147 count = lstrlenW( in )+2;
148 out = msi_alloc( count*sizeof(WCHAR) );
149 p = out;
151 if( bTable )
153 *p++ = 0x4840;
154 count --;
156 while( count -- )
158 ch = *in++;
159 if( !ch )
161 *p = ch;
162 return out;
164 if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
166 ch = utf2mime(ch) + 0x4800;
167 next = *in;
168 if( next && (next<0x80) )
170 next = utf2mime(next);
171 if( next >= 0 )
173 next += 0x3ffffc0;
174 ch += (next<<6);
175 in++;
179 *p++ = ch;
181 ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
182 msi_free( out );
183 return NULL;
186 static int mime2utf(int x)
188 if( x<10 )
189 return x + '0';
190 if( x<(10+26))
191 return x - 10 + 'A';
192 if( x<(10+26+26))
193 return x - 10 - 26 + 'a';
194 if( x == (10+26+26) )
195 return '.';
196 return '_';
199 BOOL decode_streamname(LPWSTR in, LPWSTR out)
201 WCHAR ch;
202 DWORD count = 0;
204 while ( (ch = *in++) )
206 if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
208 if( ch >= 0x4800 )
209 ch = mime2utf(ch-0x4800);
210 else
212 ch -= 0x3800;
213 *out++ = mime2utf(ch&0x3f);
214 count++;
215 ch = mime2utf((ch>>6)&0x3f);
218 *out++ = ch;
219 count++;
221 *out = 0;
222 return count;
225 void enum_stream_names( IStorage *stg )
227 IEnumSTATSTG *stgenum = NULL;
228 HRESULT r;
229 STATSTG stat;
230 ULONG n, count;
231 WCHAR name[0x40];
233 r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
234 if( FAILED( r ) )
235 return;
237 n = 0;
238 while( 1 )
240 count = 0;
241 r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
242 if( FAILED( r ) || !count )
243 break;
244 decode_streamname( stat.pwcsName, name );
245 TRACE("stream %2d -> %s %s\n", n,
246 debugstr_w(stat.pwcsName), debugstr_w(name) );
247 n++;
250 IEnumSTATSTG_Release( stgenum );
253 UINT read_stream_data( IStorage *stg, LPCWSTR stname,
254 USHORT **pdata, UINT *psz )
256 HRESULT r;
257 UINT ret = ERROR_FUNCTION_FAILED;
258 VOID *data;
259 ULONG sz, count;
260 IStream *stm = NULL;
261 STATSTG stat;
262 LPWSTR encname;
264 encname = encode_streamname(TRUE, stname);
266 TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
268 r = IStorage_OpenStream(stg, encname, NULL,
269 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
270 msi_free( encname );
271 if( FAILED( r ) )
273 WARN("open stream failed r = %08x - empty table?\n", r);
274 return ret;
277 r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
278 if( FAILED( r ) )
280 WARN("open stream failed r = %08x!\n", r);
281 goto end;
284 if( stat.cbSize.QuadPart >> 32 )
286 WARN("Too big!\n");
287 goto end;
290 sz = stat.cbSize.QuadPart;
291 data = msi_alloc( sz );
292 if( !data )
294 WARN("couldn't allocate memory r=%08x!\n", r);
295 ret = ERROR_NOT_ENOUGH_MEMORY;
296 goto end;
299 r = IStream_Read(stm, data, sz, &count );
300 if( FAILED( r ) || ( count != sz ) )
302 msi_free( data );
303 WARN("read stream failed r = %08x!\n", r);
304 goto end;
307 *pdata = data;
308 *psz = sz;
309 ret = ERROR_SUCCESS;
311 end:
312 IStream_Release( stm );
314 return ret;
317 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
319 LPWSTR encname;
320 HRESULT r;
322 encname = encode_streamname(FALSE, stname);
324 TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
326 r = IStorage_OpenStream(db->storage, encname, NULL,
327 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
328 if( FAILED( r ) )
330 MSITRANSFORM *transform;
332 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
334 TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
335 r = IStorage_OpenStream( transform->stg, encname, NULL,
336 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
337 if (SUCCEEDED(r))
338 break;
342 msi_free( encname );
344 return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
347 UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
348 USHORT **pdata, UINT *psz )
350 HRESULT r;
351 UINT ret = ERROR_FUNCTION_FAILED;
352 VOID *data;
353 ULONG sz, count;
354 IStream *stm = NULL;
355 STATSTG stat;
357 r = db_get_raw_stream( db, stname, &stm );
358 if( r != ERROR_SUCCESS)
359 return ret;
360 r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
361 if( FAILED( r ) )
363 WARN("open stream failed r = %08x!\n", r);
364 goto end;
367 if( stat.cbSize.QuadPart >> 32 )
369 WARN("Too big!\n");
370 goto end;
373 sz = stat.cbSize.QuadPart;
374 data = msi_alloc( sz );
375 if( !data )
377 WARN("couldn't allocate memory r=%08x!\n", r);
378 ret = ERROR_NOT_ENOUGH_MEMORY;
379 goto end;
382 r = IStream_Read(stm, data, sz, &count );
383 if( FAILED( r ) || ( count != sz ) )
385 msi_free( data );
386 WARN("read stream failed r = %08x!\n", r);
387 goto end;
390 *pdata = data;
391 *psz = sz;
392 ret = ERROR_SUCCESS;
394 end:
395 IStream_Release( stm );
397 return ret;
400 UINT write_stream_data( IStorage *stg, LPCWSTR stname,
401 LPVOID data, UINT sz, BOOL bTable )
403 HRESULT r;
404 UINT ret = ERROR_FUNCTION_FAILED;
405 ULONG count;
406 IStream *stm = NULL;
407 ULARGE_INTEGER size;
408 LARGE_INTEGER pos;
409 LPWSTR encname;
411 encname = encode_streamname(bTable, stname );
412 r = IStorage_OpenStream( stg, encname, NULL,
413 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
414 if( FAILED(r) )
416 r = IStorage_CreateStream( stg, encname,
417 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
419 msi_free( encname );
420 if( FAILED( r ) )
422 WARN("open stream failed r = %08x\n", r);
423 return ret;
426 size.QuadPart = sz;
427 r = IStream_SetSize( stm, size );
428 if( FAILED( r ) )
430 WARN("Failed to SetSize\n");
431 goto end;
434 pos.QuadPart = 0;
435 r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
436 if( FAILED( r ) )
438 WARN("Failed to Seek\n");
439 goto end;
442 if (sz)
444 r = IStream_Write(stm, data, sz, &count );
445 if( FAILED( r ) || ( count != sz ) )
447 WARN("Failed to Write\n");
448 goto end;
452 ret = ERROR_SUCCESS;
454 end:
455 IStream_Release( stm );
457 return ret;
460 static void free_table( MSITABLE *table )
462 int i;
463 for( i=0; i<table->row_count; i++ )
464 msi_free( table->data[i] );
465 msi_free( table->data );
466 for( i=0; i<table->nonpersistent_row_count; i++ )
467 msi_free( table->nonpersistent_data[i] );
468 msi_free( table->nonpersistent_data );
469 if( (table->colinfo != _Tables_cols) &&
470 (table->colinfo != _Columns_cols) )
472 msi_free_colinfo( table->colinfo, table->col_count );
473 msi_free( table->colinfo );
475 msi_free( table );
478 static UINT msi_table_get_row_size( const MSICOLUMNINFO *cols, UINT count )
480 const MSICOLUMNINFO *last_col = &cols[count-1];
481 if (!count)
482 return 0;
483 return last_col->offset + bytes_per_column( last_col );
486 /* add this table to the list of cached tables in the database */
487 static UINT read_table_from_storage( MSITABLE *t, IStorage *stg )
489 USHORT *rawdata = NULL;
490 UINT rawsize = 0, i, j, row_size = 0;
492 TRACE("%s\n",debugstr_w(t->name));
494 row_size = msi_table_get_row_size( t->colinfo, t->col_count );
496 /* if we can't read the table, just assume that it's empty */
497 read_stream_data( stg, t->name, &rawdata, &rawsize );
498 if( !rawdata )
499 return ERROR_SUCCESS;
501 TRACE("Read %d bytes\n", rawsize );
503 if( rawsize % row_size )
505 WARN("Table size is invalid %d/%d\n", rawsize, row_size );
506 goto err;
509 t->row_count = rawsize / row_size;
510 t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) );
511 if( !t->data )
512 goto err;
514 /* transpose all the data */
515 TRACE("Transposing data from %d rows\n", t->row_count );
516 for( i=0; i<t->row_count; i++ )
518 t->data[i] = msi_alloc( row_size );
519 if( !t->data[i] )
520 goto err;
522 for( j=0; j<t->col_count; j++ )
524 UINT ofs = t->colinfo[j].offset/2;
525 UINT n = bytes_per_column( &t->colinfo[j] );
527 switch( n )
529 case 2:
530 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
531 break;
532 case 4:
533 t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ];
534 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1];
535 break;
536 default:
537 ERR("oops - unknown column width %d\n", n);
538 goto err;
543 msi_free( rawdata );
544 return ERROR_SUCCESS;
545 err:
546 msi_free( rawdata );
547 return ERROR_FUNCTION_FAILED;
550 void free_cached_tables( MSIDATABASE *db )
552 while( !list_empty( &db->tables ) )
554 MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
556 list_remove( &t->entry );
557 free_table( t );
561 static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
563 MSITABLE *t;
565 LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
566 if( !lstrcmpW( name, t->name ) )
567 return t;
569 return NULL;
572 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
574 UINT r, column_count = 0;
575 MSICOLUMNINFO *columns;
577 /* get the number of columns in this table */
578 column_count = 0;
579 r = get_tablecolumns( db, name, NULL, &column_count );
580 if( r != ERROR_SUCCESS )
581 return r;
583 /* if there's no columns, there's no table */
584 if( column_count == 0 )
585 return ERROR_INVALID_PARAMETER;
587 TRACE("Table %s found\n", debugstr_w(name) );
589 columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO) );
590 if( !columns )
591 return ERROR_FUNCTION_FAILED;
593 r = get_tablecolumns( db, name, columns, &column_count );
594 if( r != ERROR_SUCCESS )
596 msi_free( columns );
597 return ERROR_FUNCTION_FAILED;
600 *pcols = columns;
601 *pcount = column_count;
603 return r;
606 UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info,
607 BOOL persistent, MSITABLE **table_ret)
609 UINT r, nField;
610 MSIVIEW *tv = NULL;
611 MSIRECORD *rec = NULL;
612 column_info *col;
613 MSITABLE *table;
614 UINT i;
616 /* only add tables that don't exist already */
617 if( TABLE_Exists(db, name ) )
619 WARN("table %s exists\n", debugstr_w(name));
620 return ERROR_BAD_QUERY_SYNTAX;
623 table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
624 if( !table )
625 return ERROR_FUNCTION_FAILED;
627 table->row_count = 0;
628 table->data = NULL;
629 table->nonpersistent_row_count = 0;
630 table->nonpersistent_data = NULL;
631 table->colinfo = NULL;
632 table->col_count = 0;
633 table->persistent = persistent;
634 lstrcpyW( table->name, name );
636 for( col = col_info; col; col = col->next )
637 table->col_count++;
639 table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) );
640 if (!table->colinfo)
642 free_table( table );
643 return ERROR_FUNCTION_FAILED;
646 for( i = 0, col = col_info; col; i++, col = col->next )
648 table->colinfo[ i ].tablename = strdupW( col->table );
649 table->colinfo[ i ].number = i + 1;
650 table->colinfo[ i ].colname = strdupW( col->column );
651 table->colinfo[ i ].type = col->type;
652 table->colinfo[ i ].offset = 0;
653 table->colinfo[ i ].hash_table = NULL;
655 table_calc_column_offsets( table->colinfo, table->col_count);
657 r = TABLE_CreateView( db, szTables, &tv );
658 TRACE("CreateView returned %x\n", r);
659 if( r )
661 free_table( table );
662 return r;
665 r = tv->ops->execute( tv, 0 );
666 TRACE("tv execute returned %x\n", r);
667 if( r )
668 goto err;
670 rec = MSI_CreateRecord( 1 );
671 if( !rec )
672 goto err;
674 r = MSI_RecordSetStringW( rec, 1, name );
675 if( r )
676 goto err;
678 r = tv->ops->insert_row( tv, rec, !persistent );
679 TRACE("insert_row returned %x\n", r);
680 if( r )
681 goto err;
683 tv->ops->delete( tv );
684 tv = NULL;
686 msiobj_release( &rec->hdr );
687 rec = NULL;
689 if( persistent )
691 /* add each column to the _Columns table */
692 r = TABLE_CreateView( db, szColumns, &tv );
693 if( r )
694 return r;
696 r = tv->ops->execute( tv, 0 );
697 TRACE("tv execute returned %x\n", r);
698 if( r )
699 goto err;
701 rec = MSI_CreateRecord( 4 );
702 if( !rec )
703 goto err;
705 r = MSI_RecordSetStringW( rec, 1, name );
706 if( r )
707 goto err;
710 * need to set the table, column number, col name and type
711 * for each column we enter in the table
713 nField = 1;
714 for( col = col_info; col; col = col->next )
716 r = MSI_RecordSetInteger( rec, 2, nField );
717 if( r )
718 goto err;
720 r = MSI_RecordSetStringW( rec, 3, col->column );
721 if( r )
722 goto err;
724 r = MSI_RecordSetInteger( rec, 4, col->type );
725 if( r )
726 goto err;
728 r = tv->ops->insert_row( tv, rec, FALSE );
729 if( r )
730 goto err;
732 nField++;
734 if( !col )
735 r = ERROR_SUCCESS;
738 err:
739 if (rec)
740 msiobj_release( &rec->hdr );
741 /* FIXME: remove values from the string table on error */
742 if( tv )
743 tv->ops->delete( tv );
745 if (r == ERROR_SUCCESS)
747 list_add_head( &db->tables, &table->entry );
748 *table_ret = table;
750 else
751 free_table( table );
753 return r;
756 static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret )
758 MSITABLE *table;
759 UINT r;
761 /* first, see if the table is cached */
762 table = find_cached_table( db, name );
763 if( table )
765 *table_ret = table;
766 return ERROR_SUCCESS;
769 /* nonexistent tables should be interpreted as empty tables */
770 table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
771 if( !table )
772 return ERROR_FUNCTION_FAILED;
774 table->row_count = 0;
775 table->data = NULL;
776 table->nonpersistent_row_count = 0;
777 table->nonpersistent_data = NULL;
778 table->colinfo = NULL;
779 table->col_count = 0;
780 table->persistent = TRUE;
781 lstrcpyW( table->name, name );
783 /* these two tables are special - we know the column types already */
784 if( !lstrcmpW( name, szColumns ) )
786 table->colinfo = _Columns_cols;
787 table->col_count = sizeof(_Columns_cols)/sizeof(_Columns_cols[0]);
789 else if( !lstrcmpW( name, szTables ) )
791 table->colinfo = _Tables_cols;
792 table->col_count = sizeof(_Tables_cols)/sizeof(_Tables_cols[0]);
794 else
796 r = table_get_column_info( db, name, &table->colinfo, &table->col_count);
797 if (r != ERROR_SUCCESS)
799 free_table ( table );
800 return r;
804 r = read_table_from_storage( table, db->storage );
805 if( r != ERROR_SUCCESS )
807 free_table( table );
808 return r;
811 list_add_head( &db->tables, &table->entry );
812 *table_ret = table;
813 return ERROR_SUCCESS;
816 static UINT save_table( MSIDATABASE *db, MSITABLE *t )
818 USHORT *rawdata = NULL, *p;
819 UINT rawsize, r, i, j, row_size;
821 /* Nothing to do for non-persistent tables */
822 if( !t->persistent )
823 return ERROR_SUCCESS;
825 TRACE("Saving %s\n", debugstr_w( t->name ) );
827 row_size = msi_table_get_row_size( t->colinfo, t->col_count );
829 rawsize = t->row_count * row_size;
830 rawdata = msi_alloc_zero( rawsize );
831 if( !rawdata )
833 r = ERROR_NOT_ENOUGH_MEMORY;
834 goto err;
837 p = rawdata;
838 for( i=0; i<t->col_count; i++ )
840 for( j=0; j<t->row_count; j++ )
842 UINT offset = t->colinfo[i].offset;
844 *p++ = t->data[j][offset/2];
845 if( 4 == bytes_per_column( &t->colinfo[i] ) )
846 *p++ = t->data[j][offset/2+1];
850 TRACE("writing %d bytes\n", rawsize);
851 r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE );
853 err:
854 msi_free( rawdata );
856 return r;
859 static void table_calc_column_offsets( MSICOLUMNINFO *colinfo, DWORD count )
861 DWORD i;
863 for( i=0; colinfo && (i<count); i++ )
865 assert( (i+1) == colinfo[ i ].number );
866 if (i)
867 colinfo[i].offset = colinfo[ i - 1 ].offset
868 + bytes_per_column( &colinfo[ i - 1 ] );
869 else
870 colinfo[i].offset = 0;
871 TRACE("column %d is [%s] with type %08x ofs %d\n",
872 colinfo[i].number, debugstr_w(colinfo[i].colname),
873 colinfo[i].type, colinfo[i].offset);
877 static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
879 const MSICOLUMNINFO *p;
880 DWORD i, n;
882 TRACE("%s\n", debugstr_w(name));
884 if (!lstrcmpW( name, szTables ))
886 p = _Tables_cols;
887 n = 1;
889 else if (!lstrcmpW( name, szColumns ))
891 p = _Columns_cols;
892 n = 4;
894 else
895 return ERROR_FUNCTION_FAILED;
897 /* duplicate the string data so we can free it in msi_free_colinfo */
898 for (i=0; i<n; i++)
900 if (colinfo && (i < *sz) )
902 memcpy( &colinfo[i], &p[i], sizeof(MSICOLUMNINFO) );
903 colinfo[i].tablename = strdupW( p[i].tablename );
904 colinfo[i].colname = strdupW( p[i].colname );
906 if( colinfo && (i >= *sz) )
907 break;
909 table_calc_column_offsets( colinfo, n );
910 *sz = n;
911 return ERROR_SUCCESS;
914 static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
916 UINT i;
918 for( i=0; i<count; i++ )
920 msi_free( (LPWSTR) colinfo[i].tablename );
921 msi_free( (LPWSTR) colinfo[i].colname );
922 msi_free( colinfo[i].hash_table );
926 static LPWSTR msi_makestring( MSIDATABASE *db, UINT stringid)
928 return strdupW(msi_string_lookup_id( db->strings, stringid ));
931 static UINT get_tablecolumns( MSIDATABASE *db,
932 LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
934 UINT r, i, n=0, table_id, count, maxcount = *sz;
935 MSITABLE *table = NULL;
937 TRACE("%s\n", debugstr_w(szTableName));
939 /* first check if there is a default table with that name */
940 r = get_defaulttablecolumns( szTableName, colinfo, sz );
941 if( ( r == ERROR_SUCCESS ) && *sz )
942 return r;
944 r = get_table( db, szColumns, &table );
945 if( r != ERROR_SUCCESS )
947 ERR("couldn't load _Columns table\n");
948 return ERROR_FUNCTION_FAILED;
951 /* convert table and column names to IDs from the string table */
952 r = msi_string2idW( db->strings, szTableName, &table_id );
953 if( r != ERROR_SUCCESS )
955 WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
956 return r;
959 TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
961 /* Note: _Columns table doesn't have non-persistent data */
963 /* if maxcount is non-zero, assume it's exactly right for this table */
964 memset( colinfo, 0, maxcount*sizeof(*colinfo) );
965 count = table->row_count;
966 for( i=0; i<count; i++ )
968 if( table->data[ i ][ 0 ] != table_id )
969 continue;
970 if( colinfo )
972 UINT id = table->data[ i ] [ 2 ];
973 UINT col = table->data[ i ][ 1 ] - (1<<15);
975 /* check the column number is in range */
976 if (col<1 || col>maxcount)
978 ERR("column %d out of range\n", col);
979 continue;
982 /* check if this column was already set */
983 if (colinfo[ col - 1 ].number)
985 ERR("duplicate column %d\n", col);
986 continue;
989 colinfo[ col - 1 ].tablename = msi_makestring( db, table_id );
990 colinfo[ col - 1 ].number = col;
991 colinfo[ col - 1 ].colname = msi_makestring( db, id );
992 colinfo[ col - 1 ].type = table->data[ i ] [ 3 ] - (1<<15);
993 colinfo[ col - 1 ].offset = 0;
994 colinfo[ col - 1 ].hash_table = NULL;
996 n++;
999 TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
1001 if (colinfo && n != maxcount)
1003 ERR("missing column in table %s\n", debugstr_w(szTableName));
1004 msi_free_colinfo(colinfo, maxcount );
1005 return ERROR_FUNCTION_FAILED;
1008 table_calc_column_offsets( colinfo, n );
1009 *sz = n;
1011 return ERROR_SUCCESS;
1014 /* try to find the table name in the _Tables table */
1015 BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name )
1017 UINT r, table_id = 0, i, count;
1018 MSITABLE *table = NULL;
1020 if( !lstrcmpW( name, szTables ) )
1021 return TRUE;
1022 if( !lstrcmpW( name, szColumns ) )
1023 return TRUE;
1025 r = msi_string2idW( db->strings, name, &table_id );
1026 if( r != ERROR_SUCCESS )
1028 TRACE("Couldn't find id for %s\n", debugstr_w(name));
1029 return FALSE;
1032 r = get_table( db, szTables, &table );
1033 if( r != ERROR_SUCCESS )
1035 ERR("table %s not available\n", debugstr_w(szTables));
1036 return FALSE;
1039 count = table->row_count;
1040 for( i=0; i<count; i++ )
1041 if( table->data[ i ][ 0 ] == table_id )
1042 break;
1044 if (i!=count)
1045 return TRUE;
1047 count = table->nonpersistent_row_count;
1048 for( i=0; i<count; i++ )
1049 if( table->nonpersistent_data[ i ][ 0 ] == table_id )
1050 break;
1052 if (i!=count)
1053 return TRUE;
1055 TRACE("Searched %d tables, but %d was not found\n", count, table_id );
1057 return FALSE;
1060 /* below is the query interface to a table */
1062 typedef struct tagMSITABLEVIEW
1064 MSIVIEW view;
1065 MSIDATABASE *db;
1066 MSITABLE *table;
1067 MSICOLUMNINFO *columns;
1068 UINT num_cols;
1069 UINT row_size;
1070 WCHAR name[1];
1071 } MSITABLEVIEW;
1073 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1075 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1076 UINT offset, n;
1077 USHORT **data;
1079 if( !tv->table )
1080 return ERROR_INVALID_PARAMETER;
1082 if( (col==0) || (col>tv->num_cols) )
1083 return ERROR_INVALID_PARAMETER;
1085 /* how many rows are there ? */
1086 if( row >= tv->table->row_count + tv->table->nonpersistent_row_count )
1087 return ERROR_NO_MORE_ITEMS;
1089 if( tv->columns[col-1].offset >= tv->row_size )
1091 ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1092 ERR("%p %p\n", tv, tv->columns );
1093 return ERROR_FUNCTION_FAILED;
1096 if (row >= tv->table->row_count)
1098 row -= tv->table->row_count;
1099 data = tv->table->nonpersistent_data;
1101 else
1102 data = tv->table->data;
1103 n = bytes_per_column( &tv->columns[col-1] );
1104 switch( n )
1106 case 4:
1107 offset = tv->columns[col-1].offset/2;
1108 *val = data[row][offset] +
1109 (data[row][offset + 1] << 16);
1110 break;
1111 case 2:
1112 offset = tv->columns[col-1].offset/2;
1113 *val = data[row][offset];
1114 break;
1115 default:
1116 ERR("oops! what is %d bytes per column?\n", n );
1117 return ERROR_FUNCTION_FAILED;
1120 /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1122 return ERROR_SUCCESS;
1126 * We need a special case for streams, as we need to reference column with
1127 * the name of the stream in the same table, and the table name
1128 * which may not be available at higher levels of the query
1130 static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1132 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1133 UINT ival = 0, refcol = 0, r;
1134 LPCWSTR sval;
1135 LPWSTR full_name;
1136 DWORD len;
1137 static const WCHAR szDot[] = { '.', 0 };
1138 WCHAR number[0x20];
1140 if( !view->ops->fetch_int )
1141 return ERROR_INVALID_PARAMETER;
1144 * The column marked with the type stream data seems to have a single number
1145 * which references the column containing the name of the stream data
1147 * Fetch the column to reference first.
1149 r = view->ops->fetch_int( view, row, col, &ival );
1150 if( r != ERROR_SUCCESS )
1151 return r;
1153 /* check the column value is in range */
1154 if (ival < 0 || ival > tv->num_cols || ival == col)
1156 ERR("bad column ref (%u) for stream\n", ival);
1157 return ERROR_FUNCTION_FAILED;
1160 if ( tv->columns[ival - 1].type & MSITYPE_STRING )
1162 /* now get the column with the name of the stream */
1163 r = view->ops->fetch_int( view, row, ival, &refcol );
1164 if ( r != ERROR_SUCCESS )
1165 return r;
1167 /* lookup the string value from the string table */
1168 sval = msi_string_lookup_id( tv->db->strings, refcol );
1169 if ( !sval )
1170 return ERROR_INVALID_PARAMETER;
1172 else
1174 static const WCHAR fmt[] = { '%','d',0 };
1175 sprintfW( number, fmt, ival );
1176 sval = number;
1179 len = lstrlenW( tv->name ) + 2 + lstrlenW( sval );
1180 full_name = msi_alloc( len*sizeof(WCHAR) );
1181 lstrcpyW( full_name, tv->name );
1182 lstrcatW( full_name, szDot );
1183 lstrcatW( full_name, sval );
1185 r = db_get_raw_stream( tv->db, full_name, stm );
1186 if( r )
1187 ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
1188 msi_free( full_name );
1190 return r;
1193 static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
1195 UINT offset, n;
1196 USHORT **data;
1198 if( !tv->table )
1199 return ERROR_INVALID_PARAMETER;
1201 if( (col==0) || (col>tv->num_cols) )
1202 return ERROR_INVALID_PARAMETER;
1204 if( row >= tv->table->row_count + tv->table->nonpersistent_row_count )
1205 return ERROR_INVALID_PARAMETER;
1207 if( tv->columns[col-1].offset >= tv->row_size )
1209 ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1210 ERR("%p %p\n", tv, tv->columns );
1211 return ERROR_FUNCTION_FAILED;
1214 msi_free( tv->columns[col-1].hash_table );
1215 tv->columns[col-1].hash_table = NULL;
1217 if (row >= tv->table->row_count)
1219 row -= tv->table->row_count;
1220 data = tv->table->nonpersistent_data;
1222 else
1223 data = tv->table->data;
1224 n = bytes_per_column( &tv->columns[col-1] );
1225 switch( n )
1227 case 4:
1228 offset = tv->columns[col-1].offset/2;
1229 data[row][offset] = val & 0xffff;
1230 data[row][offset + 1] = (val>>16)&0xffff;
1231 break;
1232 case 2:
1233 offset = tv->columns[col-1].offset/2;
1234 data[row][offset] = val;
1235 break;
1236 default:
1237 ERR("oops! what is %d bytes per column?\n", n );
1238 return ERROR_FUNCTION_FAILED;
1240 return ERROR_SUCCESS;
1243 static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
1245 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1246 UINT i, val, r = ERROR_SUCCESS;
1248 if ( !tv->table )
1249 return ERROR_INVALID_PARAMETER;
1251 /* test if any of the mask bits are invalid */
1252 if ( mask >= (1<<tv->num_cols) )
1253 return ERROR_INVALID_PARAMETER;
1255 for ( i = 0; i < tv->num_cols; i++ )
1257 BOOL persistent;
1259 /* only update the fields specified in the mask */
1260 if ( !(mask&(1<<i)) )
1261 continue;
1263 /* if row >= tv->table->row_count then it is a non-persistent row */
1264 persistent = tv->table->persistent && (row < tv->table->row_count);
1265 /* FIXME: should we allow updating keys? */
1267 val = 0;
1268 if ( !MSI_RecordIsNull( rec, i + 1 ) )
1270 if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
1272 val = 1; /* refers to the first key column */
1274 else if ( tv->columns[i].type & MSITYPE_STRING )
1276 LPCWSTR sval = MSI_RecordGetString( rec, i + 1 );
1277 val = msi_addstringW( tv->db->strings, 0, sval, -1, 1,
1278 persistent ? StringPersistent : StringNonPersistent );
1280 else if ( 2 == bytes_per_column( &tv->columns[ i ] ) )
1282 val = 0x8000 + MSI_RecordGetInteger( rec, i + 1 );
1283 if ( val & 0xffff0000 )
1285 ERR("field %u value %d out of range\n", i+1, val - 0x8000 );
1286 return ERROR_FUNCTION_FAILED;
1289 else
1291 INT ival = MSI_RecordGetInteger( rec, i + 1 );
1292 val = ival ^ 0x80000000;
1296 r = TABLE_set_int( tv, row, i+1, val );
1297 if ( r != ERROR_SUCCESS )
1298 break;
1300 return r;
1303 static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary )
1305 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1306 USHORT **p, *row;
1307 UINT sz;
1308 USHORT ***data_ptr;
1309 UINT *row_count;
1311 TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE");
1313 if( !tv->table )
1314 return ERROR_INVALID_PARAMETER;
1316 row = msi_alloc_zero( tv->row_size );
1317 if( !row )
1318 return ERROR_NOT_ENOUGH_MEMORY;
1320 if( temporary )
1322 row_count = &tv->table->nonpersistent_row_count;
1323 data_ptr = &tv->table->nonpersistent_data;
1324 *num = tv->table->row_count + tv->table->nonpersistent_row_count;
1326 else
1328 row_count = &tv->table->row_count;
1329 data_ptr = &tv->table->data;
1330 *num = tv->table->row_count;
1333 sz = (*row_count + 1) * sizeof (UINT*);
1334 if( *data_ptr )
1335 p = msi_realloc( *data_ptr, sz );
1336 else
1337 p = msi_alloc( sz );
1338 if( !p )
1340 msi_free( row );
1341 return ERROR_NOT_ENOUGH_MEMORY;
1344 *data_ptr = p;
1345 (*data_ptr)[*row_count] = row;
1346 (*row_count)++;
1348 return ERROR_SUCCESS;
1351 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1353 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1355 TRACE("%p %p\n", tv, record);
1357 TRACE("There are %d columns\n", tv->num_cols );
1359 return ERROR_SUCCESS;
1362 static UINT TABLE_close( struct tagMSIVIEW *view )
1364 TRACE("%p\n", view );
1366 return ERROR_SUCCESS;
1369 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1371 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1373 TRACE("%p %p %p\n", view, rows, cols );
1375 if( cols )
1376 *cols = tv->num_cols;
1377 if( rows )
1379 if( !tv->table )
1380 return ERROR_INVALID_PARAMETER;
1381 *rows = tv->table->row_count + tv->table->nonpersistent_row_count;
1384 return ERROR_SUCCESS;
1387 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1388 UINT n, LPWSTR *name, UINT *type )
1390 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1392 TRACE("%p %d %p %p\n", tv, n, name, type );
1394 if( ( n == 0 ) || ( n > tv->num_cols ) )
1395 return ERROR_INVALID_PARAMETER;
1397 if( name )
1399 *name = strdupW( tv->columns[n-1].colname );
1400 if( !*name )
1401 return ERROR_FUNCTION_FAILED;
1403 if( type )
1404 *type = tv->columns[n-1].type;
1406 return ERROR_SUCCESS;
1409 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row );
1411 static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec )
1413 UINT r, row, i;
1415 /* check there's no null values where they're not allowed */
1416 for( i = 0; i < tv->num_cols; i++ )
1418 if ( tv->columns[i].type & MSITYPE_NULLABLE )
1419 continue;
1421 if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
1422 TRACE("skipping binary column\n");
1423 else if ( tv->columns[i].type & MSITYPE_STRING )
1425 LPCWSTR str;
1427 str = MSI_RecordGetString( rec, i+1 );
1428 if (str == NULL || str[0] == 0)
1429 return ERROR_INVALID_DATA;
1431 else
1433 UINT n;
1435 n = MSI_RecordGetInteger( rec, i+1 );
1436 if (n == MSI_NULL_INTEGER)
1437 return ERROR_INVALID_DATA;
1441 /* check there's no duplicate keys */
1442 r = msi_table_find_row( tv, rec, &row );
1443 if (r == ERROR_SUCCESS)
1444 return ERROR_INVALID_DATA;
1446 return ERROR_SUCCESS;
1449 static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, BOOL temporary )
1451 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1452 UINT r, row = -1;
1454 TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" );
1456 /* check that the key is unique - can we find a matching row? */
1457 r = table_validate_new( tv, rec );
1458 if( r != ERROR_SUCCESS )
1459 return ERROR_FUNCTION_FAILED;
1461 r = table_create_new_row( view, &row, temporary );
1462 TRACE("insert_row returned %08x\n", r);
1463 if( r != ERROR_SUCCESS )
1464 return r;
1466 return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
1469 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1470 MSIRECORD *rec)
1472 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1473 UINT r;
1475 TRACE("%p %d %p\n", view, eModifyMode, rec );
1477 switch (eModifyMode)
1479 case MSIMODIFY_VALIDATE_NEW:
1480 r = table_validate_new( tv, rec );
1481 break;
1483 case MSIMODIFY_INSERT_TEMPORARY:
1484 r = table_validate_new( tv, rec );
1485 if (r != ERROR_SUCCESS)
1486 break;
1487 r = TABLE_insert_row( view, rec, TRUE );
1488 break;
1490 case MSIMODIFY_REFRESH:
1491 case MSIMODIFY_INSERT:
1492 case MSIMODIFY_UPDATE:
1493 case MSIMODIFY_ASSIGN:
1494 case MSIMODIFY_REPLACE:
1495 case MSIMODIFY_MERGE:
1496 case MSIMODIFY_DELETE:
1497 case MSIMODIFY_VALIDATE:
1498 case MSIMODIFY_VALIDATE_FIELD:
1499 case MSIMODIFY_VALIDATE_DELETE:
1500 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1501 r = ERROR_CALL_NOT_IMPLEMENTED;
1502 break;
1504 default:
1505 r = ERROR_INVALID_DATA;
1508 return r;
1511 static UINT TABLE_delete( struct tagMSIVIEW *view )
1513 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1515 TRACE("%p\n", view );
1517 tv->table = NULL;
1519 tv->columns = NULL;
1521 msi_free( tv );
1523 return ERROR_SUCCESS;
1526 static UINT TABLE_find_matching_rows( struct tagMSIVIEW *view, UINT col,
1527 UINT val, UINT *row, MSIITERHANDLE *handle )
1529 MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1530 const MSICOLUMNHASHENTRY *entry;
1532 TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
1534 if( !tv->table )
1535 return ERROR_INVALID_PARAMETER;
1537 if( (col==0) || (col > tv->num_cols) )
1538 return ERROR_INVALID_PARAMETER;
1540 if( !tv->columns[col-1].hash_table )
1542 UINT i;
1543 UINT num_rows = tv->table->row_count + tv->table->nonpersistent_row_count;
1544 MSICOLUMNHASHENTRY **hash_table;
1545 MSICOLUMNHASHENTRY *new_entry;
1547 if( tv->columns[col-1].offset >= tv->row_size )
1549 ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1550 ERR("%p %p\n", tv, tv->columns );
1551 return ERROR_FUNCTION_FAILED;
1554 /* allocate contiguous memory for the table and its entries so we
1555 * don't have to do an expensive cleanup */
1556 hash_table = msi_alloc(MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*) +
1557 num_rows * sizeof(MSICOLUMNHASHENTRY));
1558 if (!hash_table)
1559 return ERROR_OUTOFMEMORY;
1561 memset(hash_table, 0, MSITABLE_HASH_TABLE_SIZE * sizeof(MSICOLUMNHASHENTRY*));
1562 tv->columns[col-1].hash_table = hash_table;
1564 new_entry = (MSICOLUMNHASHENTRY *)(hash_table + MSITABLE_HASH_TABLE_SIZE);
1566 for (i = 0; i < num_rows; i++, new_entry++)
1568 UINT row_value;
1570 if (view->ops->fetch_int( view, i, col, &row_value ) != ERROR_SUCCESS)
1571 continue;
1573 new_entry->next = NULL;
1574 new_entry->value = row_value;
1575 new_entry->row = i;
1576 if (hash_table[row_value % MSITABLE_HASH_TABLE_SIZE])
1578 MSICOLUMNHASHENTRY *prev_entry = hash_table[row_value % MSITABLE_HASH_TABLE_SIZE];
1579 while (prev_entry->next)
1580 prev_entry = prev_entry->next;
1581 prev_entry->next = new_entry;
1583 else
1584 hash_table[row_value % MSITABLE_HASH_TABLE_SIZE] = new_entry;
1588 if( !*handle )
1589 entry = tv->columns[col-1].hash_table[val % MSITABLE_HASH_TABLE_SIZE];
1590 else
1591 entry = (*handle)->next;
1593 while (entry && entry->value != val)
1594 entry = entry->next;
1596 *handle = entry;
1597 if (!entry)
1598 return ERROR_NO_MORE_ITEMS;
1600 *row = entry->row;
1601 return ERROR_SUCCESS;
1605 static const MSIVIEWOPS table_ops =
1607 TABLE_fetch_int,
1608 TABLE_fetch_stream,
1609 TABLE_set_row,
1610 TABLE_insert_row,
1611 TABLE_execute,
1612 TABLE_close,
1613 TABLE_get_dimensions,
1614 TABLE_get_column_info,
1615 TABLE_modify,
1616 TABLE_delete,
1617 TABLE_find_matching_rows
1620 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1622 MSITABLEVIEW *tv ;
1623 UINT r, sz;
1625 static const WCHAR Streams[] = {'_','S','t','r','e','a','m','s',0};
1627 TRACE("%p %s %p\n", db, debugstr_w(name), view );
1629 if ( !lstrcmpW( name, Streams ) )
1630 return STREAMS_CreateView( db, view );
1632 sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1633 tv = msi_alloc_zero( sz );
1634 if( !tv )
1635 return ERROR_FUNCTION_FAILED;
1637 r = get_table( db, name, &tv->table );
1638 if( r != ERROR_SUCCESS )
1640 msi_free( tv );
1641 WARN("table not found\n");
1642 return r;
1645 TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count);
1647 /* fill the structure */
1648 tv->view.ops = &table_ops;
1649 tv->db = db;
1650 tv->columns = tv->table->colinfo;
1651 tv->num_cols = tv->table->col_count;
1652 tv->row_size = msi_table_get_row_size( tv->table->colinfo, tv->table->col_count );
1654 TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
1656 *view = (MSIVIEW*) tv;
1657 lstrcpyW( tv->name, name );
1659 return ERROR_SUCCESS;
1662 UINT MSI_CommitTables( MSIDATABASE *db )
1664 UINT r;
1665 MSITABLE *table = NULL;
1667 TRACE("%p\n",db);
1669 r = msi_save_string_table( db->strings, db->storage );
1670 if( r != ERROR_SUCCESS )
1672 WARN("failed to save string table r=%08x\n",r);
1673 return r;
1676 LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
1678 r = save_table( db, table );
1679 if( r != ERROR_SUCCESS )
1681 WARN("failed to save table %s (r=%08x)\n",
1682 debugstr_w(table->name), r);
1683 return r;
1687 /* force everything to reload next time */
1688 free_cached_tables( db );
1690 return ERROR_SUCCESS;
1693 MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
1695 MSITABLE *t;
1696 UINT r;
1698 TRACE("%p %s\n", db, debugstr_w(table));
1700 if (!table)
1701 return MSICONDITION_ERROR;
1703 r = get_table( db, table, &t );
1704 if (r != ERROR_SUCCESS)
1705 return MSICONDITION_NONE;
1707 if (t->persistent)
1708 return MSICONDITION_TRUE;
1709 else
1710 return MSICONDITION_FALSE;
1713 static MSIRECORD *msi_get_transform_record( MSITABLEVIEW *tv, string_table *st, USHORT *rawdata )
1715 UINT i, val, ofs = 0;
1716 USHORT mask = *rawdata++;
1717 MSICOLUMNINFO *columns = tv->columns;
1718 MSIRECORD *rec;
1720 rec = MSI_CreateRecord( tv->num_cols );
1721 if( !rec )
1722 return rec;
1724 TRACE("row ->\n");
1725 for( i=0; i<tv->num_cols; i++ )
1727 UINT n = bytes_per_column( &columns[i] );
1729 if ( (mask&1) && (i>=(mask>>8)) )
1730 break;
1731 /* all keys must be present */
1732 if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
1733 continue;
1735 switch( n )
1737 case 2:
1738 val = rawdata[ofs];
1739 if( (columns[i].type & MSITYPE_STRING) &&
1740 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
1742 LPCWSTR sval = msi_string_lookup_id( st, val );
1743 MSI_RecordSetStringW( rec, i+1, sval );
1744 TRACE(" field %d [%s]\n", i+1, debugstr_w(sval));
1746 else
1748 if (val)
1749 MSI_RecordSetInteger( rec, i+1, val^0x8000 );
1750 TRACE(" field %d [0x%04x]\n", i+1, val );
1752 break;
1753 case 4:
1754 val = (rawdata[ofs] + (rawdata[ofs + 1]<<16));
1755 if (val)
1756 MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
1757 TRACE(" field %d [0x%08x]\n", i+1, val );
1758 break;
1759 default:
1760 ERR("oops - unknown column width %d\n", n);
1761 break;
1763 ofs += n/2;
1765 return rec;
1768 static void dump_record( MSIRECORD *rec )
1770 UINT i, n;
1772 n = MSI_RecordGetFieldCount( rec );
1773 for( i=1; i<=n; i++ )
1775 LPCWSTR sval = MSI_RecordGetString( rec, i );
1777 if( MSI_RecordIsNull( rec, i ) )
1778 TRACE("row -> []\n");
1779 else if( (sval = MSI_RecordGetString( rec, i )) )
1780 TRACE("row -> [%s]\n", debugstr_w(sval));
1781 else
1782 TRACE("row -> [0x%08x]\n", MSI_RecordGetInteger( rec, i ) );
1786 static void dump_table( string_table *st, USHORT *rawdata, UINT rawsize )
1788 LPCWSTR sval;
1789 UINT i;
1791 for( i=0; i<(rawsize/2); i++ )
1793 sval = msi_string_lookup_id( st, rawdata[i] );
1794 MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) );
1798 static UINT* msi_record_to_row( MSITABLEVIEW *tv, MSIRECORD *rec )
1800 LPCWSTR str;
1801 UINT i, r, *data;
1803 data = msi_alloc( tv->num_cols *sizeof (UINT) );
1804 for( i=0; i<tv->num_cols; i++ )
1806 data[i] = 0;
1808 if ( ~tv->columns[i].type & MSITYPE_KEY )
1809 continue;
1811 /* turn the transform column value into a row value */
1812 if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
1813 ! MSITYPE_IS_BINARY(tv->columns[i].type) )
1815 str = MSI_RecordGetString( rec, i+1 );
1816 r = msi_string2idW( tv->db->strings, str, &data[i] );
1818 /* if there's no matching string in the string table,
1819 these keys can't match any record, so fail now. */
1820 if( ERROR_SUCCESS != r )
1822 msi_free( data );
1823 return NULL;
1826 else
1828 data[i] = MSI_RecordGetInteger( rec, i+1 );
1829 if ((tv->columns[i].type&0xff) == 2)
1830 data[i] += 0x8000;
1831 else
1832 data[i] += 0x80000000;
1835 return data;
1838 static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, UINT *data )
1840 UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
1842 for( i=0; i<tv->num_cols; i++ )
1844 if ( ~tv->columns[i].type & MSITYPE_KEY )
1845 continue;
1847 /* turn the transform column value into a row value */
1848 r = TABLE_fetch_int( &tv->view, row, i+1, &x );
1849 if ( r != ERROR_SUCCESS )
1851 ERR("TABLE_fetch_int shouldn't fail here\n");
1852 break;
1855 /* if this key matches, move to the next column */
1856 if ( x != data[i] )
1858 ret = ERROR_FUNCTION_FAILED;
1859 break;
1862 ret = ERROR_SUCCESS;
1865 return ret;
1868 static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row )
1870 UINT i, r = ERROR_FUNCTION_FAILED, *data;
1872 data = msi_record_to_row( tv, rec );
1873 if( !data )
1874 return r;
1875 for( i = 0; i < tv->table->row_count + tv->table->nonpersistent_row_count; i++ )
1877 r = msi_row_matches( tv, i, data );
1878 if( r == ERROR_SUCCESS )
1880 *row = i;
1881 break;
1884 msi_free( data );
1885 return r;
1888 static UINT msi_delete_row( MSITABLEVIEW *tv, UINT row )
1890 UINT i;
1892 for( i=1; i<=tv->num_cols; i++ )
1893 TABLE_set_int( tv, row, i, 0 );
1894 return ERROR_SUCCESS;
1897 static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
1898 string_table *st, LPCWSTR name )
1900 UINT rawsize = 0;
1901 USHORT *rawdata = NULL;
1902 MSITABLEVIEW *tv = NULL;
1903 UINT r, n, sz, i, mask;
1904 MSIRECORD *rec = NULL;
1905 UINT colcol = 0;
1906 WCHAR coltable[32];
1908 coltable[0] = 0;
1909 TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
1911 /* read the transform data */
1912 read_stream_data( stg, name, &rawdata, &rawsize );
1913 if ( !rawdata )
1915 TRACE("table %s empty\n", debugstr_w(name) );
1916 return ERROR_INVALID_TABLE;
1919 /* create a table view */
1920 r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
1921 if( r != ERROR_SUCCESS )
1922 goto err;
1924 r = tv->view.ops->execute( &tv->view, NULL );
1925 if( r != ERROR_SUCCESS )
1926 goto err;
1928 TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
1929 debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
1931 /* interpret the data */
1932 r = ERROR_SUCCESS;
1933 for( n=0; n < (rawsize/2); )
1935 mask = rawdata[n];
1937 if (mask&1)
1940 * if the low bit is set, columns are continuous and
1941 * the number of columns is specified in the high byte
1943 sz = 2 + tv->row_size;
1945 else
1948 * If the low bit is not set, rowdata[n] is a bitmask.
1949 * Excepting for key fields, which are always present,
1950 * each bit indicates that a field is present in the transform record.
1952 * rawdata[n] == 0 is a special case ... only the keys will be present
1953 * and it means that this row should be deleted.
1955 sz = 2;
1956 for( i=0; i<tv->num_cols; i++ )
1958 if( (tv->columns[i].type & MSITYPE_KEY) || ((1<<i)&mask))
1959 sz += bytes_per_column( &tv->columns[i] );
1963 /* check we didn't run of the end of the table */
1964 if ( (n+sz) > rawsize )
1966 ERR("borked.\n");
1967 dump_table( st, rawdata, rawsize );
1968 break;
1971 rec = msi_get_transform_record( tv, st, &rawdata[n] );
1972 if (rec)
1974 if ( mask & 1 )
1976 TRACE("inserting record\n");
1979 * Native msi seems writes nul into the
1980 * Number (2nd) column of the _Columns table.
1981 * Not sure that it's deliberate...
1983 if (!lstrcmpW(name, szColumns))
1985 WCHAR table[32];
1986 DWORD sz = 32;
1988 MSI_RecordGetStringW( rec, 1, table, &sz );
1990 /* reset the column number on a new table */
1991 if ( lstrcmpW(coltable, table) )
1993 colcol = 0;
1994 lstrcpyW( coltable, table );
1997 /* fix nul column numbers */
1998 MSI_RecordSetInteger( rec, 2, ++colcol );
2001 r = TABLE_insert_row( &tv->view, rec, FALSE );
2002 if (r != ERROR_SUCCESS)
2003 ERR("insert row failed\n");
2005 else
2007 UINT row = 0;
2009 r = msi_table_find_row( tv, rec, &row );
2010 if (r != ERROR_SUCCESS)
2011 ERR("no matching row to transform\n");
2012 else if ( mask )
2014 TRACE("modifying row [%d]:\n", row);
2015 TABLE_set_row( &tv->view, row, rec, mask );
2017 else
2019 TRACE("deleting row [%d]:\n", row);
2020 msi_delete_row( tv, row );
2023 if( TRACE_ON(msidb) ) dump_record( rec );
2024 msiobj_release( &rec->hdr );
2027 n += sz/2;
2030 err:
2031 /* no need to free the table, it's associated with the database */
2032 msi_free( rawdata );
2033 if( tv )
2034 tv->view.ops->delete( &tv->view );
2036 return ERROR_SUCCESS;
2040 * msi_table_apply_transform
2042 * Enumerate the table transforms in a transform storage and apply each one.
2044 UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
2046 IEnumSTATSTG *stgenum = NULL;
2047 HRESULT r;
2048 STATSTG stat;
2049 ULONG count;
2050 WCHAR name[0x40];
2051 string_table *strings;
2052 UINT ret = ERROR_FUNCTION_FAILED;
2054 TRACE("%p %p\n", db, stg );
2056 strings = msi_load_string_table( stg );
2057 if( !strings )
2058 goto end;
2060 r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
2061 if( FAILED( r ) )
2062 goto end;
2065 * Apply _Tables and _Columns transforms first so that
2066 * the table metadata is correct, and empty tables exist.
2068 ret = msi_table_load_transform( db, stg, strings, szTables );
2069 if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2070 goto end;
2072 ret = msi_table_load_transform( db, stg, strings, szColumns );
2073 if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2074 goto end;
2076 ret = ERROR_SUCCESS;
2078 while( r == ERROR_SUCCESS )
2080 count = 0;
2081 r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
2082 if( FAILED( r ) || !count )
2083 break;
2085 decode_streamname( stat.pwcsName, name );
2086 if ( name[0] != 0x4840 )
2087 continue;
2089 TRACE("transform contains stream %s\n", debugstr_w(name));
2091 if ( !lstrcmpW( name+1, szStringPool ) ||
2092 !lstrcmpW( name+1, szStringData ) ||
2093 !lstrcmpW( name+1, szColumns ) ||
2094 !lstrcmpW( name+1, szTables ) )
2095 continue;
2097 ret = msi_table_load_transform( db, stg, strings, name+1 );
2100 if ( ret == ERROR_SUCCESS )
2101 append_storage_to_db( db, stg );
2103 end:
2104 if ( stgenum )
2105 IEnumSTATSTG_Release( stgenum );
2106 if ( strings )
2107 msi_destroy_stringtable( strings );
2109 return ret;
2112 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
2114 MSITRANSFORM *t;
2116 t = msi_alloc( sizeof *t );
2117 t->stg = stg;
2118 IStorage_AddRef( stg );
2119 list_add_tail( &db->transforms, &t->entry );
2122 void msi_free_transforms( MSIDATABASE *db )
2124 while( !list_empty( &db->transforms ) )
2126 MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
2127 MSITRANSFORM, entry );
2128 list_remove( &t->entry );
2129 IStorage_Release( t->stg );
2130 msi_free( t );