If using the default values, also set dwType to REG_SZ as our default
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002 Mike McCormack for CodeWeavers
5  *
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.
10  *
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.
15  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winerror.h"
26 #include "wine/debug.h"
27 #include "msi.h"
28 #include "msiquery.h"
29 #include "objbase.h"
30 #include "objidl.h"
31 #include "msipriv.h"
32 #include "winnls.h"
33
34 #include "query.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(msi);
37
38 typedef struct tagMSICOLUMNINFO
39 {
40     LPWSTR tablename;
41     UINT   number;
42     LPWSTR colname;
43     UINT   type;
44     UINT   offset;
45 } MSICOLUMNINFO;
46
47 struct tagMSITABLE
48 {
49     USHORT *data;
50     UINT size;
51     UINT ref_count;
52     /* MSICOLUMNINFO *columns; */
53     /* UINT num_cols; */
54     struct tagMSITABLE *next;
55     struct tagMSITABLE *prev;
56     WCHAR name[1];
57 } ;
58
59 #define MAX_STREAM_NAME 0x1f
60
61 static int utf2mime(int x)
62 {
63     if( (x>='0') && (x<='9') )
64         return x-'0';
65     if( (x>='A') && (x<='Z') )
66         return x-'A'+10;
67     if( (x>='a') && (x<='z') )
68         return x-'a'+10+26;
69     if( x=='.' )
70         return 10+26+26;
71     if( x=='_' )
72         return 10+26+26+1;
73     return -1;
74 }
75
76 static BOOL encode_streamname(BOOL bTable, LPCWSTR in, LPWSTR out)
77 {
78     DWORD count = MAX_STREAM_NAME;
79     DWORD ch, next;
80
81     if( bTable )
82     {
83          *out++ = 0x4840;
84          count --;
85     }
86     while( count -- ) 
87     {
88         ch = *in++;
89         if( !ch )
90         {
91             *out = ch;
92             return TRUE;
93         }
94         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
95         {
96             ch = utf2mime(ch) + 0x4800;
97             next = *in;
98             if( next && (next<0x80) )
99             {
100                 next = utf2mime(next);
101                 if( next >= 0  )
102                 {
103                      next += 0x3ffffc0;
104                      ch += (next<<6);
105                      in++;
106                 }
107             }
108         }
109         *out++ = ch;
110     }
111     return FALSE;
112 }
113
114 #if 0
115 static int mime2utf(int x)
116 {
117     if( x<10 )
118         return x + '0';
119     if( x<(10+26))
120         return x - 10 + 'A';
121     if( x<(10+26+26))
122         return x - 10 - 26 + 'a';
123     if( x == (10+26+26) )
124         return '.';
125     return '_';
126 }
127
128 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
129 {
130     WCHAR ch;
131     DWORD count = 0;
132
133     while ( (ch = *in++) )
134     {
135         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
136         {
137             if( ch >= 0x4800 )
138                 ch = mime2utf(ch-0x4800);
139             else
140             {
141                 ch -= 0x3800;
142                 *out++ = mime2utf(ch&0x3f);
143                 count++;
144                 ch = mime2utf((ch>>6)&0x3f);
145             }
146         }
147         *out++ = ch;
148         count++;
149     }
150     *out = 0;
151     return count;
152 }
153 #endif
154
155 static BOOL read_stream_data( IStorage *stg, LPWSTR stname,
156                               USHORT **pdata, UINT *psz )
157 {
158     HRESULT r;
159     UINT ret = ERROR_FUNCTION_FAILED;
160     VOID *data;
161     ULONG sz, count;
162     IStream *stm = NULL;
163     STATSTG stat;
164
165     r = IStorage_OpenStream(stg, stname, NULL, 
166             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
167     if( FAILED( r ) )
168     {
169         WARN("open stream failed r = %08lx - empty table?\n",r);
170         return ret;
171     }
172
173     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
174     if( FAILED( r ) )
175     {
176         ERR("open stream failed r = %08lx!\n",r);
177         goto end;
178     }
179
180     if( stat.cbSize.QuadPart >> 32 )
181     {
182         ERR("Too big!\n");
183         goto end;
184     }
185         
186     sz = stat.cbSize.QuadPart;
187     data = HeapAlloc( GetProcessHeap(), 0, sz );
188     if( !data )
189     {
190         ERR("couldn't allocate memory r=%08lx!\n",r);
191         ret = ERROR_NOT_ENOUGH_MEMORY;
192         goto end;
193     }
194         
195     r = IStream_Read(stm, data, sz, &count );
196     if( FAILED( r ) || ( count != sz ) )
197     {
198         HeapFree( GetProcessHeap(), 0, data );
199         ERR("read stream failed r = %08lx!\n",r);
200         goto end;
201     }
202
203     *pdata = data;
204     *psz = sz;
205     ret = ERROR_SUCCESS;
206
207 end:
208     IStream_Release( stm );
209
210     return ret;
211 }
212
213 UINT read_table_from_storage(IStorage *stg, LPCWSTR name, MSITABLE **ptable)
214 {
215     WCHAR buffer[0x20];
216     MSITABLE *t;
217
218     TRACE("%s -> %s\n",debugstr_w(name),debugstr_w(buffer));
219
220     /* non-existing tables should be interpretted as empty tables */
221     t = HeapAlloc( GetProcessHeap(), 0, 
222                    sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
223     if( !t )
224         return ERROR_NOT_ENOUGH_MEMORY;
225
226     t->size = 0;
227     t->data = NULL;
228     lstrcpyW( t->name, name );
229     t->ref_count = 1;
230     *ptable = t;
231
232     /* if we can't read the table, just assume that it's empty */
233     encode_streamname(TRUE, name, buffer);
234     read_stream_data( stg, buffer, &t->data, &t->size );
235
236     return ERROR_SUCCESS;
237 }
238
239 /* add this table to the list of cached tables in the database */
240 void add_table(MSIDATABASE *db, MSITABLE *table)
241 {
242     table->next = db->first_table;
243     table->prev = NULL;
244     if( db->first_table )
245         db->first_table->prev = table;
246     else
247         db->last_table = table;
248     db->first_table = table;
249 }
250  
251 /* remove from the list of cached tables */
252 void remove_table( MSIDATABASE *db, MSITABLE *table )
253 {
254     if( table->next )
255         table->next->prev = table->prev;
256     else
257         db->last_table = table->prev;
258     if( table->prev )
259         table->prev->next = table->next;
260     else
261         db->first_table = table->next;
262     table->next = NULL;
263     table->prev = NULL;
264 }
265
266 void release_table( MSIDATABASE *db, MSITABLE *table )
267 {
268     if( !table->ref_count )
269         ERR("Trying to destroy table with refcount 0\n");
270     table->ref_count --;
271     if( !table->ref_count )
272     {
273         remove_table( db, table );
274         HeapFree( GetProcessHeap(), 0, table->data );
275         HeapFree( GetProcessHeap(), 0, table );
276         TRACE("Destroyed table %s\n", debugstr_w(table->name));
277     }
278 }
279
280 void free_cached_tables( MSIDATABASE *db )
281 {
282     while( db->first_table )
283     {
284         MSITABLE *t = db->first_table;
285
286         if ( --t->ref_count )
287             ERR("table ref count not zero for %s\n", debugstr_w(t->name));
288         remove_table( db, t );
289         HeapFree( GetProcessHeap(), 0, t->data );
290         HeapFree( GetProcessHeap(), 0, t );
291     }
292 }
293
294 UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
295 {
296     MSITABLE *t;
297
298     for( t = db->first_table; t; t=t->next )
299     {
300         if( !lstrcmpW( name, t->name ) )
301         {
302             *ptable = t;
303             return ERROR_SUCCESS;
304         }
305     }
306
307     return ERROR_FUNCTION_FAILED;
308 }
309
310 UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
311 {
312     UINT r;
313
314     *ptable = NULL;
315
316     /* first, see if the table is cached */
317     r = find_cached_table( db, name, ptable );
318     if( r == ERROR_SUCCESS )
319     {
320         (*ptable)->ref_count++;
321         return r;
322     }
323
324     r = read_table_from_storage( db->storage, name, ptable );
325     if( r != ERROR_SUCCESS )
326         return r;
327
328     /* add the table to the list */
329     add_table( db, *ptable );
330     (*ptable)->ref_count++;
331
332     return ERROR_SUCCESS;
333 }
334
335 UINT dump_string_table(MSIDATABASE *db)
336 {
337     DWORD i, count, offset, len;
338     string_table *st = &db->strings;
339
340     MESSAGE("%d,%d bytes\n",st->pool.size,st->info.size);
341
342     count = st->pool.size/4;
343
344     offset = 0;
345     for(i=0; i<count; i++)
346     {
347         len = st->pool.data[i*2];
348         MESSAGE("[%2ld] = %s\n",i, debugstr_an(st->info.data+offset,len));
349         offset += len;
350     }
351
352     return ERROR_SUCCESS;
353 }
354
355 UINT load_string_table( MSIDATABASE *db, string_table *pst)
356 {
357     MSITABLE *pool = NULL, *info = NULL;
358     UINT r, ret = ERROR_FUNCTION_FAILED;
359     const WCHAR szStringData[] = { 
360         '_','S','t','r','i','n','g','D','a','t','a',0 };
361     const WCHAR szStringPool[] = { 
362         '_','S','t','r','i','n','g','P','o','o','l',0 };
363
364     r = get_table( db, szStringPool, &pool );
365     if( r != ERROR_SUCCESS)
366         goto end;
367     r = get_table( db, szStringData, &info );
368     if( r != ERROR_SUCCESS)
369         goto end;
370
371     pst->pool.size = pool->size;
372     pst->pool.data = pool->data;
373     pst->info.size = info->size;
374     pst->info.data = (CHAR *)info->data;
375
376     TRACE("Loaded %d,%d bytes\n",pst->pool.size,pst->info.size);
377
378     ret = ERROR_SUCCESS;
379
380 end:
381     if( info )
382         release_table( db, info );
383     if( pool )
384         release_table( db, pool );
385
386     return ret;
387 }
388
389 UINT msi_id2string( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz )
390 {
391     DWORD i, count, offset, len;
392
393     count = st->pool.size/4;
394     TRACE("Finding string %d of %ld\n", string_no, count);
395     if(string_no >= count)
396         return ERROR_FUNCTION_FAILED;
397
398     offset = 0;
399     for(i=0; i<string_no; i++)
400     {
401         len = st->pool.data[i*2];
402         offset += len;
403     }
404
405     len = st->pool.data[i*2];
406
407     if( !buffer )
408     {
409         *sz = len;
410         return ERROR_SUCCESS;
411     }
412
413     if( (offset+len) > st->info.size )
414         return ERROR_FUNCTION_FAILED;
415
416     len = MultiByteToWideChar(CP_ACP,0,&st->info.data[offset],len,buffer,*sz-1); 
417     buffer[len] = 0;
418     *sz = len+1;
419
420     return ERROR_SUCCESS;
421 }
422
423 static UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id )
424 {
425     DWORD i, count, offset, len, sz;
426     UINT r = ERROR_INVALID_PARAMETER;
427     LPSTR str;
428
429     count = st->pool.size/4;
430     TRACE("Finding string %s in %ld strings\n", debugstr_w(buffer), count);
431
432     sz = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
433     if( sz <= 0 )
434         return r;
435     str = HeapAlloc( GetProcessHeap(), 0, sz );
436     if( !str )
437         return ERROR_NOT_ENOUGH_MEMORY;
438     WideCharToMultiByte( CP_ACP, 0, buffer, -1, str, sz, NULL, NULL );
439
440     offset = 0;
441     sz--;  /* nul terminated */
442     for(i=0; i<count; i++)
443     {
444         len = st->pool.data[i*2];
445         if ( ( sz == len ) && !memcmp( str, st->info.data+offset, sz ) )
446         {
447             TRACE("%ld <- %s\n",i,debugstr_an(st->info.data+offset, len) );
448             *id = i;
449             r = ERROR_SUCCESS;
450             break;
451         }
452         offset += len;
453     }
454
455     if( str )
456         HeapFree( GetProcessHeap(), 0, str );
457
458     return r;
459 }
460
461 static LPWSTR strdupW( LPCWSTR str )
462 {
463     UINT len = lstrlenW( str ) + 1;
464     LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
465     if( ret )
466         lstrcpyW( ret, str );
467     return ret;
468 }
469
470 static inline UINT bytes_per_column( MSICOLUMNINFO *col )
471 {
472     if( col->type & MSITYPE_STRING )
473         return 2;
474     if( (col->type & 0xff) > 4 )
475         ERR("Invalid column size!\n");
476     return col->type & 0xff;
477 }
478
479 /* information for default tables */
480 const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
481 const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
482 const WCHAR szName[]    = { 'N','a','m','e',0 };
483 const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
484 const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
485 const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
486 const WCHAR szType[]    = { 'T','y','p','e',0 };
487
488 struct standard_table {
489     LPCWSTR tablename;
490     LPCWSTR columnname;
491     UINT number;
492     UINT type;
493 } MSI_standard_tables[] =
494 {
495   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
496   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
497   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
498   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
499   { szColumns, szType,   4, MSITYPE_VALID | 2},
500 };
501
502 #define STANDARD_TABLE_COUNT \
503      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
504
505 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
506 {
507     DWORD i, n=0;
508
509     for(i=0; i<STANDARD_TABLE_COUNT; i++)
510     {
511         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
512             continue;
513         if(colinfo && (n < *sz) )
514         {
515             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
516             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
517             colinfo[n].number = MSI_standard_tables[i].number;
518             colinfo[n].type = MSI_standard_tables[i].type;
519             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
520                     debugstr_w(colinfo[n].colname)); */
521             if( n )
522                 colinfo[n].offset = colinfo[n-1].offset
523                                   + bytes_per_column( &colinfo[n-1] );
524             else
525                 colinfo[n].offset = 0;
526         }
527         n++;
528         if( colinfo && (n >= *sz) )
529             break;
530     }
531     *sz = n;
532     return ERROR_SUCCESS;
533 }
534
535 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
536 {
537     UINT sz=0, r;
538     LPWSTR str;
539
540     r = msi_id2string( &db->strings, stringid, NULL, &sz );
541     if( r != ERROR_SUCCESS )
542         return NULL;
543     sz ++; /* space for NUL char */
544     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
545     if( !str )
546         return str;
547     r = msi_id2string( &db->strings, stringid, str, &sz );
548     if( r == ERROR_SUCCESS )
549         return str;
550     HeapFree(  GetProcessHeap(), 0, str );
551     return NULL;
552 }
553
554 UINT get_tablecolumns( MSIDATABASE *db, 
555        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
556 {
557     UINT r, i, n=0, table_id, count, maxcount = *sz;
558     MSITABLE *table = NULL;
559     const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
560
561     /* first check if there is a default table with that name */
562     r = get_defaulttablecolumns( szTableName, colinfo, sz );
563     if( ( r == ERROR_SUCCESS ) && *sz )
564         return r;
565
566     r = get_table( db, szColumns, &table);
567     if( r != ERROR_SUCCESS )
568     {
569         ERR("table %s not available\n", debugstr_w(szColumns));
570         return r;
571     }
572
573     /* convert table and column names to IDs from the string table */
574     r = msi_string2id( &db->strings, szTableName, &table_id );
575     if( r != ERROR_SUCCESS )
576     {
577         release_table( db, table );
578         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
579         return r;
580     }
581
582     TRACE("Table id is %d\n", table_id);
583
584     count = table->size/8;
585     for( i=0; i<count; i++ )
586     {
587         if( table->data[ i ] != table_id )
588             continue;
589         if( colinfo )
590         {
591             UINT id = table->data[ i + count*2 ];
592             colinfo[n].tablename = MSI_makestring( db, table_id );
593             colinfo[n].number = table->data[ i + count ] - (1<<15);
594             colinfo[n].colname = MSI_makestring( db, id );
595             colinfo[n].type = table->data[ i + count*3 ];
596             /* this assumes that columns are in order in the table */
597             if( n )
598                 colinfo[n].offset = colinfo[n-1].offset
599                                   + bytes_per_column( &colinfo[n-1] );
600             else
601                 colinfo[n].offset = 0;
602             TRACE("table %s column %d is [%s] (%d) with type %08x "
603                   "offset %d at row %d\n", debugstr_w(szTableName),
604                    colinfo[n].number, debugstr_w(colinfo[n].colname),
605                    id, colinfo[n].type, colinfo[n].offset, i);
606             if( n != (colinfo[n].number-1) )
607             {
608                 ERR("oops. data in the _Columns table isn't in the right "
609                     "order for table %s\n", debugstr_w(szTableName));
610                 return ERROR_FUNCTION_FAILED;
611             }
612         }
613         n++;
614         if( colinfo && ( n >= maxcount ) )
615             break;
616     }
617     *sz = n;
618
619     release_table( db, table );
620
621     return ERROR_SUCCESS;
622 }
623
624 /* try to find the table name in the _Tables table */
625 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
626 {
627     const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
628     const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
629     UINT r, table_id = 0, i, count;
630     MSITABLE *table = NULL;
631
632     if( !lstrcmpW( name, szTables ) )
633         return TRUE;
634     if( !lstrcmpW( name, szColumns ) )
635         return TRUE;
636
637     r = msi_string2id( &db->strings, name, &table_id );
638     if( r != ERROR_SUCCESS )
639     {
640         ERR("Couldn't find id for %s\n", debugstr_w(name));
641         return FALSE;
642     }
643
644     r = get_table( db, szTables, &table);
645     if( r != ERROR_SUCCESS )
646     {
647         ERR("table %s not available\n", debugstr_w(szTables));
648         return FALSE;
649     }
650
651     count = table->size/2;
652     for( i=0; i<count; i++ )
653         if( table->data[ i ] == table_id )
654             break;
655
656     release_table( db, table );
657
658     if (i!=count)
659         return TRUE;
660
661     return FALSE;
662 }
663
664 /* below is the query interface to a table */
665
666 typedef struct tagMSITABLEVIEW
667 {
668     MSIVIEW        view;
669     MSIDATABASE   *db;
670     MSITABLE      *table;
671     MSICOLUMNINFO *columns;
672     UINT           num_cols;
673     UINT           row_size;
674     WCHAR          name[1];
675 } MSITABLEVIEW;
676
677
678 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
679 {
680     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
681     UINT offset, num_rows, n;
682
683     if( !tv->table )
684         return ERROR_INVALID_PARAMETER;
685
686     if( (col==0) || (col>tv->num_cols) )
687         return ERROR_INVALID_PARAMETER;
688
689     /* how many rows are there ? */
690     num_rows = tv->table->size / tv->row_size;
691     if( row >= num_rows )
692         return ERROR_NO_MORE_ITEMS;
693
694     if( tv->columns[col-1].offset >= tv->row_size )
695     {
696         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
697         ERR("%p %p\n", tv, tv->columns );
698         return ERROR_FUNCTION_FAILED;
699     }
700
701     offset = row + (tv->columns[col-1].offset/2) * num_rows;
702     n = bytes_per_column( &tv->columns[col-1] );
703     switch( n )
704     {
705     case 4:
706         offset = row*2 + (tv->columns[col-1].offset/2) * num_rows;
707         *val = tv->table->data[offset] + (tv->table->data[offset + 1] << 16);
708         break;
709     case 2:
710         offset = row + (tv->columns[col-1].offset/2) * num_rows;
711         *val = tv->table->data[offset];
712         break;
713     default:
714         ERR("oops! what is %d bytes per column?\n", n );
715         return ERROR_FUNCTION_FAILED;
716     }
717
718     TRACE("Data [%d][%d] = %d \n", row, col, *val );
719
720     return ERROR_SUCCESS;
721 }
722
723 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIHANDLE record )
724 {
725     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
726     UINT r;
727
728     TRACE("%p %ld\n", tv, record);
729
730     if( tv->table )
731         return ERROR_FUNCTION_FAILED;
732
733     r = get_table( tv->db, tv->name, &tv->table );
734     if( r != ERROR_SUCCESS )
735         return r;
736     
737     return ERROR_SUCCESS;
738 }
739
740 static UINT TABLE_close( struct tagMSIVIEW *view )
741 {
742     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
743
744     TRACE("%p\n", view );
745
746     if( !tv->table )
747         return ERROR_FUNCTION_FAILED;
748
749     release_table( tv->db, tv->table );
750     tv->table = NULL;
751     
752     return ERROR_SUCCESS;
753 }
754
755 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
756 {
757     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
758
759     TRACE("%p %p %p\n", view, rows, cols );
760
761     if( cols )
762         *cols = tv->num_cols;
763     if( rows )
764     {
765         if( !tv->table )
766             return ERROR_INVALID_PARAMETER;
767         *rows = tv->table->size / tv->row_size;
768     }
769
770     return ERROR_SUCCESS;
771 }
772
773 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
774                 UINT n, LPWSTR *name, UINT *type )
775 {
776     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
777
778     TRACE("%p %d %p %p\n", tv, n, name, type );
779
780     if( ( n == 0 ) || ( n > tv->num_cols ) )
781         return ERROR_INVALID_PARAMETER;
782
783     if( name )
784     {
785         *name = strdupW( tv->columns[n-1].colname );
786         if( !*name )
787             return ERROR_FUNCTION_FAILED;
788     }
789     if( type )
790         *type = tv->columns[n-1].type;
791
792     return ERROR_SUCCESS;
793 }
794
795 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
796 {
797     FIXME("%p %d %ld\n", view, eModifyMode, hrec );
798     return ERROR_CALL_NOT_IMPLEMENTED;
799 }
800
801 static UINT TABLE_delete( struct tagMSIVIEW *view )
802 {
803     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
804
805     TRACE("%p\n", view );
806
807     if( tv->table )
808         release_table( tv->db, tv->table );
809     tv->table = NULL;
810
811     if( tv->columns )
812     {
813         UINT i;
814         for( i=0; i<tv->num_cols; i++)
815         {
816             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
817             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
818         }
819         HeapFree( GetProcessHeap(), 0, tv->columns );
820     }
821     tv->columns = NULL;
822
823     HeapFree( GetProcessHeap(), 0, tv );
824
825     return ERROR_SUCCESS;
826 }
827
828
829 MSIVIEWOPS table_ops =
830 {
831     TABLE_fetch_int,
832     TABLE_execute,
833     TABLE_close,
834     TABLE_get_dimensions,
835     TABLE_get_column_info,
836     TABLE_modify,
837     TABLE_delete
838 };
839
840 UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view )
841 {
842     MSITABLEVIEW *tv ;
843     UINT r, sz, column_count;
844     MSICOLUMNINFO *columns, *last_col;
845
846     TRACE("%p %s %p\n", db, debugstr_w(name), view );
847
848     /* get the number of columns in this table */
849     column_count = 0;
850     r = get_tablecolumns( db, name, NULL, &column_count );
851     if( r != ERROR_SUCCESS )
852         return r;
853
854     /* if there's no columns, there's no table */
855     if( column_count == 0 )
856         return ERROR_INVALID_PARAMETER;
857
858     TRACE("Table found\n");
859
860     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
861     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
862     if( !tv )
863         return ERROR_FUNCTION_FAILED;
864     
865     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
866     if( !columns )
867     {
868         HeapFree( GetProcessHeap(), 0, tv );
869         return ERROR_FUNCTION_FAILED;
870     }
871
872     r = get_tablecolumns( db, name, columns, &column_count );
873     if( r != ERROR_SUCCESS )
874     {
875         HeapFree( GetProcessHeap(), 0, columns );
876         HeapFree( GetProcessHeap(), 0, tv );
877         return ERROR_FUNCTION_FAILED;
878     }
879
880     TRACE("Table has %d columns\n", column_count);
881
882     last_col = &columns[column_count-1];
883
884     /* fill the structure */
885     tv->view.ops = &table_ops;
886     tv->db = db;
887     tv->columns = columns;
888     tv->num_cols = column_count;
889     tv->table = NULL;
890     tv->row_size = last_col->offset + bytes_per_column( last_col );
891
892     TRACE("one row is %d bytes\n", tv->row_size );
893
894     *view = (MSIVIEW*) tv;
895     lstrcpyW( tv->name, name );
896
897     return ERROR_SUCCESS;
898 }