xref: /reactos/dll/win32/msi/table.c (revision 211c4104)
1c2c66affSColin Finck /*
2c2c66affSColin Finck  * Implementation of the Microsoft Installer (msi.dll)
3c2c66affSColin Finck  *
4c2c66affSColin Finck  * Copyright 2002-2005 Mike McCormack for CodeWeavers
5c2c66affSColin Finck  *
6c2c66affSColin Finck  * This library is free software; you can redistribute it and/or
7c2c66affSColin Finck  * modify it under the terms of the GNU Lesser General Public
8c2c66affSColin Finck  * License as published by the Free Software Foundation; either
9c2c66affSColin Finck  * version 2.1 of the License, or (at your option) any later version.
10c2c66affSColin Finck  *
11c2c66affSColin Finck  * This library is distributed in the hope that it will be useful,
12c2c66affSColin Finck  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13c2c66affSColin Finck  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14c2c66affSColin Finck  * Lesser General Public License for more details.
15c2c66affSColin Finck  *
16c2c66affSColin Finck  * You should have received a copy of the GNU Lesser General Public
17c2c66affSColin Finck  * License along with this library; if not, write to the Free Software
18c2c66affSColin Finck  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19c2c66affSColin Finck  */
20c2c66affSColin Finck 
21c42b133eSAmine Khaldi #include <stdarg.h>
22c42b133eSAmine Khaldi #include <assert.h>
23c42b133eSAmine Khaldi 
24c42b133eSAmine Khaldi #define COBJMACROS
25c42b133eSAmine Khaldi 
26c42b133eSAmine Khaldi #include "windef.h"
27c42b133eSAmine Khaldi #include "winbase.h"
28c42b133eSAmine Khaldi #include "winerror.h"
29c42b133eSAmine Khaldi #include "msi.h"
30c42b133eSAmine Khaldi #include "msiquery.h"
31c42b133eSAmine Khaldi #include "objbase.h"
32c42b133eSAmine Khaldi #include "objidl.h"
33c42b133eSAmine Khaldi #include "winnls.h"
34c2c66affSColin Finck #include "msipriv.h"
35c42b133eSAmine Khaldi #include "query.h"
36c42b133eSAmine Khaldi 
37c42b133eSAmine Khaldi #include "wine/debug.h"
38c42b133eSAmine Khaldi #include "wine/unicode.h"
39c2c66affSColin Finck 
40c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL(msidb);
41c2c66affSColin Finck 
42c2c66affSColin Finck #define MSITABLE_HASH_TABLE_SIZE 37
43c2c66affSColin Finck 
44c2c66affSColin Finck typedef struct tagMSICOLUMNHASHENTRY
45c2c66affSColin Finck {
46c2c66affSColin Finck     struct tagMSICOLUMNHASHENTRY *next;
47c2c66affSColin Finck     UINT value;
48c2c66affSColin Finck     UINT row;
49c2c66affSColin Finck } MSICOLUMNHASHENTRY;
50c2c66affSColin Finck 
51c2c66affSColin Finck typedef struct tagMSICOLUMNINFO
52c2c66affSColin Finck {
53c2c66affSColin Finck     LPCWSTR tablename;
54c2c66affSColin Finck     UINT    number;
55c2c66affSColin Finck     LPCWSTR colname;
56c2c66affSColin Finck     UINT    type;
57c2c66affSColin Finck     UINT    offset;
58c2c66affSColin Finck     INT     ref_count;
59c2c66affSColin Finck     BOOL    temporary;
60c2c66affSColin Finck     MSICOLUMNHASHENTRY **hash_table;
61c2c66affSColin Finck } MSICOLUMNINFO;
62c2c66affSColin Finck 
63c2c66affSColin Finck struct tagMSITABLE
64c2c66affSColin Finck {
65c2c66affSColin Finck     BYTE **data;
66c2c66affSColin Finck     BOOL *data_persistent;
67c2c66affSColin Finck     UINT row_count;
68c2c66affSColin Finck     struct list entry;
69c2c66affSColin Finck     MSICOLUMNINFO *colinfo;
70c2c66affSColin Finck     UINT col_count;
71c2c66affSColin Finck     MSICONDITION persistent;
72c2c66affSColin Finck     INT ref_count;
73c2c66affSColin Finck     WCHAR name[1];
74c2c66affSColin Finck };
75c2c66affSColin Finck 
76c2c66affSColin Finck /* information for default tables */
77c2c66affSColin Finck static const WCHAR szTables[]  = {'_','T','a','b','l','e','s',0};
78c2c66affSColin Finck static const WCHAR szTable[]   = {'T','a','b','l','e',0};
79c2c66affSColin Finck static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0};
80c2c66affSColin Finck static const WCHAR szNumber[]  = {'N','u','m','b','e','r',0};
81c2c66affSColin Finck static const WCHAR szType[]    = {'T','y','p','e',0};
82c2c66affSColin Finck 
83c2c66affSColin Finck static const MSICOLUMNINFO _Columns_cols[4] = {
84c2c66affSColin Finck     { szColumns, 1, szTable,  MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL },
85c2c66affSColin Finck     { szColumns, 2, szNumber, MSITYPE_VALID | MSITYPE_KEY | 2,     2, 0, 0, NULL },
86c2c66affSColin Finck     { szColumns, 3, szName,   MSITYPE_VALID | MSITYPE_STRING | 64, 4, 0, 0, NULL },
87c2c66affSColin Finck     { szColumns, 4, szType,   MSITYPE_VALID | 2,                   6, 0, 0, NULL },
88c2c66affSColin Finck };
89c2c66affSColin Finck 
90c2c66affSColin Finck static const MSICOLUMNINFO _Tables_cols[1] = {
91c2c66affSColin Finck     { szTables,  1, szName,   MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, 0, 0, NULL },
92c2c66affSColin Finck };
93c2c66affSColin Finck 
94c2c66affSColin Finck #define MAX_STREAM_NAME 0x1f
95c2c66affSColin Finck 
96c2c66affSColin Finck static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref )
97c2c66affSColin Finck {
98c2c66affSColin Finck     if( MSITYPE_IS_BINARY(col->type) )
99c2c66affSColin Finck         return 2;
100c2c66affSColin Finck 
101c2c66affSColin Finck     if( col->type & MSITYPE_STRING )
102c2c66affSColin Finck         return bytes_per_strref;
103c2c66affSColin Finck 
104c2c66affSColin Finck     if( (col->type & 0xff) <= 2)
105c2c66affSColin Finck         return 2;
106c2c66affSColin Finck 
107c2c66affSColin Finck     if( (col->type & 0xff) != 4 )
108c2c66affSColin Finck         ERR("Invalid column size %u\n", col->type & 0xff);
109c2c66affSColin Finck 
110c2c66affSColin Finck     return 4;
111c2c66affSColin Finck }
112c2c66affSColin Finck 
113c2c66affSColin Finck static int utf2mime(int x)
114c2c66affSColin Finck {
115c2c66affSColin Finck     if( (x>='0') && (x<='9') )
116c2c66affSColin Finck         return x-'0';
117c2c66affSColin Finck     if( (x>='A') && (x<='Z') )
118c2c66affSColin Finck         return x-'A'+10;
119c2c66affSColin Finck     if( (x>='a') && (x<='z') )
120c2c66affSColin Finck         return x-'a'+10+26;
121c2c66affSColin Finck     if( x=='.' )
122c2c66affSColin Finck         return 10+26+26;
123c2c66affSColin Finck     if( x=='_' )
124c2c66affSColin Finck         return 10+26+26+1;
125c2c66affSColin Finck     return -1;
126c2c66affSColin Finck }
127c2c66affSColin Finck 
128c2c66affSColin Finck LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
129c2c66affSColin Finck {
130c2c66affSColin Finck     DWORD count = MAX_STREAM_NAME;
131c2c66affSColin Finck     DWORD ch, next;
132c2c66affSColin Finck     LPWSTR out, p;
133c2c66affSColin Finck 
134c2c66affSColin Finck     if( !bTable )
135c2c66affSColin Finck         count = lstrlenW( in )+2;
136c2c66affSColin Finck     if (!(out = msi_alloc( count*sizeof(WCHAR) ))) return NULL;
137c2c66affSColin Finck     p = out;
138c2c66affSColin Finck 
139c2c66affSColin Finck     if( bTable )
140c2c66affSColin Finck     {
141c2c66affSColin Finck          *p++ = 0x4840;
142c2c66affSColin Finck          count --;
143c2c66affSColin Finck     }
144c2c66affSColin Finck     while( count -- )
145c2c66affSColin Finck     {
146c2c66affSColin Finck         ch = *in++;
147c2c66affSColin Finck         if( !ch )
148c2c66affSColin Finck         {
149c2c66affSColin Finck             *p = ch;
150c2c66affSColin Finck             return out;
151c2c66affSColin Finck         }
152c2c66affSColin Finck         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
153c2c66affSColin Finck         {
154c2c66affSColin Finck             ch = utf2mime(ch) + 0x4800;
155c2c66affSColin Finck             next = *in;
156c2c66affSColin Finck             if( next && (next<0x80) )
157c2c66affSColin Finck             {
158c2c66affSColin Finck                 next = utf2mime(next);
159c2c66affSColin Finck                 if( next != -1 )
160c2c66affSColin Finck                 {
161c2c66affSColin Finck                      next += 0x3ffffc0;
162c2c66affSColin Finck                      ch += (next<<6);
163c2c66affSColin Finck                      in++;
164c2c66affSColin Finck                 }
165c2c66affSColin Finck             }
166c2c66affSColin Finck         }
167c2c66affSColin Finck         *p++ = ch;
168c2c66affSColin Finck     }
169c2c66affSColin Finck     ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
170c2c66affSColin Finck     msi_free( out );
171c2c66affSColin Finck     return NULL;
172c2c66affSColin Finck }
173c2c66affSColin Finck 
174c2c66affSColin Finck static int mime2utf(int x)
175c2c66affSColin Finck {
176c2c66affSColin Finck     if( x<10 )
177c2c66affSColin Finck         return x + '0';
178c2c66affSColin Finck     if( x<(10+26))
179c2c66affSColin Finck         return x - 10 + 'A';
180c2c66affSColin Finck     if( x<(10+26+26))
181c2c66affSColin Finck         return x - 10 - 26 + 'a';
182c2c66affSColin Finck     if( x == (10+26+26) )
183c2c66affSColin Finck         return '.';
184c2c66affSColin Finck     return '_';
185c2c66affSColin Finck }
186c2c66affSColin Finck 
187c2c66affSColin Finck BOOL decode_streamname(LPCWSTR in, LPWSTR out)
188c2c66affSColin Finck {
189c2c66affSColin Finck     WCHAR ch;
190c2c66affSColin Finck     DWORD count = 0;
191c2c66affSColin Finck 
192c2c66affSColin Finck     while ( (ch = *in++) )
193c2c66affSColin Finck     {
194c2c66affSColin Finck         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
195c2c66affSColin Finck         {
196c2c66affSColin Finck             if( ch >= 0x4800 )
197c2c66affSColin Finck                 ch = mime2utf(ch-0x4800);
198c2c66affSColin Finck             else
199c2c66affSColin Finck             {
200c2c66affSColin Finck                 ch -= 0x3800;
201c2c66affSColin Finck                 *out++ = mime2utf(ch&0x3f);
202c2c66affSColin Finck                 count++;
203c2c66affSColin Finck                 ch = mime2utf((ch>>6)&0x3f);
204c2c66affSColin Finck             }
205c2c66affSColin Finck         }
206c2c66affSColin Finck         *out++ = ch;
207c2c66affSColin Finck         count++;
208c2c66affSColin Finck     }
209c2c66affSColin Finck     *out = 0;
210c2c66affSColin Finck     return count;
211c2c66affSColin Finck }
212c2c66affSColin Finck 
213c2c66affSColin Finck void enum_stream_names( IStorage *stg )
214c2c66affSColin Finck {
215c2c66affSColin Finck     IEnumSTATSTG *stgenum = NULL;
216c2c66affSColin Finck     HRESULT r;
217c2c66affSColin Finck     STATSTG stat;
218c2c66affSColin Finck     ULONG n, count;
219c2c66affSColin Finck     WCHAR name[0x40];
220c2c66affSColin Finck 
221c2c66affSColin Finck     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
222c2c66affSColin Finck     if( FAILED( r ) )
223c2c66affSColin Finck         return;
224c2c66affSColin Finck 
225c2c66affSColin Finck     n = 0;
226c2c66affSColin Finck     while( 1 )
227c2c66affSColin Finck     {
228c2c66affSColin Finck         count = 0;
229c2c66affSColin Finck         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
230c2c66affSColin Finck         if( FAILED( r ) || !count )
231c2c66affSColin Finck             break;
232c2c66affSColin Finck         decode_streamname( stat.pwcsName, name );
233c2c66affSColin Finck         TRACE("stream %2d -> %s %s\n", n,
234c2c66affSColin Finck               debugstr_w(stat.pwcsName), debugstr_w(name) );
235c2c66affSColin Finck         CoTaskMemFree( stat.pwcsName );
236c2c66affSColin Finck         n++;
237c2c66affSColin Finck     }
238c2c66affSColin Finck 
239c2c66affSColin Finck     IEnumSTATSTG_Release( stgenum );
240c2c66affSColin Finck }
241c2c66affSColin Finck 
242c2c66affSColin Finck UINT read_stream_data( IStorage *stg, LPCWSTR stname, BOOL table,
243c2c66affSColin Finck                        BYTE **pdata, UINT *psz )
244c2c66affSColin Finck {
245c2c66affSColin Finck     HRESULT r;
246c2c66affSColin Finck     UINT ret = ERROR_FUNCTION_FAILED;
247c2c66affSColin Finck     VOID *data;
248c2c66affSColin Finck     ULONG sz, count;
249c2c66affSColin Finck     IStream *stm = NULL;
250c2c66affSColin Finck     STATSTG stat;
251c2c66affSColin Finck     LPWSTR encname;
252c2c66affSColin Finck 
253c2c66affSColin Finck     encname = encode_streamname(table, stname);
254c2c66affSColin Finck 
255c2c66affSColin Finck     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
256c2c66affSColin Finck 
257c2c66affSColin Finck     r = IStorage_OpenStream(stg, encname, NULL,
258c2c66affSColin Finck             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
259c2c66affSColin Finck     msi_free( encname );
260c2c66affSColin Finck     if( FAILED( r ) )
261c2c66affSColin Finck     {
262c2c66affSColin Finck         WARN("open stream failed r = %08x - empty table?\n", r);
263c2c66affSColin Finck         return ret;
264c2c66affSColin Finck     }
265c2c66affSColin Finck 
266c2c66affSColin Finck     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
267c2c66affSColin Finck     if( FAILED( r ) )
268c2c66affSColin Finck     {
269c2c66affSColin Finck         WARN("open stream failed r = %08x!\n", r);
270c2c66affSColin Finck         goto end;
271c2c66affSColin Finck     }
272c2c66affSColin Finck 
273c2c66affSColin Finck     if( stat.cbSize.QuadPart >> 32 )
274c2c66affSColin Finck     {
275c2c66affSColin Finck         WARN("Too big!\n");
276c2c66affSColin Finck         goto end;
277c2c66affSColin Finck     }
278c2c66affSColin Finck 
279c2c66affSColin Finck     sz = stat.cbSize.QuadPart;
280c2c66affSColin Finck     data = msi_alloc( sz );
281c2c66affSColin Finck     if( !data )
282c2c66affSColin Finck     {
283c2c66affSColin Finck         WARN("couldn't allocate memory r=%08x!\n", r);
284c2c66affSColin Finck         ret = ERROR_NOT_ENOUGH_MEMORY;
285c2c66affSColin Finck         goto end;
286c2c66affSColin Finck     }
287c2c66affSColin Finck 
288c2c66affSColin Finck     r = IStream_Read(stm, data, sz, &count );
289c2c66affSColin Finck     if( FAILED( r ) || ( count != sz ) )
290c2c66affSColin Finck     {
291c2c66affSColin Finck         msi_free( data );
292c2c66affSColin Finck         WARN("read stream failed r = %08x!\n", r);
293c2c66affSColin Finck         goto end;
294c2c66affSColin Finck     }
295c2c66affSColin Finck 
296c2c66affSColin Finck     *pdata = data;
297c2c66affSColin Finck     *psz = sz;
298c2c66affSColin Finck     ret = ERROR_SUCCESS;
299c2c66affSColin Finck 
300c2c66affSColin Finck end:
301c2c66affSColin Finck     IStream_Release( stm );
302c2c66affSColin Finck 
303c2c66affSColin Finck     return ret;
304c2c66affSColin Finck }
305c2c66affSColin Finck 
306c2c66affSColin Finck UINT write_stream_data( IStorage *stg, LPCWSTR stname,
307c2c66affSColin Finck                         LPCVOID data, UINT sz, BOOL bTable )
308c2c66affSColin Finck {
309c2c66affSColin Finck     HRESULT r;
310c2c66affSColin Finck     UINT ret = ERROR_FUNCTION_FAILED;
311c2c66affSColin Finck     ULONG count;
312c2c66affSColin Finck     IStream *stm = NULL;
313c2c66affSColin Finck     ULARGE_INTEGER size;
314c2c66affSColin Finck     LARGE_INTEGER pos;
315c2c66affSColin Finck     LPWSTR encname;
316c2c66affSColin Finck 
317c2c66affSColin Finck     encname = encode_streamname(bTable, stname );
318c2c66affSColin Finck     r = IStorage_OpenStream( stg, encname, NULL,
319c2c66affSColin Finck             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
320c2c66affSColin Finck     if( FAILED(r) )
321c2c66affSColin Finck     {
322c2c66affSColin Finck         r = IStorage_CreateStream( stg, encname,
323c2c66affSColin Finck                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
324c2c66affSColin Finck     }
325c2c66affSColin Finck     msi_free( encname );
326c2c66affSColin Finck     if( FAILED( r ) )
327c2c66affSColin Finck     {
328c2c66affSColin Finck         WARN("open stream failed r = %08x\n", r);
329c2c66affSColin Finck         return ret;
330c2c66affSColin Finck     }
331c2c66affSColin Finck 
332c2c66affSColin Finck     size.QuadPart = sz;
333c2c66affSColin Finck     r = IStream_SetSize( stm, size );
334c2c66affSColin Finck     if( FAILED( r ) )
335c2c66affSColin Finck     {
336c2c66affSColin Finck         WARN("Failed to SetSize\n");
337c2c66affSColin Finck         goto end;
338c2c66affSColin Finck     }
339c2c66affSColin Finck 
340c2c66affSColin Finck     pos.QuadPart = 0;
341c2c66affSColin Finck     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
342c2c66affSColin Finck     if( FAILED( r ) )
343c2c66affSColin Finck     {
344c2c66affSColin Finck         WARN("Failed to Seek\n");
345c2c66affSColin Finck         goto end;
346c2c66affSColin Finck     }
347c2c66affSColin Finck 
348c2c66affSColin Finck     if (sz)
349c2c66affSColin Finck     {
350c2c66affSColin Finck         r = IStream_Write(stm, data, sz, &count );
351c2c66affSColin Finck         if( FAILED( r ) || ( count != sz ) )
352c2c66affSColin Finck         {
353c2c66affSColin Finck             WARN("Failed to Write\n");
354c2c66affSColin Finck             goto end;
355c2c66affSColin Finck         }
356c2c66affSColin Finck     }
357c2c66affSColin Finck 
358c2c66affSColin Finck     ret = ERROR_SUCCESS;
359c2c66affSColin Finck 
360c2c66affSColin Finck end:
361c2c66affSColin Finck     IStream_Release( stm );
362c2c66affSColin Finck 
363c2c66affSColin Finck     return ret;
364c2c66affSColin Finck }
365c2c66affSColin Finck 
366c2c66affSColin Finck static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
367c2c66affSColin Finck {
368c2c66affSColin Finck     UINT i;
369c2c66affSColin Finck     for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table );
370c2c66affSColin Finck }
371c2c66affSColin Finck 
372c2c66affSColin Finck static void free_table( MSITABLE *table )
373c2c66affSColin Finck {
374c2c66affSColin Finck     UINT i;
375c2c66affSColin Finck     for( i=0; i<table->row_count; i++ )
376c2c66affSColin Finck         msi_free( table->data[i] );
377c2c66affSColin Finck     msi_free( table->data );
378c2c66affSColin Finck     msi_free( table->data_persistent );
379c2c66affSColin Finck     msi_free_colinfo( table->colinfo, table->col_count );
380c2c66affSColin Finck     msi_free( table->colinfo );
381c2c66affSColin Finck     msi_free( table );
382c2c66affSColin Finck }
383c2c66affSColin Finck 
384c2c66affSColin Finck static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref )
385c2c66affSColin Finck {
386c2c66affSColin Finck     const MSICOLUMNINFO *last_col;
387c2c66affSColin Finck 
388c2c66affSColin Finck     if (!count)
389c2c66affSColin Finck         return 0;
390c2c66affSColin Finck 
391c2c66affSColin Finck     if (bytes_per_strref != LONG_STR_BYTES)
392c2c66affSColin Finck     {
393c2c66affSColin Finck         UINT i, size = 0;
394c2c66affSColin Finck         for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref );
395c2c66affSColin Finck         return size;
396c2c66affSColin Finck     }
397c2c66affSColin Finck     last_col = &cols[count - 1];
398c2c66affSColin Finck     return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref );
399c2c66affSColin Finck }
400c2c66affSColin Finck 
401c2c66affSColin Finck /* add this table to the list of cached tables in the database */
402c2c66affSColin Finck static UINT read_table_from_storage( MSIDATABASE *db, MSITABLE *t, IStorage *stg )
403c2c66affSColin Finck {
404c2c66affSColin Finck     BYTE *rawdata = NULL;
405c2c66affSColin Finck     UINT rawsize = 0, i, j, row_size, row_size_mem;
406c2c66affSColin Finck 
407c2c66affSColin Finck     TRACE("%s\n",debugstr_w(t->name));
408c2c66affSColin Finck 
409c2c66affSColin Finck     row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, db->bytes_per_strref );
410c2c66affSColin Finck     row_size_mem = msi_table_get_row_size( db, t->colinfo, t->col_count, LONG_STR_BYTES );
411c2c66affSColin Finck 
412c2c66affSColin Finck     /* if we can't read the table, just assume that it's empty */
413c2c66affSColin Finck     read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize );
414c2c66affSColin Finck     if( !rawdata )
415c2c66affSColin Finck         return ERROR_SUCCESS;
416c2c66affSColin Finck 
417c2c66affSColin Finck     TRACE("Read %d bytes\n", rawsize );
418c2c66affSColin Finck 
419c2c66affSColin Finck     if( rawsize % row_size )
420c2c66affSColin Finck     {
421c2c66affSColin Finck         WARN("Table size is invalid %d/%d\n", rawsize, row_size );
422c2c66affSColin Finck         goto err;
423c2c66affSColin Finck     }
424c2c66affSColin Finck 
425c2c66affSColin Finck     if ((t->row_count = rawsize / row_size))
426c2c66affSColin Finck     {
427c2c66affSColin Finck         if (!(t->data = msi_alloc_zero( t->row_count * sizeof(USHORT *) ))) goto err;
428c2c66affSColin Finck         if (!(t->data_persistent = msi_alloc_zero( t->row_count * sizeof(BOOL) ))) goto err;
429c2c66affSColin Finck     }
430c2c66affSColin Finck 
431c2c66affSColin Finck     /* transpose all the data */
432c2c66affSColin Finck     TRACE("Transposing data from %d rows\n", t->row_count );
433c2c66affSColin Finck     for (i = 0; i < t->row_count; i++)
434c2c66affSColin Finck     {
435c2c66affSColin Finck         UINT ofs = 0, ofs_mem = 0;
436c2c66affSColin Finck 
437c2c66affSColin Finck         t->data[i] = msi_alloc( row_size_mem );
438c2c66affSColin Finck         if( !t->data[i] )
439c2c66affSColin Finck             goto err;
440c2c66affSColin Finck         t->data_persistent[i] = TRUE;
441c2c66affSColin Finck 
442c2c66affSColin Finck         for (j = 0; j < t->col_count; j++)
443c2c66affSColin Finck         {
444c2c66affSColin Finck             UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
445c2c66affSColin Finck             UINT n = bytes_per_column( db, &t->colinfo[j], db->bytes_per_strref );
446c2c66affSColin Finck             UINT k;
447c2c66affSColin Finck 
448c2c66affSColin Finck             if ( n != 2 && n != 3 && n != 4 )
449c2c66affSColin Finck             {
450c2c66affSColin Finck                 ERR("oops - unknown column width %d\n", n);
451c2c66affSColin Finck                 goto err;
452c2c66affSColin Finck             }
453c2c66affSColin Finck             if (t->colinfo[j].type & MSITYPE_STRING && n < m)
454c2c66affSColin Finck             {
455c2c66affSColin Finck                 for (k = 0; k < m; k++)
456c2c66affSColin Finck                 {
457c2c66affSColin Finck                     if (k < n)
458c2c66affSColin Finck                         t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
459c2c66affSColin Finck                     else
460c2c66affSColin Finck                         t->data[i][ofs_mem + k] = 0;
461c2c66affSColin Finck                 }
462c2c66affSColin Finck             }
463c2c66affSColin Finck             else
464c2c66affSColin Finck             {
465c2c66affSColin Finck                 for (k = 0; k < n; k++)
466c2c66affSColin Finck                     t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
467c2c66affSColin Finck             }
468c2c66affSColin Finck             ofs_mem += m;
469c2c66affSColin Finck             ofs += n;
470c2c66affSColin Finck         }
471c2c66affSColin Finck     }
472c2c66affSColin Finck 
473c2c66affSColin Finck     msi_free( rawdata );
474c2c66affSColin Finck     return ERROR_SUCCESS;
475c2c66affSColin Finck err:
476c2c66affSColin Finck     msi_free( rawdata );
477c2c66affSColin Finck     return ERROR_FUNCTION_FAILED;
478c2c66affSColin Finck }
479c2c66affSColin Finck 
480c2c66affSColin Finck void free_cached_tables( MSIDATABASE *db )
481c2c66affSColin Finck {
482c2c66affSColin Finck     while( !list_empty( &db->tables ) )
483c2c66affSColin Finck     {
484c2c66affSColin Finck         MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry );
485c2c66affSColin Finck 
486c2c66affSColin Finck         list_remove( &t->entry );
487c2c66affSColin Finck         free_table( t );
488c2c66affSColin Finck     }
489c2c66affSColin Finck }
490c2c66affSColin Finck 
491c2c66affSColin Finck static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name )
492c2c66affSColin Finck {
493c2c66affSColin Finck     MSITABLE *t;
494c2c66affSColin Finck 
495c2c66affSColin Finck     LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry )
496c2c66affSColin Finck         if( !strcmpW( name, t->name ) )
497c2c66affSColin Finck             return t;
498c2c66affSColin Finck 
499c2c66affSColin Finck     return NULL;
500c2c66affSColin Finck }
501c2c66affSColin Finck 
502c2c66affSColin Finck static void table_calc_column_offsets( MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count )
503c2c66affSColin Finck {
504c2c66affSColin Finck     DWORD i;
505c2c66affSColin Finck 
506c2c66affSColin Finck     for (i = 0; colinfo && i < count; i++)
507c2c66affSColin Finck     {
508c2c66affSColin Finck          assert( i + 1 == colinfo[i].number );
509c2c66affSColin Finck          if (i) colinfo[i].offset = colinfo[i - 1].offset +
510c2c66affSColin Finck                                     bytes_per_column( db, &colinfo[i - 1], LONG_STR_BYTES );
511c2c66affSColin Finck          else colinfo[i].offset = 0;
512c2c66affSColin Finck 
513c2c66affSColin Finck          TRACE("column %d is [%s] with type %08x ofs %d\n",
514c2c66affSColin Finck                colinfo[i].number, debugstr_w(colinfo[i].colname),
515c2c66affSColin Finck                colinfo[i].type, colinfo[i].offset);
516c2c66affSColin Finck     }
517c2c66affSColin Finck }
518c2c66affSColin Finck 
519c2c66affSColin Finck static UINT get_defaulttablecolumns( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz )
520c2c66affSColin Finck {
521c2c66affSColin Finck     const MSICOLUMNINFO *p;
522c2c66affSColin Finck     DWORD i, n;
523c2c66affSColin Finck 
524c2c66affSColin Finck     TRACE("%s\n", debugstr_w(name));
525c2c66affSColin Finck 
526c2c66affSColin Finck     if (!strcmpW( name, szTables ))
527c2c66affSColin Finck     {
528c2c66affSColin Finck         p = _Tables_cols;
529c2c66affSColin Finck         n = 1;
530c2c66affSColin Finck     }
531c2c66affSColin Finck     else if (!strcmpW( name, szColumns ))
532c2c66affSColin Finck     {
533c2c66affSColin Finck         p = _Columns_cols;
534c2c66affSColin Finck         n = 4;
535c2c66affSColin Finck     }
536c2c66affSColin Finck     else return ERROR_FUNCTION_FAILED;
537c2c66affSColin Finck 
538c2c66affSColin Finck     for (i = 0; i < n; i++)
539c2c66affSColin Finck     {
540c2c66affSColin Finck         if (colinfo && i < *sz) colinfo[i] = p[i];
541c2c66affSColin Finck         if (colinfo && i >= *sz) break;
542c2c66affSColin Finck     }
543c2c66affSColin Finck     table_calc_column_offsets( db, colinfo, n );
544c2c66affSColin Finck     *sz = n;
545c2c66affSColin Finck     return ERROR_SUCCESS;
546c2c66affSColin Finck }
547c2c66affSColin Finck 
548c2c66affSColin Finck static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz );
549c2c66affSColin Finck 
550c2c66affSColin Finck static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
551c2c66affSColin Finck {
552c2c66affSColin Finck     UINT r, column_count = 0;
553c2c66affSColin Finck     MSICOLUMNINFO *columns;
554c2c66affSColin Finck 
555c2c66affSColin Finck     /* get the number of columns in this table */
556c2c66affSColin Finck     column_count = 0;
557c2c66affSColin Finck     r = get_tablecolumns( db, name, NULL, &column_count );
558c2c66affSColin Finck     if (r != ERROR_SUCCESS)
559c2c66affSColin Finck         return r;
560c2c66affSColin Finck 
561c2c66affSColin Finck     *pcount = column_count;
562c2c66affSColin Finck 
563c2c66affSColin Finck     /* if there are no columns, there's no table */
564c2c66affSColin Finck     if (!column_count)
565c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
566c2c66affSColin Finck 
567c2c66affSColin Finck     TRACE("table %s found\n", debugstr_w(name));
568c2c66affSColin Finck 
569c2c66affSColin Finck     columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) );
570c2c66affSColin Finck     if (!columns)
571c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
572c2c66affSColin Finck 
573c2c66affSColin Finck     r = get_tablecolumns( db, name, columns, &column_count );
574c2c66affSColin Finck     if (r != ERROR_SUCCESS)
575c2c66affSColin Finck     {
576c2c66affSColin Finck         msi_free( columns );
577c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
578c2c66affSColin Finck     }
579c2c66affSColin Finck     *pcols = columns;
580c2c66affSColin Finck     return r;
581c2c66affSColin Finck }
582c2c66affSColin Finck 
583c2c66affSColin Finck static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret )
584c2c66affSColin Finck {
585c2c66affSColin Finck     MSITABLE *table;
586c2c66affSColin Finck     UINT r;
587c2c66affSColin Finck 
588c2c66affSColin Finck     /* first, see if the table is cached */
589c2c66affSColin Finck     table = find_cached_table( db, name );
590c2c66affSColin Finck     if (table)
591c2c66affSColin Finck     {
592c2c66affSColin Finck         *table_ret = table;
593c2c66affSColin Finck         return ERROR_SUCCESS;
594c2c66affSColin Finck     }
595c2c66affSColin Finck 
596c2c66affSColin Finck     /* nonexistent tables should be interpreted as empty tables */
597c2c66affSColin Finck     table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) );
598c2c66affSColin Finck     if (!table)
599c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
600c2c66affSColin Finck 
601c2c66affSColin Finck     table->row_count = 0;
602c2c66affSColin Finck     table->data = NULL;
603c2c66affSColin Finck     table->data_persistent = NULL;
604c2c66affSColin Finck     table->colinfo = NULL;
605c2c66affSColin Finck     table->col_count = 0;
606c2c66affSColin Finck     table->persistent = MSICONDITION_TRUE;
607c2c66affSColin Finck     lstrcpyW( table->name, name );
608c2c66affSColin Finck 
609c2c66affSColin Finck     if (!strcmpW( name, szTables ) || !strcmpW( name, szColumns ))
610c2c66affSColin Finck         table->persistent = MSICONDITION_NONE;
611c2c66affSColin Finck 
612c2c66affSColin Finck     r = table_get_column_info( db, name, &table->colinfo, &table->col_count );
613c2c66affSColin Finck     if (r != ERROR_SUCCESS)
614c2c66affSColin Finck     {
615c2c66affSColin Finck         free_table( table );
616c2c66affSColin Finck         return r;
617c2c66affSColin Finck     }
618c2c66affSColin Finck     r = read_table_from_storage( db, table, db->storage );
619c2c66affSColin Finck     if (r != ERROR_SUCCESS)
620c2c66affSColin Finck     {
621c2c66affSColin Finck         free_table( table );
622c2c66affSColin Finck         return r;
623c2c66affSColin Finck     }
624c2c66affSColin Finck     list_add_head( &db->tables, &table->entry );
625c2c66affSColin Finck     *table_ret = table;
626c2c66affSColin Finck     return ERROR_SUCCESS;
627c2c66affSColin Finck }
628c2c66affSColin Finck 
629c2c66affSColin Finck static UINT read_table_int( BYTE *const *data, UINT row, UINT col, UINT bytes )
630c2c66affSColin Finck {
631c2c66affSColin Finck     UINT ret = 0, i;
632c2c66affSColin Finck 
633c2c66affSColin Finck     for (i = 0; i < bytes; i++)
634c2c66affSColin Finck         ret += data[row][col + i] << i * 8;
635c2c66affSColin Finck 
636c2c66affSColin Finck     return ret;
637c2c66affSColin Finck }
638c2c66affSColin Finck 
639c2c66affSColin Finck static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz )
640c2c66affSColin Finck {
641c2c66affSColin Finck     UINT r, i, n = 0, table_id, count, maxcount = *sz;
642c2c66affSColin Finck     MSITABLE *table = NULL;
643c2c66affSColin Finck 
644c2c66affSColin Finck     TRACE("%s\n", debugstr_w(szTableName));
645c2c66affSColin Finck 
646c2c66affSColin Finck     /* first check if there is a default table with that name */
647c2c66affSColin Finck     r = get_defaulttablecolumns( db, szTableName, colinfo, sz );
648c2c66affSColin Finck     if (r == ERROR_SUCCESS && *sz)
649c2c66affSColin Finck         return r;
650c2c66affSColin Finck 
651c2c66affSColin Finck     r = get_table( db, szColumns, &table );
652c2c66affSColin Finck     if (r != ERROR_SUCCESS)
653c2c66affSColin Finck     {
654c2c66affSColin Finck         ERR("couldn't load _Columns table\n");
655c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
656c2c66affSColin Finck     }
657c2c66affSColin Finck 
658c2c66affSColin Finck     /* convert table and column names to IDs from the string table */
659c2c66affSColin Finck     r = msi_string2id( db->strings, szTableName, -1, &table_id );
660c2c66affSColin Finck     if (r != ERROR_SUCCESS)
661c2c66affSColin Finck     {
662c2c66affSColin Finck         WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
663c2c66affSColin Finck         return r;
664c2c66affSColin Finck     }
665c2c66affSColin Finck     TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
666c2c66affSColin Finck 
667c2c66affSColin Finck     /* Note: _Columns table doesn't have non-persistent data */
668c2c66affSColin Finck 
669c2c66affSColin Finck     /* if maxcount is non-zero, assume it's exactly right for this table */
670c2c66affSColin Finck     if (colinfo) memset( colinfo, 0, maxcount * sizeof(*colinfo) );
671c2c66affSColin Finck     count = table->row_count;
672c2c66affSColin Finck     for (i = 0; i < count; i++)
673c2c66affSColin Finck     {
674c2c66affSColin Finck         if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue;
675c2c66affSColin Finck         if (colinfo)
676c2c66affSColin Finck         {
677c2c66affSColin Finck             UINT id = read_table_int( table->data, i, table->colinfo[2].offset, LONG_STR_BYTES );
678c2c66affSColin Finck             UINT col = read_table_int( table->data, i, table->colinfo[1].offset, sizeof(USHORT) ) - (1 << 15);
679c2c66affSColin Finck 
680c2c66affSColin Finck             /* check the column number is in range */
681c2c66affSColin Finck             if (col < 1 || col > maxcount)
682c2c66affSColin Finck             {
683c2c66affSColin Finck                 ERR("column %d out of range (maxcount: %d)\n", col, maxcount);
684c2c66affSColin Finck                 continue;
685c2c66affSColin Finck             }
686c2c66affSColin Finck             /* check if this column was already set */
687c2c66affSColin Finck             if (colinfo[col - 1].number)
688c2c66affSColin Finck             {
689c2c66affSColin Finck                 ERR("duplicate column %d\n", col);
690c2c66affSColin Finck                 continue;
691c2c66affSColin Finck             }
692c2c66affSColin Finck             colinfo[col - 1].tablename = msi_string_lookup( db->strings, table_id, NULL );
693c2c66affSColin Finck             colinfo[col - 1].number = col;
694c2c66affSColin Finck             colinfo[col - 1].colname = msi_string_lookup( db->strings, id, NULL );
695c2c66affSColin Finck             colinfo[col - 1].type = read_table_int( table->data, i, table->colinfo[3].offset,
696c2c66affSColin Finck                                                     sizeof(USHORT) ) - (1 << 15);
697c2c66affSColin Finck             colinfo[col - 1].offset = 0;
698c2c66affSColin Finck             colinfo[col - 1].ref_count = 0;
699c2c66affSColin Finck             colinfo[col - 1].hash_table = NULL;
700c2c66affSColin Finck         }
701c2c66affSColin Finck         n++;
702c2c66affSColin Finck     }
703c2c66affSColin Finck     TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
704c2c66affSColin Finck 
705c2c66affSColin Finck     if (colinfo && n != maxcount)
706c2c66affSColin Finck     {
707c2c66affSColin Finck         ERR("missing column in table %s\n", debugstr_w(szTableName));
708c2c66affSColin Finck         msi_free_colinfo( colinfo, maxcount );
709c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
710c2c66affSColin Finck     }
711c2c66affSColin Finck     table_calc_column_offsets( db, colinfo, n );
712c2c66affSColin Finck     *sz = n;
713c2c66affSColin Finck     return ERROR_SUCCESS;
714c2c66affSColin Finck }
715c2c66affSColin Finck 
716c2c66affSColin Finck UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info,
717c2c66affSColin Finck                        MSICONDITION persistent )
718c2c66affSColin Finck {
719c2c66affSColin Finck     UINT r, nField;
720c2c66affSColin Finck     MSIVIEW *tv = NULL;
721c2c66affSColin Finck     MSIRECORD *rec = NULL;
722c2c66affSColin Finck     column_info *col;
723c2c66affSColin Finck     MSITABLE *table;
724c2c66affSColin Finck     UINT i;
725c2c66affSColin Finck 
726c2c66affSColin Finck     /* only add tables that don't exist already */
727c2c66affSColin Finck     if( TABLE_Exists(db, name ) )
728c2c66affSColin Finck     {
729c2c66affSColin Finck         WARN("table %s exists\n", debugstr_w(name));
730c2c66affSColin Finck         return ERROR_BAD_QUERY_SYNTAX;
731c2c66affSColin Finck     }
732c2c66affSColin Finck 
733c2c66affSColin Finck     table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
734c2c66affSColin Finck     if( !table )
735c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
736c2c66affSColin Finck 
737c2c66affSColin Finck     table->ref_count = 1;
738c2c66affSColin Finck     table->row_count = 0;
739c2c66affSColin Finck     table->data = NULL;
740c2c66affSColin Finck     table->data_persistent = NULL;
741c2c66affSColin Finck     table->colinfo = NULL;
742c2c66affSColin Finck     table->col_count = 0;
743c2c66affSColin Finck     table->persistent = persistent;
744c2c66affSColin Finck     lstrcpyW( table->name, name );
745c2c66affSColin Finck 
746c2c66affSColin Finck     for( col = col_info; col; col = col->next )
747c2c66affSColin Finck         table->col_count++;
748c2c66affSColin Finck 
749c2c66affSColin Finck     table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) );
750c2c66affSColin Finck     if (!table->colinfo)
751c2c66affSColin Finck     {
752c2c66affSColin Finck         free_table( table );
753c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
754c2c66affSColin Finck     }
755c2c66affSColin Finck 
756c2c66affSColin Finck     for( i = 0, col = col_info; col; i++, col = col->next )
757c2c66affSColin Finck     {
758*211c4104Swinesync         UINT table_id = msi_add_string( db->strings, col->table, -1, persistent );
759*211c4104Swinesync         UINT col_id = msi_add_string( db->strings, col->column, -1, persistent );
760c2c66affSColin Finck 
761c2c66affSColin Finck         table->colinfo[ i ].tablename = msi_string_lookup( db->strings, table_id, NULL );
762c2c66affSColin Finck         table->colinfo[ i ].number = i + 1;
763c2c66affSColin Finck         table->colinfo[ i ].colname = msi_string_lookup( db->strings, col_id, NULL );
764c2c66affSColin Finck         table->colinfo[ i ].type = col->type;
765c2c66affSColin Finck         table->colinfo[ i ].offset = 0;
766c2c66affSColin Finck         table->colinfo[ i ].ref_count = 0;
767c2c66affSColin Finck         table->colinfo[ i ].hash_table = NULL;
768c2c66affSColin Finck         table->colinfo[ i ].temporary = col->temporary;
769c2c66affSColin Finck     }
770c2c66affSColin Finck     table_calc_column_offsets( db, table->colinfo, table->col_count);
771c2c66affSColin Finck 
772c2c66affSColin Finck     r = TABLE_CreateView( db, szTables, &tv );
773c2c66affSColin Finck     TRACE("CreateView returned %x\n", r);
774c2c66affSColin Finck     if( r )
775c2c66affSColin Finck     {
776c2c66affSColin Finck         free_table( table );
777c2c66affSColin Finck         return r;
778c2c66affSColin Finck     }
779c2c66affSColin Finck 
780c2c66affSColin Finck     r = tv->ops->execute( tv, 0 );
781c2c66affSColin Finck     TRACE("tv execute returned %x\n", r);
782c2c66affSColin Finck     if( r )
783c2c66affSColin Finck         goto err;
784c2c66affSColin Finck 
785c2c66affSColin Finck     rec = MSI_CreateRecord( 1 );
786c2c66affSColin Finck     if( !rec )
787c2c66affSColin Finck         goto err;
788c2c66affSColin Finck 
789c2c66affSColin Finck     r = MSI_RecordSetStringW( rec, 1, name );
790c2c66affSColin Finck     if( r )
791c2c66affSColin Finck         goto err;
792c2c66affSColin Finck 
793c2c66affSColin Finck     r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE );
794c2c66affSColin Finck     TRACE("insert_row returned %x\n", r);
795c2c66affSColin Finck     if( r )
796c2c66affSColin Finck         goto err;
797c2c66affSColin Finck 
798c2c66affSColin Finck     tv->ops->delete( tv );
799c2c66affSColin Finck     tv = NULL;
800c2c66affSColin Finck 
801c2c66affSColin Finck     msiobj_release( &rec->hdr );
802c2c66affSColin Finck     rec = NULL;
803c2c66affSColin Finck 
804c2c66affSColin Finck     if( persistent != MSICONDITION_FALSE )
805c2c66affSColin Finck     {
806c2c66affSColin Finck         /* add each column to the _Columns table */
807c2c66affSColin Finck         r = TABLE_CreateView( db, szColumns, &tv );
808c2c66affSColin Finck         if( r )
809c2c66affSColin Finck             goto err;
810c2c66affSColin Finck 
811c2c66affSColin Finck         r = tv->ops->execute( tv, 0 );
812c2c66affSColin Finck         TRACE("tv execute returned %x\n", r);
813c2c66affSColin Finck         if( r )
814c2c66affSColin Finck             goto err;
815c2c66affSColin Finck 
816c2c66affSColin Finck         rec = MSI_CreateRecord( 4 );
817c2c66affSColin Finck         if( !rec )
818c2c66affSColin Finck             goto err;
819c2c66affSColin Finck 
820c2c66affSColin Finck         r = MSI_RecordSetStringW( rec, 1, name );
821c2c66affSColin Finck         if( r )
822c2c66affSColin Finck             goto err;
823c2c66affSColin Finck 
824c2c66affSColin Finck         /*
825c2c66affSColin Finck          * need to set the table, column number, col name and type
826c2c66affSColin Finck          * for each column we enter in the table
827c2c66affSColin Finck          */
828c2c66affSColin Finck         nField = 1;
829c2c66affSColin Finck         for( col = col_info; col; col = col->next )
830c2c66affSColin Finck         {
831c2c66affSColin Finck             r = MSI_RecordSetInteger( rec, 2, nField );
832c2c66affSColin Finck             if( r )
833c2c66affSColin Finck                 goto err;
834c2c66affSColin Finck 
835c2c66affSColin Finck             r = MSI_RecordSetStringW( rec, 3, col->column );
836c2c66affSColin Finck             if( r )
837c2c66affSColin Finck                 goto err;
838c2c66affSColin Finck 
839c2c66affSColin Finck             r = MSI_RecordSetInteger( rec, 4, col->type );
840c2c66affSColin Finck             if( r )
841c2c66affSColin Finck                 goto err;
842c2c66affSColin Finck 
843c2c66affSColin Finck             r = tv->ops->insert_row( tv, rec, -1, FALSE );
844c2c66affSColin Finck             if( r )
845c2c66affSColin Finck                 goto err;
846c2c66affSColin Finck 
847c2c66affSColin Finck             nField++;
848c2c66affSColin Finck         }
849c2c66affSColin Finck         if( !col )
850c2c66affSColin Finck             r = ERROR_SUCCESS;
851c2c66affSColin Finck     }
852c2c66affSColin Finck 
853c2c66affSColin Finck err:
854c2c66affSColin Finck     if (rec)
855c2c66affSColin Finck         msiobj_release( &rec->hdr );
856c2c66affSColin Finck     /* FIXME: remove values from the string table on error */
857c2c66affSColin Finck     if( tv )
858c2c66affSColin Finck         tv->ops->delete( tv );
859c2c66affSColin Finck 
860c2c66affSColin Finck     if (r == ERROR_SUCCESS)
861c2c66affSColin Finck         list_add_head( &db->tables, &table->entry );
862c2c66affSColin Finck     else
863c2c66affSColin Finck         free_table( table );
864c2c66affSColin Finck 
865c2c66affSColin Finck     return r;
866c2c66affSColin Finck }
867c2c66affSColin Finck 
868c2c66affSColin Finck static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref )
869c2c66affSColin Finck {
870c2c66affSColin Finck     BYTE *rawdata = NULL;
871c2c66affSColin Finck     UINT rawsize, i, j, row_size, row_count;
872c2c66affSColin Finck     UINT r = ERROR_FUNCTION_FAILED;
873c2c66affSColin Finck 
874c2c66affSColin Finck     /* Nothing to do for non-persistent tables */
875c2c66affSColin Finck     if( t->persistent == MSICONDITION_FALSE )
876c2c66affSColin Finck         return ERROR_SUCCESS;
877c2c66affSColin Finck 
878c2c66affSColin Finck     TRACE("Saving %s\n", debugstr_w( t->name ) );
879c2c66affSColin Finck 
880c2c66affSColin Finck     row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref );
881c2c66affSColin Finck     row_count = t->row_count;
882c2c66affSColin Finck     for (i = 0; i < t->row_count; i++)
883c2c66affSColin Finck     {
884c2c66affSColin Finck         if (!t->data_persistent[i])
885c2c66affSColin Finck         {
886c2c66affSColin Finck             row_count = 1; /* yes, this is bizarre */
887c2c66affSColin Finck             break;
888c2c66affSColin Finck         }
889c2c66affSColin Finck     }
890c2c66affSColin Finck     rawsize = row_count * row_size;
891c2c66affSColin Finck     rawdata = msi_alloc_zero( rawsize );
892c2c66affSColin Finck     if( !rawdata )
893c2c66affSColin Finck     {
894c2c66affSColin Finck         r = ERROR_NOT_ENOUGH_MEMORY;
895c2c66affSColin Finck         goto err;
896c2c66affSColin Finck     }
897c2c66affSColin Finck 
898c2c66affSColin Finck     rawsize = 0;
899c2c66affSColin Finck     for (i = 0; i < row_count; i++)
900c2c66affSColin Finck     {
901c2c66affSColin Finck         UINT ofs = 0, ofs_mem = 0;
902c2c66affSColin Finck 
903c2c66affSColin Finck         if (!t->data_persistent[i]) break;
904c2c66affSColin Finck 
905c2c66affSColin Finck         for (j = 0; j < t->col_count; j++)
906c2c66affSColin Finck         {
907c2c66affSColin Finck             UINT m = bytes_per_column( db, &t->colinfo[j], LONG_STR_BYTES );
908c2c66affSColin Finck             UINT n = bytes_per_column( db, &t->colinfo[j], bytes_per_strref );
909c2c66affSColin Finck             UINT k;
910c2c66affSColin Finck 
911c2c66affSColin Finck             if (n != 2 && n != 3 && n != 4)
912c2c66affSColin Finck             {
913c2c66affSColin Finck                 ERR("oops - unknown column width %d\n", n);
914c2c66affSColin Finck                 goto err;
915c2c66affSColin Finck             }
916c2c66affSColin Finck             if (t->colinfo[j].type & MSITYPE_STRING && n < m)
917c2c66affSColin Finck             {
918c2c66affSColin Finck                 UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES );
919c2c66affSColin Finck                 if (id > 1 << bytes_per_strref * 8)
920c2c66affSColin Finck                 {
921c2c66affSColin Finck                     ERR("string id %u out of range\n", id);
922c2c66affSColin Finck                     goto err;
923c2c66affSColin Finck                 }
924c2c66affSColin Finck             }
925c2c66affSColin Finck             for (k = 0; k < n; k++)
926c2c66affSColin Finck             {
927c2c66affSColin Finck                 rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k];
928c2c66affSColin Finck             }
929c2c66affSColin Finck             ofs_mem += m;
930c2c66affSColin Finck             ofs += n;
931c2c66affSColin Finck         }
932c2c66affSColin Finck         rawsize += row_size;
933c2c66affSColin Finck     }
934c2c66affSColin Finck 
935c2c66affSColin Finck     TRACE("writing %d bytes\n", rawsize);
936c2c66affSColin Finck     r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE );
937c2c66affSColin Finck 
938c2c66affSColin Finck err:
939c2c66affSColin Finck     msi_free( rawdata );
940c2c66affSColin Finck     return r;
941c2c66affSColin Finck }
942c2c66affSColin Finck 
943c2c66affSColin Finck static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name )
944c2c66affSColin Finck {
945c2c66affSColin Finck     MSITABLE *table;
946c2c66affSColin Finck     UINT size, offset, old_count;
947c2c66affSColin Finck     UINT n;
948c2c66affSColin Finck 
949c2c66affSColin Finck     if (!(table = find_cached_table( db, name ))) return;
950c2c66affSColin Finck     old_count = table->col_count;
951c2c66affSColin Finck     msi_free_colinfo( table->colinfo, table->col_count );
952c2c66affSColin Finck     msi_free( table->colinfo );
953c2c66affSColin Finck     table->colinfo = NULL;
954c2c66affSColin Finck 
955c2c66affSColin Finck     table_get_column_info( db, name, &table->colinfo, &table->col_count );
956c2c66affSColin Finck     if (!table->col_count) return;
957c2c66affSColin Finck 
958c2c66affSColin Finck     size = msi_table_get_row_size( db, table->colinfo, table->col_count, LONG_STR_BYTES );
959c2c66affSColin Finck     offset = table->colinfo[table->col_count - 1].offset;
960c2c66affSColin Finck 
961c2c66affSColin Finck     for ( n = 0; n < table->row_count; n++ )
962c2c66affSColin Finck     {
963c2c66affSColin Finck         table->data[n] = msi_realloc( table->data[n], size );
964c2c66affSColin Finck         if (old_count < table->col_count)
965c2c66affSColin Finck             memset( &table->data[n][offset], 0, size - offset );
966c2c66affSColin Finck     }
967c2c66affSColin Finck }
968c2c66affSColin Finck 
969c2c66affSColin Finck /* try to find the table name in the _Tables table */
970c2c66affSColin Finck BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name )
971c2c66affSColin Finck {
972c2c66affSColin Finck     UINT r, table_id, i;
973c2c66affSColin Finck     MSITABLE *table;
974c2c66affSColin Finck 
975c2c66affSColin Finck     if( !strcmpW( name, szTables ) || !strcmpW( name, szColumns ) ||
976c2c66affSColin Finck         !strcmpW( name, szStreams ) || !strcmpW( name, szStorages ) )
977c2c66affSColin Finck         return TRUE;
978c2c66affSColin Finck 
979c2c66affSColin Finck     r = msi_string2id( db->strings, name, -1, &table_id );
980c2c66affSColin Finck     if( r != ERROR_SUCCESS )
981c2c66affSColin Finck     {
982c2c66affSColin Finck         TRACE("Couldn't find id for %s\n", debugstr_w(name));
983c2c66affSColin Finck         return FALSE;
984c2c66affSColin Finck     }
985c2c66affSColin Finck 
986c2c66affSColin Finck     r = get_table( db, szTables, &table );
987c2c66affSColin Finck     if( r != ERROR_SUCCESS )
988c2c66affSColin Finck     {
989c2c66affSColin Finck         ERR("table %s not available\n", debugstr_w(szTables));
990c2c66affSColin Finck         return FALSE;
991c2c66affSColin Finck     }
992c2c66affSColin Finck 
993c2c66affSColin Finck     for( i = 0; i < table->row_count; i++ )
994c2c66affSColin Finck     {
995c2c66affSColin Finck         if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id )
996c2c66affSColin Finck             return TRUE;
997c2c66affSColin Finck     }
998c2c66affSColin Finck 
999c2c66affSColin Finck     return FALSE;
1000c2c66affSColin Finck }
1001c2c66affSColin Finck 
1002c2c66affSColin Finck /* below is the query interface to a table */
1003c2c66affSColin Finck 
1004c2c66affSColin Finck typedef struct tagMSITABLEVIEW
1005c2c66affSColin Finck {
1006c2c66affSColin Finck     MSIVIEW        view;
1007c2c66affSColin Finck     MSIDATABASE   *db;
1008c2c66affSColin Finck     MSITABLE      *table;
1009c2c66affSColin Finck     MSICOLUMNINFO *columns;
1010c2c66affSColin Finck     UINT           num_cols;
1011c2c66affSColin Finck     UINT           row_size;
1012c2c66affSColin Finck     WCHAR          name[1];
1013c2c66affSColin Finck } MSITABLEVIEW;
1014c2c66affSColin Finck 
1015c2c66affSColin Finck static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1016c2c66affSColin Finck {
1017c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1018c2c66affSColin Finck     UINT offset, n;
1019c2c66affSColin Finck 
1020c2c66affSColin Finck     if( !tv->table )
1021c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1022c2c66affSColin Finck 
1023c2c66affSColin Finck     if( (col==0) || (col>tv->num_cols) )
1024c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1025c2c66affSColin Finck 
1026c2c66affSColin Finck     /* how many rows are there ? */
1027c2c66affSColin Finck     if( row >= tv->table->row_count )
1028c2c66affSColin Finck         return ERROR_NO_MORE_ITEMS;
1029c2c66affSColin Finck 
1030c2c66affSColin Finck     if( tv->columns[col-1].offset >= tv->row_size )
1031c2c66affSColin Finck     {
1032c2c66affSColin Finck         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1033c2c66affSColin Finck         ERR("%p %p\n", tv, tv->columns );
1034c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1035c2c66affSColin Finck     }
1036c2c66affSColin Finck 
1037c2c66affSColin Finck     n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1038c2c66affSColin Finck     if (n != 2 && n != 3 && n != 4)
1039c2c66affSColin Finck     {
1040c2c66affSColin Finck         ERR("oops! what is %d bytes per column?\n", n );
1041c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1042c2c66affSColin Finck     }
1043c2c66affSColin Finck 
1044c2c66affSColin Finck     offset = tv->columns[col-1].offset;
1045c2c66affSColin Finck     *val = read_table_int(tv->table->data, row, offset, n);
1046c2c66affSColin Finck 
1047c2c66affSColin Finck     /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1048c2c66affSColin Finck 
1049c2c66affSColin Finck     return ERROR_SUCCESS;
1050c2c66affSColin Finck }
1051c2c66affSColin Finck 
1052c2c66affSColin Finck static UINT get_stream_name( const MSITABLEVIEW *tv, UINT row, WCHAR **pstname )
1053c2c66affSColin Finck {
1054c2c66affSColin Finck     LPWSTR p, stname = NULL;
1055c2c66affSColin Finck     UINT i, r, type, ival;
1056c2c66affSColin Finck     DWORD len;
1057c2c66affSColin Finck     LPCWSTR sval;
1058c2c66affSColin Finck     MSIVIEW *view = (MSIVIEW *) tv;
1059c2c66affSColin Finck 
1060c2c66affSColin Finck     TRACE("%p %d\n", tv, row);
1061c2c66affSColin Finck 
1062c2c66affSColin Finck     len = lstrlenW( tv->name ) + 1;
1063c2c66affSColin Finck     stname = msi_alloc( len*sizeof(WCHAR) );
1064c2c66affSColin Finck     if ( !stname )
1065c2c66affSColin Finck     {
1066c2c66affSColin Finck        r = ERROR_OUTOFMEMORY;
1067c2c66affSColin Finck        goto err;
1068c2c66affSColin Finck     }
1069c2c66affSColin Finck 
1070c2c66affSColin Finck     lstrcpyW( stname, tv->name );
1071c2c66affSColin Finck 
1072c2c66affSColin Finck     for ( i = 0; i < tv->num_cols; i++ )
1073c2c66affSColin Finck     {
1074c2c66affSColin Finck         type = tv->columns[i].type;
1075c2c66affSColin Finck         if ( type & MSITYPE_KEY )
1076c2c66affSColin Finck         {
1077c2c66affSColin Finck             WCHAR number[0x20];
1078c2c66affSColin Finck 
1079c2c66affSColin Finck             r = TABLE_fetch_int( view, row, i+1, &ival );
1080c2c66affSColin Finck             if ( r != ERROR_SUCCESS )
1081c2c66affSColin Finck                 goto err;
1082c2c66affSColin Finck 
1083c2c66affSColin Finck             if ( tv->columns[i].type & MSITYPE_STRING )
1084c2c66affSColin Finck             {
1085c2c66affSColin Finck                 sval = msi_string_lookup( tv->db->strings, ival, NULL );
1086c2c66affSColin Finck                 if ( !sval )
1087c2c66affSColin Finck                 {
1088c2c66affSColin Finck                     r = ERROR_INVALID_PARAMETER;
1089c2c66affSColin Finck                     goto err;
1090c2c66affSColin Finck                 }
1091c2c66affSColin Finck             }
1092c2c66affSColin Finck             else
1093c2c66affSColin Finck             {
1094c2c66affSColin Finck                 static const WCHAR fmt[] = { '%','d',0 };
1095c2c66affSColin Finck                 UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES );
1096c2c66affSColin Finck 
1097c2c66affSColin Finck                 switch( n )
1098c2c66affSColin Finck                 {
1099c2c66affSColin Finck                 case 2:
1100c2c66affSColin Finck                     sprintfW( number, fmt, ival-0x8000 );
1101c2c66affSColin Finck                     break;
1102c2c66affSColin Finck                 case 4:
1103c2c66affSColin Finck                     sprintfW( number, fmt, ival^0x80000000 );
1104c2c66affSColin Finck                     break;
1105c2c66affSColin Finck                 default:
1106c2c66affSColin Finck                     ERR( "oops - unknown column width %d\n", n );
1107c2c66affSColin Finck                     r = ERROR_FUNCTION_FAILED;
1108c2c66affSColin Finck                     goto err;
1109c2c66affSColin Finck                 }
1110c2c66affSColin Finck                 sval = number;
1111c2c66affSColin Finck             }
1112c2c66affSColin Finck 
1113c2c66affSColin Finck             len += lstrlenW( szDot ) + lstrlenW( sval );
1114c2c66affSColin Finck             p = msi_realloc ( stname, len*sizeof(WCHAR) );
1115c2c66affSColin Finck             if ( !p )
1116c2c66affSColin Finck             {
1117c2c66affSColin Finck                 r = ERROR_OUTOFMEMORY;
1118c2c66affSColin Finck                 goto err;
1119c2c66affSColin Finck             }
1120c2c66affSColin Finck             stname = p;
1121c2c66affSColin Finck 
1122c2c66affSColin Finck             lstrcatW( stname, szDot );
1123c2c66affSColin Finck             lstrcatW( stname, sval );
1124c2c66affSColin Finck         }
1125c2c66affSColin Finck         else
1126c2c66affSColin Finck            continue;
1127c2c66affSColin Finck     }
1128c2c66affSColin Finck 
1129c2c66affSColin Finck     *pstname = stname;
1130c2c66affSColin Finck     return ERROR_SUCCESS;
1131c2c66affSColin Finck 
1132c2c66affSColin Finck err:
1133c2c66affSColin Finck     msi_free( stname );
1134c2c66affSColin Finck     *pstname = NULL;
1135c2c66affSColin Finck     return r;
1136c2c66affSColin Finck }
1137c2c66affSColin Finck 
1138c2c66affSColin Finck /*
1139c2c66affSColin Finck  * We need a special case for streams, as we need to reference column with
1140c2c66affSColin Finck  * the name of the stream in the same table, and the table name
1141c2c66affSColin Finck  * which may not be available at higher levels of the query
1142c2c66affSColin Finck  */
1143c2c66affSColin Finck static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
1144c2c66affSColin Finck {
1145c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1146c2c66affSColin Finck     UINT r;
1147c2c66affSColin Finck     WCHAR *name;
1148c2c66affSColin Finck 
1149c2c66affSColin Finck     if( !view->ops->fetch_int )
1150c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1151c2c66affSColin Finck 
1152c2c66affSColin Finck     r = get_stream_name( tv, row, &name );
1153c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1154c2c66affSColin Finck     {
1155c2c66affSColin Finck         ERR("fetching stream, error = %u\n", r);
1156c2c66affSColin Finck         return r;
1157c2c66affSColin Finck     }
1158c2c66affSColin Finck 
1159c2c66affSColin Finck     r = msi_get_stream( tv->db, name, stm );
1160c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1161c2c66affSColin Finck         ERR("fetching stream %s, error = %u\n", debugstr_w(name), r);
1162c2c66affSColin Finck 
1163c2c66affSColin Finck     msi_free( name );
1164c2c66affSColin Finck     return r;
1165c2c66affSColin Finck }
1166c2c66affSColin Finck 
1167c2c66affSColin Finck static UINT TABLE_set_int( MSITABLEVIEW *tv, UINT row, UINT col, UINT val )
1168c2c66affSColin Finck {
1169c2c66affSColin Finck     UINT offset, n, i;
1170c2c66affSColin Finck 
1171c2c66affSColin Finck     if( !tv->table )
1172c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1173c2c66affSColin Finck 
1174c2c66affSColin Finck     if( (col==0) || (col>tv->num_cols) )
1175c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1176c2c66affSColin Finck 
1177c2c66affSColin Finck     if( row >= tv->table->row_count )
1178c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1179c2c66affSColin Finck 
1180c2c66affSColin Finck     if( tv->columns[col-1].offset >= tv->row_size )
1181c2c66affSColin Finck     {
1182c2c66affSColin Finck         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1183c2c66affSColin Finck         ERR("%p %p\n", tv, tv->columns );
1184c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1185c2c66affSColin Finck     }
1186c2c66affSColin Finck 
1187c2c66affSColin Finck     msi_free( tv->columns[col-1].hash_table );
1188c2c66affSColin Finck     tv->columns[col-1].hash_table = NULL;
1189c2c66affSColin Finck 
1190c2c66affSColin Finck     n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1191c2c66affSColin Finck     if ( n != 2 && n != 3 && n != 4 )
1192c2c66affSColin Finck     {
1193c2c66affSColin Finck         ERR("oops! what is %d bytes per column?\n", n );
1194c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1195c2c66affSColin Finck     }
1196c2c66affSColin Finck 
1197c2c66affSColin Finck     offset = tv->columns[col-1].offset;
1198c2c66affSColin Finck     for ( i = 0; i < n; i++ )
1199c2c66affSColin Finck         tv->table->data[row][offset + i] = (val >> i * 8) & 0xff;
1200c2c66affSColin Finck 
1201c2c66affSColin Finck     return ERROR_SUCCESS;
1202c2c66affSColin Finck }
1203c2c66affSColin Finck 
1204c2c66affSColin Finck static UINT TABLE_get_row( struct tagMSIVIEW *view, UINT row, MSIRECORD **rec )
1205c2c66affSColin Finck {
1206c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1207c2c66affSColin Finck 
1208c2c66affSColin Finck     if (!tv->table)
1209c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1210c2c66affSColin Finck 
1211c2c66affSColin Finck     return msi_view_get_row(tv->db, view, row, rec);
1212c2c66affSColin Finck }
1213c2c66affSColin Finck 
1214c2c66affSColin Finck static UINT add_stream( MSIDATABASE *db, const WCHAR *name, IStream *data )
1215c2c66affSColin Finck {
1216c2c66affSColin Finck     static const WCHAR insert[] = {
1217c2c66affSColin Finck         'I','N','S','E','R','T',' ','I','N','T','O',' ',
1218c2c66affSColin Finck         '`','_','S','t','r','e','a','m','s','`',' ',
1219c2c66affSColin Finck         '(','`','N','a','m','e','`',',','`','D','a','t','a','`',')',' ',
1220c2c66affSColin Finck         'V','A','L','U','E','S',' ','(','?',',','?',')',0};
1221c2c66affSColin Finck     static const WCHAR update[] = {
1222c2c66affSColin Finck         'U','P','D','A','T','E',' ','`','_','S','t','r','e','a','m','s','`',' ',
1223c2c66affSColin Finck         'S','E','T',' ','`','D','a','t','a','`',' ','=',' ','?',' ',
1224c2c66affSColin Finck         'W','H','E','R','E',' ','`','N','a','m','e','`',' ','=',' ','?',0};
1225c2c66affSColin Finck     MSIQUERY *query;
1226c2c66affSColin Finck     MSIRECORD *rec;
1227c2c66affSColin Finck     UINT r;
1228c2c66affSColin Finck 
1229c2c66affSColin Finck     TRACE("%p %s %p\n", db, debugstr_w(name), data);
1230c2c66affSColin Finck 
1231c2c66affSColin Finck     if (!(rec = MSI_CreateRecord( 2 )))
1232c2c66affSColin Finck         return ERROR_OUTOFMEMORY;
1233c2c66affSColin Finck 
1234c2c66affSColin Finck     r = MSI_RecordSetStringW( rec, 1, name );
1235c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1236c2c66affSColin Finck        goto done;
1237c2c66affSColin Finck 
1238c2c66affSColin Finck     r = MSI_RecordSetIStream( rec, 2, data );
1239c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1240c2c66affSColin Finck        goto done;
1241c2c66affSColin Finck 
1242c2c66affSColin Finck     r = MSI_DatabaseOpenViewW( db, insert, &query );
1243c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1244c2c66affSColin Finck        goto done;
1245c2c66affSColin Finck 
1246c2c66affSColin Finck     r = MSI_ViewExecute( query, rec );
1247c2c66affSColin Finck     msiobj_release( &query->hdr );
1248c2c66affSColin Finck     if (r == ERROR_SUCCESS)
1249c2c66affSColin Finck         goto done;
1250c2c66affSColin Finck 
1251c2c66affSColin Finck     msiobj_release( &rec->hdr );
1252c2c66affSColin Finck     if (!(rec = MSI_CreateRecord( 2 )))
1253c2c66affSColin Finck         return ERROR_OUTOFMEMORY;
1254c2c66affSColin Finck 
1255c2c66affSColin Finck     r = MSI_RecordSetIStream( rec, 1, data );
1256c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1257c2c66affSColin Finck        goto done;
1258c2c66affSColin Finck 
1259c2c66affSColin Finck     r = MSI_RecordSetStringW( rec, 2, name );
1260c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1261c2c66affSColin Finck        goto done;
1262c2c66affSColin Finck 
1263c2c66affSColin Finck     r = MSI_DatabaseOpenViewW( db, update, &query );
1264c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1265c2c66affSColin Finck         goto done;
1266c2c66affSColin Finck 
1267c2c66affSColin Finck     r = MSI_ViewExecute( query, rec );
1268c2c66affSColin Finck     msiobj_release( &query->hdr );
1269c2c66affSColin Finck 
1270c2c66affSColin Finck done:
1271c2c66affSColin Finck     msiobj_release( &rec->hdr );
1272c2c66affSColin Finck     return r;
1273c2c66affSColin Finck }
1274c2c66affSColin Finck 
1275c2c66affSColin Finck static UINT get_table_value_from_record( MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue )
1276c2c66affSColin Finck {
1277c2c66affSColin Finck     MSICOLUMNINFO columninfo;
1278c2c66affSColin Finck     UINT r;
1279c2c66affSColin Finck     int ival;
1280c2c66affSColin Finck 
1281306890e8Swinesync     if (!iField || iField > tv->num_cols || MSI_RecordIsNull( rec, iField ))
1282c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1283c2c66affSColin Finck 
1284c2c66affSColin Finck     columninfo = tv->columns[ iField - 1 ];
1285c2c66affSColin Finck 
1286c2c66affSColin Finck     if ( MSITYPE_IS_BINARY(columninfo.type) )
1287c2c66affSColin Finck     {
1288c2c66affSColin Finck         *pvalue = 1; /* refers to the first key column */
1289c2c66affSColin Finck     }
1290c2c66affSColin Finck     else if ( columninfo.type & MSITYPE_STRING )
1291c2c66affSColin Finck     {
1292c2c66affSColin Finck         int len;
1293c2c66affSColin Finck         const WCHAR *sval = msi_record_get_string( rec, iField, &len );
1294c2c66affSColin Finck         if (sval)
1295c2c66affSColin Finck         {
1296c2c66affSColin Finck             r = msi_string2id( tv->db->strings, sval, len, pvalue );
1297c2c66affSColin Finck             if (r != ERROR_SUCCESS)
1298c2c66affSColin Finck                 return ERROR_NOT_FOUND;
1299c2c66affSColin Finck         }
1300c2c66affSColin Finck         else *pvalue = 0;
1301c2c66affSColin Finck     }
1302c2c66affSColin Finck     else if ( bytes_per_column( tv->db, &columninfo, LONG_STR_BYTES ) == 2 )
1303c2c66affSColin Finck     {
1304c2c66affSColin Finck         ival = MSI_RecordGetInteger( rec, iField );
1305c2c66affSColin Finck         if (ival == 0x80000000) *pvalue = 0x8000;
1306c2c66affSColin Finck         else
1307c2c66affSColin Finck         {
1308c2c66affSColin Finck             *pvalue = 0x8000 + MSI_RecordGetInteger( rec, iField );
1309c2c66affSColin Finck             if (*pvalue & 0xffff0000)
1310c2c66affSColin Finck             {
1311c2c66affSColin Finck                 ERR("field %u value %d out of range\n", iField, *pvalue - 0x8000);
1312c2c66affSColin Finck                 return ERROR_FUNCTION_FAILED;
1313c2c66affSColin Finck             }
1314c2c66affSColin Finck         }
1315c2c66affSColin Finck     }
1316c2c66affSColin Finck     else
1317c2c66affSColin Finck     {
1318c2c66affSColin Finck         ival = MSI_RecordGetInteger( rec, iField );
1319c2c66affSColin Finck         *pvalue = ival ^ 0x80000000;
1320c2c66affSColin Finck     }
1321c2c66affSColin Finck 
1322c2c66affSColin Finck     return ERROR_SUCCESS;
1323c2c66affSColin Finck }
1324c2c66affSColin Finck 
1325c2c66affSColin Finck static UINT TABLE_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask )
1326c2c66affSColin Finck {
1327c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1328c2c66affSColin Finck     UINT i, val, r = ERROR_SUCCESS;
1329c2c66affSColin Finck 
1330c2c66affSColin Finck     if ( !tv->table )
1331c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1332c2c66affSColin Finck 
1333c2c66affSColin Finck     /* test if any of the mask bits are invalid */
1334c2c66affSColin Finck     if ( mask >= (1<<tv->num_cols) )
1335c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1336c2c66affSColin Finck 
1337c2c66affSColin Finck     for ( i = 0; i < tv->num_cols; i++ )
1338c2c66affSColin Finck     {
1339c2c66affSColin Finck         BOOL persistent;
1340c2c66affSColin Finck 
1341c2c66affSColin Finck         /* only update the fields specified in the mask */
1342c2c66affSColin Finck         if ( !(mask&(1<<i)) )
1343c2c66affSColin Finck             continue;
1344c2c66affSColin Finck 
1345c2c66affSColin Finck         persistent = (tv->table->persistent != MSICONDITION_FALSE) &&
1346c2c66affSColin Finck                      (tv->table->data_persistent[row]);
1347c2c66affSColin Finck         /* FIXME: should we allow updating keys? */
1348c2c66affSColin Finck 
1349c2c66affSColin Finck         val = 0;
1350c2c66affSColin Finck         if ( !MSI_RecordIsNull( rec, i + 1 ) )
1351c2c66affSColin Finck         {
1352c2c66affSColin Finck             r = get_table_value_from_record (tv, rec, i + 1, &val);
1353c2c66affSColin Finck             if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
1354c2c66affSColin Finck             {
1355c2c66affSColin Finck                 IStream *stm;
1356c2c66affSColin Finck                 LPWSTR stname;
1357c2c66affSColin Finck 
1358c2c66affSColin Finck                 if ( r != ERROR_SUCCESS )
1359c2c66affSColin Finck                     return ERROR_FUNCTION_FAILED;
1360c2c66affSColin Finck 
1361c2c66affSColin Finck                 r = MSI_RecordGetIStream( rec, i + 1, &stm );
1362c2c66affSColin Finck                 if ( r != ERROR_SUCCESS )
1363c2c66affSColin Finck                     return r;
1364c2c66affSColin Finck 
1365c2c66affSColin Finck                 r = get_stream_name( tv, row, &stname );
1366c2c66affSColin Finck                 if ( r != ERROR_SUCCESS )
1367c2c66affSColin Finck                 {
1368c2c66affSColin Finck                     IStream_Release( stm );
1369c2c66affSColin Finck                     return r;
1370c2c66affSColin Finck                 }
1371c2c66affSColin Finck 
1372c2c66affSColin Finck                 r = add_stream( tv->db, stname, stm );
1373c2c66affSColin Finck                 IStream_Release( stm );
1374c2c66affSColin Finck                 msi_free ( stname );
1375c2c66affSColin Finck 
1376c2c66affSColin Finck                 if ( r != ERROR_SUCCESS )
1377c2c66affSColin Finck                     return r;
1378c2c66affSColin Finck             }
1379c2c66affSColin Finck             else if ( tv->columns[i].type & MSITYPE_STRING )
1380c2c66affSColin Finck             {
1381c2c66affSColin Finck                 UINT x;
1382c2c66affSColin Finck 
1383c2c66affSColin Finck                 if ( r != ERROR_SUCCESS )
1384c2c66affSColin Finck                 {
1385c2c66affSColin Finck                     int len;
1386c2c66affSColin Finck                     const WCHAR *sval = msi_record_get_string( rec, i + 1, &len );
1387*211c4104Swinesync                     val = msi_add_string( tv->db->strings, sval, len, persistent );
1388c2c66affSColin Finck                 }
1389c2c66affSColin Finck                 else
1390c2c66affSColin Finck                 {
1391c2c66affSColin Finck                     TABLE_fetch_int(&tv->view, row, i + 1, &x);
1392c2c66affSColin Finck                     if (val == x)
1393c2c66affSColin Finck                         continue;
1394c2c66affSColin Finck                 }
1395c2c66affSColin Finck             }
1396c2c66affSColin Finck             else
1397c2c66affSColin Finck             {
1398c2c66affSColin Finck                 if ( r != ERROR_SUCCESS )
1399c2c66affSColin Finck                     return ERROR_FUNCTION_FAILED;
1400c2c66affSColin Finck             }
1401c2c66affSColin Finck         }
1402c2c66affSColin Finck 
1403c2c66affSColin Finck         r = TABLE_set_int( tv, row, i+1, val );
1404c2c66affSColin Finck         if ( r != ERROR_SUCCESS )
1405c2c66affSColin Finck             break;
1406c2c66affSColin Finck     }
1407c2c66affSColin Finck     return r;
1408c2c66affSColin Finck }
1409c2c66affSColin Finck 
1410c2c66affSColin Finck static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary )
1411c2c66affSColin Finck {
1412c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1413c2c66affSColin Finck     BYTE **p, *row;
1414c2c66affSColin Finck     BOOL *b;
1415c2c66affSColin Finck     UINT sz;
1416c2c66affSColin Finck     BYTE ***data_ptr;
1417c2c66affSColin Finck     BOOL **data_persist_ptr;
1418c2c66affSColin Finck     UINT *row_count;
1419c2c66affSColin Finck 
1420c2c66affSColin Finck     TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE");
1421c2c66affSColin Finck 
1422c2c66affSColin Finck     if( !tv->table )
1423c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1424c2c66affSColin Finck 
1425c2c66affSColin Finck     row = msi_alloc_zero( tv->row_size );
1426c2c66affSColin Finck     if( !row )
1427c2c66affSColin Finck         return ERROR_NOT_ENOUGH_MEMORY;
1428c2c66affSColin Finck 
1429c2c66affSColin Finck     row_count = &tv->table->row_count;
1430c2c66affSColin Finck     data_ptr = &tv->table->data;
1431c2c66affSColin Finck     data_persist_ptr = &tv->table->data_persistent;
1432c2c66affSColin Finck     if (*num == -1)
1433c2c66affSColin Finck         *num = tv->table->row_count;
1434c2c66affSColin Finck 
1435c2c66affSColin Finck     sz = (*row_count + 1) * sizeof (BYTE*);
1436c2c66affSColin Finck     if( *data_ptr )
1437c2c66affSColin Finck         p = msi_realloc( *data_ptr, sz );
1438c2c66affSColin Finck     else
1439c2c66affSColin Finck         p = msi_alloc( sz );
1440c2c66affSColin Finck     if( !p )
1441c2c66affSColin Finck     {
1442c2c66affSColin Finck         msi_free( row );
1443c2c66affSColin Finck         return ERROR_NOT_ENOUGH_MEMORY;
1444c2c66affSColin Finck     }
1445c2c66affSColin Finck 
1446c2c66affSColin Finck     sz = (*row_count + 1) * sizeof (BOOL);
1447c2c66affSColin Finck     if( *data_persist_ptr )
1448c2c66affSColin Finck         b = msi_realloc( *data_persist_ptr, sz );
1449c2c66affSColin Finck     else
1450c2c66affSColin Finck         b = msi_alloc( sz );
1451c2c66affSColin Finck     if( !b )
1452c2c66affSColin Finck     {
1453c2c66affSColin Finck         msi_free( row );
1454c2c66affSColin Finck         msi_free( p );
1455c2c66affSColin Finck         return ERROR_NOT_ENOUGH_MEMORY;
1456c2c66affSColin Finck     }
1457c2c66affSColin Finck 
1458c2c66affSColin Finck     *data_ptr = p;
1459c2c66affSColin Finck     (*data_ptr)[*row_count] = row;
1460c2c66affSColin Finck 
1461c2c66affSColin Finck     *data_persist_ptr = b;
1462c2c66affSColin Finck     (*data_persist_ptr)[*row_count] = !temporary;
1463c2c66affSColin Finck 
1464c2c66affSColin Finck     (*row_count)++;
1465c2c66affSColin Finck 
1466c2c66affSColin Finck     return ERROR_SUCCESS;
1467c2c66affSColin Finck }
1468c2c66affSColin Finck 
1469c2c66affSColin Finck static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
1470c2c66affSColin Finck {
1471c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1472c2c66affSColin Finck 
1473c2c66affSColin Finck     TRACE("%p %p\n", tv, record);
1474c2c66affSColin Finck 
1475c2c66affSColin Finck     TRACE("There are %d columns\n", tv->num_cols );
1476c2c66affSColin Finck 
1477c2c66affSColin Finck     return ERROR_SUCCESS;
1478c2c66affSColin Finck }
1479c2c66affSColin Finck 
1480c2c66affSColin Finck static UINT TABLE_close( struct tagMSIVIEW *view )
1481c2c66affSColin Finck {
1482c2c66affSColin Finck     TRACE("%p\n", view );
1483c2c66affSColin Finck 
1484c2c66affSColin Finck     return ERROR_SUCCESS;
1485c2c66affSColin Finck }
1486c2c66affSColin Finck 
1487c2c66affSColin Finck static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1488c2c66affSColin Finck {
1489c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1490c2c66affSColin Finck 
1491c2c66affSColin Finck     TRACE("%p %p %p\n", view, rows, cols );
1492c2c66affSColin Finck 
1493c2c66affSColin Finck     if( cols )
1494c2c66affSColin Finck         *cols = tv->num_cols;
1495c2c66affSColin Finck     if( rows )
1496c2c66affSColin Finck     {
1497c2c66affSColin Finck         if( !tv->table )
1498c2c66affSColin Finck             return ERROR_INVALID_PARAMETER;
1499c2c66affSColin Finck         *rows = tv->table->row_count;
1500c2c66affSColin Finck     }
1501c2c66affSColin Finck 
1502c2c66affSColin Finck     return ERROR_SUCCESS;
1503c2c66affSColin Finck }
1504c2c66affSColin Finck 
1505c2c66affSColin Finck static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1506c2c66affSColin Finck                 UINT n, LPCWSTR *name, UINT *type, BOOL *temporary,
1507c2c66affSColin Finck                 LPCWSTR *table_name )
1508c2c66affSColin Finck {
1509c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1510c2c66affSColin Finck 
1511c2c66affSColin Finck     TRACE("%p %d %p %p\n", tv, n, name, type );
1512c2c66affSColin Finck 
1513c2c66affSColin Finck     if( ( n == 0 ) || ( n > tv->num_cols ) )
1514c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1515c2c66affSColin Finck 
1516c2c66affSColin Finck     if( name )
1517c2c66affSColin Finck     {
1518c2c66affSColin Finck         *name = tv->columns[n-1].colname;
1519c2c66affSColin Finck         if( !*name )
1520c2c66affSColin Finck             return ERROR_FUNCTION_FAILED;
1521c2c66affSColin Finck     }
1522c2c66affSColin Finck 
1523c2c66affSColin Finck     if( table_name )
1524c2c66affSColin Finck     {
1525c2c66affSColin Finck         *table_name = tv->columns[n-1].tablename;
1526c2c66affSColin Finck         if( !*table_name )
1527c2c66affSColin Finck             return ERROR_FUNCTION_FAILED;
1528c2c66affSColin Finck     }
1529c2c66affSColin Finck 
1530c2c66affSColin Finck     if( type )
1531c2c66affSColin Finck         *type = tv->columns[n-1].type;
1532c2c66affSColin Finck 
1533c2c66affSColin Finck     if( temporary )
1534c2c66affSColin Finck         *temporary = tv->columns[n-1].temporary;
1535c2c66affSColin Finck 
1536c2c66affSColin Finck     return ERROR_SUCCESS;
1537c2c66affSColin Finck }
1538c2c66affSColin Finck 
1539c2c66affSColin Finck static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column );
1540c2c66affSColin Finck 
1541c2c66affSColin Finck static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column )
1542c2c66affSColin Finck {
1543c2c66affSColin Finck     UINT r, row, i;
1544c2c66affSColin Finck 
1545c2c66affSColin Finck     /* check there are no null values where they're not allowed */
1546c2c66affSColin Finck     for( i = 0; i < tv->num_cols; i++ )
1547c2c66affSColin Finck     {
1548c2c66affSColin Finck         if ( tv->columns[i].type & MSITYPE_NULLABLE )
1549c2c66affSColin Finck             continue;
1550c2c66affSColin Finck 
1551c2c66affSColin Finck         if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
1552c2c66affSColin Finck             TRACE("skipping binary column\n");
1553c2c66affSColin Finck         else if ( tv->columns[i].type & MSITYPE_STRING )
1554c2c66affSColin Finck         {
1555c2c66affSColin Finck             int len;
1556c2c66affSColin Finck             const WCHAR *str = msi_record_get_string( rec, i+1, &len );
1557c2c66affSColin Finck 
1558c2c66affSColin Finck             if (!str || (!str[0] && !len))
1559c2c66affSColin Finck             {
1560c2c66affSColin Finck                 if (column) *column = i;
1561c2c66affSColin Finck                 return ERROR_INVALID_DATA;
1562c2c66affSColin Finck             }
1563c2c66affSColin Finck         }
1564c2c66affSColin Finck         else
1565c2c66affSColin Finck         {
1566c2c66affSColin Finck             UINT n;
1567c2c66affSColin Finck 
1568c2c66affSColin Finck             n = MSI_RecordGetInteger( rec, i+1 );
1569c2c66affSColin Finck             if (n == MSI_NULL_INTEGER)
1570c2c66affSColin Finck             {
1571c2c66affSColin Finck                 if (column) *column = i;
1572c2c66affSColin Finck                 return ERROR_INVALID_DATA;
1573c2c66affSColin Finck             }
1574c2c66affSColin Finck         }
1575c2c66affSColin Finck     }
1576c2c66affSColin Finck 
1577c2c66affSColin Finck     /* check there are no duplicate keys */
1578c2c66affSColin Finck     r = msi_table_find_row( tv, rec, &row, column );
1579c2c66affSColin Finck     if (r == ERROR_SUCCESS)
1580c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1581c2c66affSColin Finck 
1582c2c66affSColin Finck     return ERROR_SUCCESS;
1583c2c66affSColin Finck }
1584c2c66affSColin Finck 
1585c2c66affSColin Finck static int compare_record( MSITABLEVIEW *tv, UINT row, MSIRECORD *rec )
1586c2c66affSColin Finck {
1587c2c66affSColin Finck     UINT r, i, ivalue, x;
1588c2c66affSColin Finck 
1589c2c66affSColin Finck     for (i = 0; i < tv->num_cols; i++ )
1590c2c66affSColin Finck     {
1591c2c66affSColin Finck         if (!(tv->columns[i].type & MSITYPE_KEY)) continue;
1592c2c66affSColin Finck 
1593c2c66affSColin Finck         r = get_table_value_from_record( tv, rec, i + 1, &ivalue );
1594c2c66affSColin Finck         if (r != ERROR_SUCCESS)
1595c2c66affSColin Finck             return 1;
1596c2c66affSColin Finck 
1597c2c66affSColin Finck         r = TABLE_fetch_int( &tv->view, row, i + 1, &x );
1598c2c66affSColin Finck         if (r != ERROR_SUCCESS)
1599c2c66affSColin Finck         {
1600c2c66affSColin Finck             WARN("TABLE_fetch_int should not fail here %u\n", r);
1601c2c66affSColin Finck             return -1;
1602c2c66affSColin Finck         }
1603c2c66affSColin Finck         if (ivalue > x)
1604c2c66affSColin Finck         {
1605c2c66affSColin Finck             return 1;
1606c2c66affSColin Finck         }
1607c2c66affSColin Finck         else if (ivalue == x)
1608c2c66affSColin Finck         {
1609c2c66affSColin Finck             if (i < tv->num_cols - 1) continue;
1610c2c66affSColin Finck             return 0;
1611c2c66affSColin Finck         }
1612c2c66affSColin Finck         else
1613c2c66affSColin Finck             return -1;
1614c2c66affSColin Finck     }
1615c2c66affSColin Finck     return 1;
1616c2c66affSColin Finck }
1617c2c66affSColin Finck 
1618c2c66affSColin Finck static int find_insert_index( MSITABLEVIEW *tv, MSIRECORD *rec )
1619c2c66affSColin Finck {
1620c2c66affSColin Finck     int idx, c, low = 0, high = tv->table->row_count - 1;
1621c2c66affSColin Finck 
1622c2c66affSColin Finck     TRACE("%p %p\n", tv, rec);
1623c2c66affSColin Finck 
1624c2c66affSColin Finck     while (low <= high)
1625c2c66affSColin Finck     {
1626c2c66affSColin Finck         idx = (low + high) / 2;
1627c2c66affSColin Finck         c = compare_record( tv, idx, rec );
1628c2c66affSColin Finck 
1629c2c66affSColin Finck         if (c < 0)
1630c2c66affSColin Finck             high = idx - 1;
1631c2c66affSColin Finck         else if (c > 0)
1632c2c66affSColin Finck             low = idx + 1;
1633c2c66affSColin Finck         else
1634c2c66affSColin Finck         {
1635c2c66affSColin Finck             TRACE("found %u\n", idx);
1636c2c66affSColin Finck             return idx;
1637c2c66affSColin Finck         }
1638c2c66affSColin Finck     }
1639c2c66affSColin Finck     TRACE("found %u\n", high + 1);
1640c2c66affSColin Finck     return high + 1;
1641c2c66affSColin Finck }
1642c2c66affSColin Finck 
1643c2c66affSColin Finck static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
1644c2c66affSColin Finck {
1645c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1646c2c66affSColin Finck     UINT i, r;
1647c2c66affSColin Finck 
1648c2c66affSColin Finck     TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" );
1649c2c66affSColin Finck 
1650c2c66affSColin Finck     /* check that the key is unique - can we find a matching row? */
1651c2c66affSColin Finck     r = table_validate_new( tv, rec, NULL );
1652c2c66affSColin Finck     if( r != ERROR_SUCCESS )
1653c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1654c2c66affSColin Finck 
1655c2c66affSColin Finck     if (row == -1)
1656c2c66affSColin Finck         row = find_insert_index( tv, rec );
1657c2c66affSColin Finck 
1658c2c66affSColin Finck     r = table_create_new_row( view, &row, temporary );
1659c2c66affSColin Finck     TRACE("insert_row returned %08x\n", r);
1660c2c66affSColin Finck     if( r != ERROR_SUCCESS )
1661c2c66affSColin Finck         return r;
1662c2c66affSColin Finck 
1663c2c66affSColin Finck     /* shift the rows to make room for the new row */
1664c2c66affSColin Finck     for (i = tv->table->row_count - 1; i > row; i--)
1665c2c66affSColin Finck     {
1666c2c66affSColin Finck         memmove(&(tv->table->data[i][0]),
1667c2c66affSColin Finck                 &(tv->table->data[i - 1][0]), tv->row_size);
1668c2c66affSColin Finck         tv->table->data_persistent[i] = tv->table->data_persistent[i - 1];
1669c2c66affSColin Finck     }
1670c2c66affSColin Finck 
1671c2c66affSColin Finck     /* Re-set the persistence flag */
1672c2c66affSColin Finck     tv->table->data_persistent[row] = !temporary;
1673c2c66affSColin Finck     return TABLE_set_row( view, row, rec, (1<<tv->num_cols) - 1 );
1674c2c66affSColin Finck }
1675c2c66affSColin Finck 
1676c2c66affSColin Finck static UINT TABLE_delete_row( struct tagMSIVIEW *view, UINT row )
1677c2c66affSColin Finck {
1678c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1679c2c66affSColin Finck     UINT r, num_rows, num_cols, i;
1680c2c66affSColin Finck 
1681c2c66affSColin Finck     TRACE("%p %d\n", tv, row);
1682c2c66affSColin Finck 
1683c2c66affSColin Finck     if ( !tv->table )
1684c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1685c2c66affSColin Finck 
1686c2c66affSColin Finck     r = TABLE_get_dimensions( view, &num_rows, &num_cols );
1687c2c66affSColin Finck     if ( r != ERROR_SUCCESS )
1688c2c66affSColin Finck         return r;
1689c2c66affSColin Finck 
1690c2c66affSColin Finck     if ( row >= num_rows )
1691c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1692c2c66affSColin Finck 
1693c2c66affSColin Finck     num_rows = tv->table->row_count;
1694c2c66affSColin Finck     tv->table->row_count--;
1695c2c66affSColin Finck 
1696c2c66affSColin Finck     /* reset the hash tables */
1697c2c66affSColin Finck     for (i = 0; i < tv->num_cols; i++)
1698c2c66affSColin Finck     {
1699c2c66affSColin Finck         msi_free( tv->columns[i].hash_table );
1700c2c66affSColin Finck         tv->columns[i].hash_table = NULL;
1701c2c66affSColin Finck     }
1702c2c66affSColin Finck 
1703c2c66affSColin Finck     for (i = row + 1; i < num_rows; i++)
1704c2c66affSColin Finck     {
1705c2c66affSColin Finck         memcpy(tv->table->data[i - 1], tv->table->data[i], tv->row_size);
1706c2c66affSColin Finck         tv->table->data_persistent[i - 1] = tv->table->data_persistent[i];
1707c2c66affSColin Finck     }
1708c2c66affSColin Finck 
1709c2c66affSColin Finck     msi_free(tv->table->data[num_rows - 1]);
1710c2c66affSColin Finck 
1711c2c66affSColin Finck     return ERROR_SUCCESS;
1712c2c66affSColin Finck }
1713c2c66affSColin Finck 
1714c2c66affSColin Finck static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row)
1715c2c66affSColin Finck {
1716c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1717c2c66affSColin Finck     UINT r, new_row;
1718c2c66affSColin Finck 
1719c2c66affSColin Finck     /* FIXME: MsiViewFetch should set rec index 0 to some ID that
1720c2c66affSColin Finck      * sets the fetched record apart from other records
1721c2c66affSColin Finck      */
1722c2c66affSColin Finck 
1723c2c66affSColin Finck     if (!tv->table)
1724c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1725c2c66affSColin Finck 
1726c2c66affSColin Finck     r = msi_table_find_row(tv, rec, &new_row, NULL);
1727c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1728c2c66affSColin Finck     {
1729c2c66affSColin Finck         ERR("can't find row to modify\n");
1730c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1731c2c66affSColin Finck     }
1732c2c66affSColin Finck 
1733c2c66affSColin Finck     /* the row cannot be changed */
1734c2c66affSColin Finck     if (row != new_row + 1)
1735c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
1736c2c66affSColin Finck 
1737c2c66affSColin Finck     return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1);
1738c2c66affSColin Finck }
1739c2c66affSColin Finck 
1740c2c66affSColin Finck static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
1741c2c66affSColin Finck {
1742c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1743c2c66affSColin Finck     UINT r, row;
1744c2c66affSColin Finck 
1745c2c66affSColin Finck     if (!tv->table)
1746c2c66affSColin Finck         return ERROR_INVALID_PARAMETER;
1747c2c66affSColin Finck 
1748c2c66affSColin Finck     r = msi_table_find_row(tv, rec, &row, NULL);
1749c2c66affSColin Finck     if (r == ERROR_SUCCESS)
1750c2c66affSColin Finck         return TABLE_set_row(view, row, rec, (1 << tv->num_cols) - 1);
1751c2c66affSColin Finck     else
1752c2c66affSColin Finck         return TABLE_insert_row( view, rec, -1, FALSE );
1753c2c66affSColin Finck }
1754c2c66affSColin Finck 
1755c2c66affSColin Finck static UINT modify_delete_row( struct tagMSIVIEW *view, MSIRECORD *rec )
1756c2c66affSColin Finck {
1757c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW *)view;
1758c2c66affSColin Finck     UINT row, r;
1759c2c66affSColin Finck 
1760c2c66affSColin Finck     r = msi_table_find_row(tv, rec, &row, NULL);
1761c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1762c2c66affSColin Finck         return r;
1763c2c66affSColin Finck 
1764c2c66affSColin Finck     return TABLE_delete_row(view, row);
1765c2c66affSColin Finck }
1766c2c66affSColin Finck 
1767c2c66affSColin Finck static UINT msi_refresh_record( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row )
1768c2c66affSColin Finck {
1769c2c66affSColin Finck     MSIRECORD *curr;
1770c2c66affSColin Finck     UINT r, i, count;
1771c2c66affSColin Finck 
1772c2c66affSColin Finck     r = TABLE_get_row(view, row - 1, &curr);
1773c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1774c2c66affSColin Finck         return r;
1775c2c66affSColin Finck 
1776c2c66affSColin Finck     /* Close the original record */
1777c2c66affSColin Finck     MSI_CloseRecord(&rec->hdr);
1778c2c66affSColin Finck 
1779c2c66affSColin Finck     count = MSI_RecordGetFieldCount(rec);
1780c2c66affSColin Finck     for (i = 0; i < count; i++)
1781c2c66affSColin Finck         MSI_RecordCopyField(curr, i + 1, rec, i + 1);
1782c2c66affSColin Finck 
1783c2c66affSColin Finck     msiobj_release(&curr->hdr);
1784c2c66affSColin Finck     return ERROR_SUCCESS;
1785c2c66affSColin Finck }
1786c2c66affSColin Finck 
1787c2c66affSColin Finck static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1788c2c66affSColin Finck                           MSIRECORD *rec, UINT row)
1789c2c66affSColin Finck {
1790c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1791c2c66affSColin Finck     UINT r, frow, column;
1792c2c66affSColin Finck 
1793c2c66affSColin Finck     TRACE("%p %d %p\n", view, eModifyMode, rec );
1794c2c66affSColin Finck 
1795c2c66affSColin Finck     switch (eModifyMode)
1796c2c66affSColin Finck     {
1797c2c66affSColin Finck     case MSIMODIFY_DELETE:
1798c2c66affSColin Finck         r = modify_delete_row( view, rec );
1799c2c66affSColin Finck         break;
1800c2c66affSColin Finck     case MSIMODIFY_VALIDATE_NEW:
1801c2c66affSColin Finck         r = table_validate_new( tv, rec, &column );
1802c2c66affSColin Finck         if (r != ERROR_SUCCESS)
1803c2c66affSColin Finck         {
1804c2c66affSColin Finck             tv->view.error = MSIDBERROR_DUPLICATEKEY;
1805c2c66affSColin Finck             tv->view.error_column = tv->columns[column].colname;
1806c2c66affSColin Finck             r = ERROR_INVALID_DATA;
1807c2c66affSColin Finck         }
1808c2c66affSColin Finck         break;
1809c2c66affSColin Finck 
1810c2c66affSColin Finck     case MSIMODIFY_INSERT:
1811c2c66affSColin Finck         r = table_validate_new( tv, rec, NULL );
1812c2c66affSColin Finck         if (r != ERROR_SUCCESS)
1813c2c66affSColin Finck             break;
1814c2c66affSColin Finck         r = TABLE_insert_row( view, rec, -1, FALSE );
1815c2c66affSColin Finck         break;
1816c2c66affSColin Finck 
1817c2c66affSColin Finck     case MSIMODIFY_INSERT_TEMPORARY:
1818c2c66affSColin Finck         r = table_validate_new( tv, rec, NULL );
1819c2c66affSColin Finck         if (r != ERROR_SUCCESS)
1820c2c66affSColin Finck             break;
1821c2c66affSColin Finck         r = TABLE_insert_row( view, rec, -1, TRUE );
1822c2c66affSColin Finck         break;
1823c2c66affSColin Finck 
1824c2c66affSColin Finck     case MSIMODIFY_REFRESH:
1825c2c66affSColin Finck         r = msi_refresh_record( view, rec, row );
1826c2c66affSColin Finck         break;
1827c2c66affSColin Finck 
1828c2c66affSColin Finck     case MSIMODIFY_UPDATE:
1829c2c66affSColin Finck         r = msi_table_update( view, rec, row );
1830c2c66affSColin Finck         break;
1831c2c66affSColin Finck 
1832c2c66affSColin Finck     case MSIMODIFY_ASSIGN:
1833c2c66affSColin Finck         r = msi_table_assign( view, rec );
1834c2c66affSColin Finck         break;
1835c2c66affSColin Finck 
1836c2c66affSColin Finck     case MSIMODIFY_MERGE:
1837c2c66affSColin Finck         /* check row that matches this record */
1838c2c66affSColin Finck         r = msi_table_find_row( tv, rec, &frow, &column );
1839c2c66affSColin Finck         if (r != ERROR_SUCCESS)
1840c2c66affSColin Finck         {
1841c2c66affSColin Finck             r = table_validate_new( tv, rec, NULL );
1842c2c66affSColin Finck             if (r == ERROR_SUCCESS)
1843c2c66affSColin Finck                 r = TABLE_insert_row( view, rec, -1, FALSE );
1844c2c66affSColin Finck         }
1845c2c66affSColin Finck         break;
1846c2c66affSColin Finck 
1847c2c66affSColin Finck     case MSIMODIFY_REPLACE:
1848c2c66affSColin Finck     case MSIMODIFY_VALIDATE:
1849c2c66affSColin Finck     case MSIMODIFY_VALIDATE_FIELD:
1850c2c66affSColin Finck     case MSIMODIFY_VALIDATE_DELETE:
1851c2c66affSColin Finck         FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1852c2c66affSColin Finck         r = ERROR_CALL_NOT_IMPLEMENTED;
1853c2c66affSColin Finck         break;
1854c2c66affSColin Finck 
1855c2c66affSColin Finck     default:
1856c2c66affSColin Finck         r = ERROR_INVALID_DATA;
1857c2c66affSColin Finck     }
1858c2c66affSColin Finck 
1859c2c66affSColin Finck     return r;
1860c2c66affSColin Finck }
1861c2c66affSColin Finck 
1862c2c66affSColin Finck static UINT TABLE_delete( struct tagMSIVIEW *view )
1863c2c66affSColin Finck {
1864c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1865c2c66affSColin Finck 
1866c2c66affSColin Finck     TRACE("%p\n", view );
1867c2c66affSColin Finck 
1868c2c66affSColin Finck     tv->table = NULL;
1869c2c66affSColin Finck     tv->columns = NULL;
1870c2c66affSColin Finck 
1871c2c66affSColin Finck     msi_free( tv );
1872c2c66affSColin Finck 
1873c2c66affSColin Finck     return ERROR_SUCCESS;
1874c2c66affSColin Finck }
1875c2c66affSColin Finck 
1876c2c66affSColin Finck static UINT TABLE_add_ref(struct tagMSIVIEW *view)
1877c2c66affSColin Finck {
1878c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1879c2c66affSColin Finck     UINT i;
1880c2c66affSColin Finck 
1881c2c66affSColin Finck     TRACE("%p %d\n", view, tv->table->ref_count);
1882c2c66affSColin Finck 
1883c2c66affSColin Finck     for (i = 0; i < tv->table->col_count; i++)
1884c2c66affSColin Finck     {
1885c2c66affSColin Finck         if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
1886c2c66affSColin Finck             InterlockedIncrement(&tv->table->colinfo[i].ref_count);
1887c2c66affSColin Finck     }
1888c2c66affSColin Finck 
1889c2c66affSColin Finck     return InterlockedIncrement(&tv->table->ref_count);
1890c2c66affSColin Finck }
1891c2c66affSColin Finck 
1892c2c66affSColin Finck static UINT TABLE_remove_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number)
1893c2c66affSColin Finck {
1894c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1895c2c66affSColin Finck     MSIRECORD *rec = NULL;
1896c2c66affSColin Finck     MSIVIEW *columns = NULL;
1897c2c66affSColin Finck     UINT row, r;
1898c2c66affSColin Finck 
1899c2c66affSColin Finck     rec = MSI_CreateRecord(2);
1900c2c66affSColin Finck     if (!rec)
1901c2c66affSColin Finck         return ERROR_OUTOFMEMORY;
1902c2c66affSColin Finck 
1903c2c66affSColin Finck     MSI_RecordSetStringW(rec, 1, table);
1904c2c66affSColin Finck     MSI_RecordSetInteger(rec, 2, number);
1905c2c66affSColin Finck 
1906c2c66affSColin Finck     r = TABLE_CreateView(tv->db, szColumns, &columns);
1907c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1908c2c66affSColin Finck     {
1909c2c66affSColin Finck         msiobj_release(&rec->hdr);
1910c2c66affSColin Finck         return r;
1911c2c66affSColin Finck     }
1912c2c66affSColin Finck 
1913c2c66affSColin Finck     r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL);
1914c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1915c2c66affSColin Finck         goto done;
1916c2c66affSColin Finck 
1917c2c66affSColin Finck     r = TABLE_delete_row(columns, row);
1918c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1919c2c66affSColin Finck         goto done;
1920c2c66affSColin Finck 
1921c2c66affSColin Finck     msi_update_table_columns(tv->db, table);
1922c2c66affSColin Finck 
1923c2c66affSColin Finck done:
1924c2c66affSColin Finck     msiobj_release(&rec->hdr);
1925c2c66affSColin Finck     columns->ops->delete(columns);
1926c2c66affSColin Finck     return r;
1927c2c66affSColin Finck }
1928c2c66affSColin Finck 
1929c2c66affSColin Finck static UINT TABLE_release(struct tagMSIVIEW *view)
1930c2c66affSColin Finck {
1931c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1932c2c66affSColin Finck     INT ref = tv->table->ref_count;
1933c2c66affSColin Finck     UINT i, r;
1934c2c66affSColin Finck 
1935c2c66affSColin Finck     TRACE("%p %d\n", view, ref);
1936c2c66affSColin Finck 
1937c2c66affSColin Finck     for (i = 0; i < tv->table->col_count; i++)
1938c2c66affSColin Finck     {
1939c2c66affSColin Finck         if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
1940c2c66affSColin Finck         {
1941c2c66affSColin Finck             ref = InterlockedDecrement(&tv->table->colinfo[i].ref_count);
1942c2c66affSColin Finck             if (ref == 0)
1943c2c66affSColin Finck             {
1944c2c66affSColin Finck                 r = TABLE_remove_column(view, tv->table->colinfo[i].tablename,
1945c2c66affSColin Finck                                         tv->table->colinfo[i].number);
1946c2c66affSColin Finck                 if (r != ERROR_SUCCESS)
1947c2c66affSColin Finck                     break;
1948c2c66affSColin Finck             }
1949c2c66affSColin Finck         }
1950c2c66affSColin Finck     }
1951c2c66affSColin Finck 
1952c2c66affSColin Finck     ref = InterlockedDecrement(&tv->table->ref_count);
1953c2c66affSColin Finck     if (ref == 0)
1954c2c66affSColin Finck     {
1955c2c66affSColin Finck         if (!tv->table->row_count)
1956c2c66affSColin Finck         {
1957c2c66affSColin Finck             list_remove(&tv->table->entry);
1958c2c66affSColin Finck             free_table(tv->table);
1959c2c66affSColin Finck             TABLE_delete(view);
1960c2c66affSColin Finck         }
1961c2c66affSColin Finck     }
1962c2c66affSColin Finck 
1963c2c66affSColin Finck     return ref;
1964c2c66affSColin Finck }
1965c2c66affSColin Finck 
1966c2c66affSColin Finck static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number,
1967c2c66affSColin Finck                              LPCWSTR column, UINT type, BOOL hold)
1968c2c66affSColin Finck {
1969c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1970c2c66affSColin Finck     MSITABLE *msitable;
1971c2c66affSColin Finck     MSIRECORD *rec;
1972c2c66affSColin Finck     UINT r, i;
1973c2c66affSColin Finck 
1974c2c66affSColin Finck     rec = MSI_CreateRecord(4);
1975c2c66affSColin Finck     if (!rec)
1976c2c66affSColin Finck         return ERROR_OUTOFMEMORY;
1977c2c66affSColin Finck 
1978c2c66affSColin Finck     MSI_RecordSetStringW(rec, 1, table);
1979c2c66affSColin Finck     MSI_RecordSetInteger(rec, 2, number);
1980c2c66affSColin Finck     MSI_RecordSetStringW(rec, 3, column);
1981c2c66affSColin Finck     MSI_RecordSetInteger(rec, 4, type);
1982c2c66affSColin Finck 
1983c2c66affSColin Finck     r = TABLE_insert_row(&tv->view, rec, -1, FALSE);
1984c2c66affSColin Finck     if (r != ERROR_SUCCESS)
1985c2c66affSColin Finck         goto done;
1986c2c66affSColin Finck 
1987c2c66affSColin Finck     msi_update_table_columns(tv->db, table);
1988c2c66affSColin Finck 
1989c2c66affSColin Finck     if (!hold)
1990c2c66affSColin Finck         goto done;
1991c2c66affSColin Finck 
1992c2c66affSColin Finck     msitable = find_cached_table(tv->db, table);
1993c2c66affSColin Finck     for (i = 0; i < msitable->col_count; i++)
1994c2c66affSColin Finck     {
1995c2c66affSColin Finck         if (!strcmpW( msitable->colinfo[i].colname, column ))
1996c2c66affSColin Finck         {
1997c2c66affSColin Finck             InterlockedIncrement(&msitable->colinfo[i].ref_count);
1998c2c66affSColin Finck             break;
1999c2c66affSColin Finck         }
2000c2c66affSColin Finck     }
2001c2c66affSColin Finck 
2002c2c66affSColin Finck done:
2003c2c66affSColin Finck     msiobj_release(&rec->hdr);
2004c2c66affSColin Finck     return r;
2005c2c66affSColin Finck }
2006c2c66affSColin Finck 
2007c2c66affSColin Finck static UINT TABLE_drop(struct tagMSIVIEW *view)
2008c2c66affSColin Finck {
2009c2c66affSColin Finck     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
2010c2c66affSColin Finck     MSIVIEW *tables = NULL;
2011c2c66affSColin Finck     MSIRECORD *rec = NULL;
2012c2c66affSColin Finck     UINT r, row;
2013c2c66affSColin Finck     INT i;
2014c2c66affSColin Finck 
2015c2c66affSColin Finck     TRACE("dropping table %s\n", debugstr_w(tv->name));
2016c2c66affSColin Finck 
2017c2c66affSColin Finck     for (i = tv->table->col_count - 1; i >= 0; i--)
2018c2c66affSColin Finck     {
2019c2c66affSColin Finck         r = TABLE_remove_column(view, tv->table->colinfo[i].tablename,
2020c2c66affSColin Finck                                 tv->table->colinfo[i].number);
2021c2c66affSColin Finck         if (r != ERROR_SUCCESS)
2022c2c66affSColin Finck             return r;
2023c2c66affSColin Finck     }
2024c2c66affSColin Finck 
2025c2c66affSColin Finck     rec = MSI_CreateRecord(1);
2026c2c66affSColin Finck     if (!rec)
2027c2c66affSColin Finck         return ERROR_OUTOFMEMORY;
2028c2c66affSColin Finck 
2029c2c66affSColin Finck     MSI_RecordSetStringW(rec, 1, tv->name);
2030c2c66affSColin Finck 
2031c2c66affSColin Finck     r = TABLE_CreateView(tv->db, szTables, &tables);
2032c2c66affSColin Finck     if (r != ERROR_SUCCESS)
2033c2c66affSColin Finck     {
2034c2c66affSColin Finck         msiobj_release(&rec->hdr);
2035c2c66affSColin Finck         return r;
2036c2c66affSColin Finck     }
2037c2c66affSColin Finck 
2038c2c66affSColin Finck     r = msi_table_find_row((MSITABLEVIEW *)tables, rec, &row, NULL);
2039c2c66affSColin Finck     if (r != ERROR_SUCCESS)
2040c2c66affSColin Finck         goto done;
2041c2c66affSColin Finck 
2042c2c66affSColin Finck     r = TABLE_delete_row(tables, row);
2043c2c66affSColin Finck     if (r != ERROR_SUCCESS)
2044c2c66affSColin Finck         goto done;
2045c2c66affSColin Finck 
2046c2c66affSColin Finck     list_remove(&tv->table->entry);
2047c2c66affSColin Finck     free_table(tv->table);
2048c2c66affSColin Finck 
2049c2c66affSColin Finck done:
2050c2c66affSColin Finck     msiobj_release(&rec->hdr);
2051c2c66affSColin Finck     tables->ops->delete(tables);
2052c2c66affSColin Finck 
2053c2c66affSColin Finck     return r;
2054c2c66affSColin Finck }
2055c2c66affSColin Finck 
2056c2c66affSColin Finck static const MSIVIEWOPS table_ops =
2057c2c66affSColin Finck {
2058c2c66affSColin Finck     TABLE_fetch_int,
2059c2c66affSColin Finck     TABLE_fetch_stream,
2060c2c66affSColin Finck     TABLE_get_row,
2061c2c66affSColin Finck     TABLE_set_row,
2062c2c66affSColin Finck     TABLE_insert_row,
2063c2c66affSColin Finck     TABLE_delete_row,
2064c2c66affSColin Finck     TABLE_execute,
2065c2c66affSColin Finck     TABLE_close,
2066c2c66affSColin Finck     TABLE_get_dimensions,
2067c2c66affSColin Finck     TABLE_get_column_info,
2068c2c66affSColin Finck     TABLE_modify,
2069c2c66affSColin Finck     TABLE_delete,
2070c2c66affSColin Finck     TABLE_add_ref,
2071c2c66affSColin Finck     TABLE_release,
2072c2c66affSColin Finck     TABLE_add_column,
2073c2c66affSColin Finck     NULL,
2074c2c66affSColin Finck     TABLE_drop,
2075c2c66affSColin Finck };
2076c2c66affSColin Finck 
2077c2c66affSColin Finck UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
2078c2c66affSColin Finck {
2079c2c66affSColin Finck     MSITABLEVIEW *tv ;
2080c2c66affSColin Finck     UINT r, sz;
2081c2c66affSColin Finck 
2082c2c66affSColin Finck     TRACE("%p %s %p\n", db, debugstr_w(name), view );
2083c2c66affSColin Finck 
2084c2c66affSColin Finck     if ( !strcmpW( name, szStreams ) )
2085c2c66affSColin Finck         return STREAMS_CreateView( db, view );
2086c2c66affSColin Finck     else if ( !strcmpW( name, szStorages ) )
2087c2c66affSColin Finck         return STORAGES_CreateView( db, view );
2088c2c66affSColin Finck 
2089c2c66affSColin Finck     sz = FIELD_OFFSET( MSITABLEVIEW, name[lstrlenW( name ) + 1] );
2090c2c66affSColin Finck     tv = msi_alloc_zero( sz );
2091c2c66affSColin Finck     if( !tv )
2092c2c66affSColin Finck         return ERROR_FUNCTION_FAILED;
2093c2c66affSColin Finck 
2094c2c66affSColin Finck     r = get_table( db, name, &tv->table );
2095c2c66affSColin Finck     if( r != ERROR_SUCCESS )
2096c2c66affSColin Finck     {
2097c2c66affSColin Finck         msi_free( tv );
2098c2c66affSColin Finck         WARN("table not found\n");
2099c2c66affSColin Finck         return r;
2100c2c66affSColin Finck     }
2101c2c66affSColin Finck 
2102c2c66affSColin Finck     TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count);
2103c2c66affSColin Finck 
2104c2c66affSColin Finck     /* fill the structure */
2105c2c66affSColin Finck     tv->view.ops = &table_ops;
2106c2c66affSColin Finck     tv->db = db;
2107c2c66affSColin Finck     tv->columns = tv->table->colinfo;
2108c2c66affSColin Finck     tv->num_cols = tv->table->col_count;
2109c2c66affSColin Finck     tv->row_size = msi_table_get_row_size( db, tv->table->colinfo, tv->table->col_count, LONG_STR_BYTES );
2110c2c66affSColin Finck 
2111c2c66affSColin Finck     TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
2112c2c66affSColin Finck 
2113c2c66affSColin Finck     *view = (MSIVIEW*) tv;
2114c2c66affSColin Finck     lstrcpyW( tv->name, name );
2115c2c66affSColin Finck 
2116c2c66affSColin Finck     return ERROR_SUCCESS;
2117c2c66affSColin Finck }
2118c2c66affSColin Finck 
2119c2c66affSColin Finck UINT MSI_CommitTables( MSIDATABASE *db )
2120c2c66affSColin Finck {
2121c2c66affSColin Finck     UINT r, bytes_per_strref;
2122c2c66affSColin Finck     HRESULT hr;
2123c2c66affSColin Finck     MSITABLE *table = NULL;
2124c2c66affSColin Finck 
2125c2c66affSColin Finck     TRACE("%p\n",db);
2126c2c66affSColin Finck 
2127c2c66affSColin Finck     r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref );
2128c2c66affSColin Finck     if( r != ERROR_SUCCESS )
2129c2c66affSColin Finck     {
2130c2c66affSColin Finck         WARN("failed to save string table r=%08x\n",r);
2131c2c66affSColin Finck         return r;
2132c2c66affSColin Finck     }
2133c2c66affSColin Finck 
2134c2c66affSColin Finck     LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry )
2135c2c66affSColin Finck     {
2136c2c66affSColin Finck         r = save_table( db, table, bytes_per_strref );
2137c2c66affSColin Finck         if( r != ERROR_SUCCESS )
2138c2c66affSColin Finck         {
2139c2c66affSColin Finck             WARN("failed to save table %s (r=%08x)\n",
2140c2c66affSColin Finck                   debugstr_w(table->name), r);
2141c2c66affSColin Finck             return r;
2142c2c66affSColin Finck         }
2143c2c66affSColin Finck     }
2144c2c66affSColin Finck 
2145c2c66affSColin Finck     hr = IStorage_Commit( db->storage, 0 );
2146c2c66affSColin Finck     if (FAILED( hr ))
2147c2c66affSColin Finck     {
2148c2c66affSColin Finck         WARN("failed to commit changes 0x%08x\n", hr);
2149c2c66affSColin Finck         r = ERROR_FUNCTION_FAILED;
2150c2c66affSColin Finck     }
2151c2c66affSColin Finck     return r;
2152c2c66affSColin Finck }
2153c2c66affSColin Finck 
2154c2c66affSColin Finck MSICONDITION MSI_DatabaseIsTablePersistent( MSIDATABASE *db, LPCWSTR table )
2155c2c66affSColin Finck {
2156c2c66affSColin Finck     MSITABLE *t;
2157c2c66affSColin Finck     UINT r;
2158c2c66affSColin Finck 
2159c2c66affSColin Finck     TRACE("%p %s\n", db, debugstr_w(table));
2160c2c66affSColin Finck 
2161c2c66affSColin Finck     if (!table)
2162c2c66affSColin Finck         return MSICONDITION_ERROR;
2163c2c66affSColin Finck 
2164c2c66affSColin Finck     r = get_table( db, table, &t );
2165c2c66affSColin Finck     if (r != ERROR_SUCCESS)
2166c2c66affSColin Finck         return MSICONDITION_NONE;
2167c2c66affSColin Finck 
2168c2c66affSColin Finck     return t->persistent;
2169c2c66affSColin Finck }
2170c2c66affSColin Finck 
2171c2c66affSColin Finck static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes)
2172c2c66affSColin Finck {
2173c2c66affSColin Finck     UINT ret = 0, i;
2174c2c66affSColin Finck 
2175c2c66affSColin Finck     for (i = 0; i < bytes; i++)
2176c2c66affSColin Finck         ret += (data[col + i] << i * 8);
2177c2c66affSColin Finck 
2178c2c66affSColin Finck     return ret;
2179c2c66affSColin Finck }
2180c2c66affSColin Finck 
2181c2c66affSColin Finck static UINT msi_record_encoded_stream_name( const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname )
2182c2c66affSColin Finck {
2183c2c66affSColin Finck     LPWSTR stname = NULL, sval, p;
2184c2c66affSColin Finck     DWORD len;
2185c2c66affSColin Finck     UINT i, r;
2186c2c66affSColin Finck 
2187c2c66affSColin Finck     TRACE("%p %p\n", tv, rec);
2188c2c66affSColin Finck 
2189c2c66affSColin Finck     len = lstrlenW( tv->name ) + 1;
2190c2c66affSColin Finck     stname = msi_alloc( len*sizeof(WCHAR) );
2191c2c66affSColin Finck     if ( !stname )
2192c2c66affSColin Finck     {
2193c2c66affSColin Finck        r = ERROR_OUTOFMEMORY;
2194c2c66affSColin Finck        goto err;
2195c2c66affSColin Finck     }
2196c2c66affSColin Finck 
2197c2c66affSColin Finck     lstrcpyW( stname, tv->name );
2198c2c66affSColin Finck 
2199c2c66affSColin Finck     for ( i = 0; i < tv->num_cols; i++ )
2200c2c66affSColin Finck     {
2201c2c66affSColin Finck         if ( tv->columns[i].type & MSITYPE_KEY )
2202c2c66affSColin Finck         {
2203c2c66affSColin Finck             sval = msi_dup_record_field( rec, i + 1 );
2204c2c66affSColin Finck             if ( !sval )
2205c2c66affSColin Finck             {
2206c2c66affSColin Finck                 r = ERROR_OUTOFMEMORY;
2207c2c66affSColin Finck                 goto err;
2208c2c66affSColin Finck             }
2209c2c66affSColin Finck 
2210c2c66affSColin Finck             len += lstrlenW( szDot ) + lstrlenW ( sval );
2211c2c66affSColin Finck             p = msi_realloc ( stname, len*sizeof(WCHAR) );
2212c2c66affSColin Finck             if ( !p )
2213c2c66affSColin Finck             {
2214c2c66affSColin Finck                 r = ERROR_OUTOFMEMORY;
2215c2c66affSColin Finck                 msi_free(sval);
2216c2c66affSColin Finck                 goto err;
2217c2c66affSColin Finck             }
2218c2c66affSColin Finck             stname = p;
2219c2c66affSColin Finck 
2220c2c66affSColin Finck             lstrcatW( stname, szDot );
2221c2c66affSColin Finck             lstrcatW( stname, sval );
2222c2c66affSColin Finck 
2223c2c66affSColin Finck             msi_free( sval );
2224c2c66affSColin Finck         }
2225c2c66affSColin Finck         else
2226c2c66affSColin Finck             continue;
2227c2c66affSColin Finck     }
2228c2c66affSColin Finck 
2229c2c66affSColin Finck     *pstname = encode_streamname( FALSE, stname );
2230c2c66affSColin Finck     msi_free( stname );
2231c2c66affSColin Finck 
2232c2c66affSColin Finck     return ERROR_SUCCESS;
2233c2c66affSColin Finck 
2234c2c66affSColin Finck err:
2235c2c66affSColin Finck     msi_free ( stname );
2236c2c66affSColin Finck     *pstname = NULL;
2237c2c66affSColin Finck     return r;
2238c2c66affSColin Finck }
2239c2c66affSColin Finck 
2240c2c66affSColin Finck static MSIRECORD *msi_get_transform_record( const MSITABLEVIEW *tv, const string_table *st,
2241c2c66affSColin Finck                                             IStorage *stg, const BYTE *rawdata, UINT bytes_per_strref )
2242c2c66affSColin Finck {
2243c2c66affSColin Finck     UINT i, val, ofs = 0;
2244c2c66affSColin Finck     USHORT mask;
2245c2c66affSColin Finck     MSICOLUMNINFO *columns = tv->columns;
2246c2c66affSColin Finck     MSIRECORD *rec;
2247c2c66affSColin Finck 
2248c2c66affSColin Finck     mask = rawdata[0] | (rawdata[1] << 8);
2249c2c66affSColin Finck     rawdata += 2;
2250c2c66affSColin Finck 
2251c2c66affSColin Finck     rec = MSI_CreateRecord( tv->num_cols );
2252c2c66affSColin Finck     if( !rec )
2253c2c66affSColin Finck         return rec;
2254c2c66affSColin Finck 
2255c2c66affSColin Finck     TRACE("row ->\n");
2256c2c66affSColin Finck     for( i=0; i<tv->num_cols; i++ )
2257c2c66affSColin Finck     {
2258c2c66affSColin Finck         if ( (mask&1) && (i>=(mask>>8)) )
2259c2c66affSColin Finck             break;
2260c2c66affSColin Finck         /* all keys must be present */
2261c2c66affSColin Finck         if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
2262c2c66affSColin Finck             continue;
2263c2c66affSColin Finck 
2264c2c66affSColin Finck         if( MSITYPE_IS_BINARY(tv->columns[i].type) )
2265c2c66affSColin Finck         {
2266c2c66affSColin Finck             LPWSTR encname;
2267c2c66affSColin Finck             IStream *stm = NULL;
2268c2c66affSColin Finck             UINT r;
2269c2c66affSColin Finck 
2270c2c66affSColin Finck             ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref );
2271c2c66affSColin Finck 
2272c2c66affSColin Finck             r = msi_record_encoded_stream_name( tv, rec, &encname );
2273c2c66affSColin Finck             if ( r != ERROR_SUCCESS )
2274c2c66affSColin Finck             {
2275c2c66affSColin Finck                 msiobj_release( &rec->hdr );
2276c2c66affSColin Finck                 return NULL;
2277c2c66affSColin Finck             }
2278c2c66affSColin Finck             r = IStorage_OpenStream( stg, encname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
2279c2c66affSColin Finck             if ( r != ERROR_SUCCESS )
2280c2c66affSColin Finck             {
2281c2c66affSColin Finck                 msiobj_release( &rec->hdr );
2282c2c66affSColin Finck                 msi_free( encname );
2283c2c66affSColin Finck                 return NULL;
2284c2c66affSColin Finck             }
2285c2c66affSColin Finck 
2286c2c66affSColin Finck             MSI_RecordSetStream( rec, i+1, stm );
2287c2c66affSColin Finck             TRACE(" field %d [%s]\n", i+1, debugstr_w(encname));
2288c2c66affSColin Finck             msi_free( encname );
2289c2c66affSColin Finck         }
2290c2c66affSColin Finck         else if( columns[i].type & MSITYPE_STRING )
2291c2c66affSColin Finck         {
2292c2c66affSColin Finck             int len;
2293c2c66affSColin Finck             const WCHAR *sval;
2294c2c66affSColin Finck 
2295c2c66affSColin Finck             val = read_raw_int(rawdata, ofs, bytes_per_strref);
2296c2c66affSColin Finck             sval = msi_string_lookup( st, val, &len );
2297c2c66affSColin Finck             msi_record_set_string( rec, i+1, sval, len );
2298c2c66affSColin Finck             TRACE(" field %d [%s]\n", i+1, debugstr_wn(sval, len));
2299c2c66affSColin Finck             ofs += bytes_per_strref;
2300c2c66affSColin Finck         }
2301c2c66affSColin Finck         else
2302c2c66affSColin Finck         {
2303c2c66affSColin Finck             UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref );
2304c2c66affSColin Finck             switch( n )
2305c2c66affSColin Finck             {
2306c2c66affSColin Finck             case 2:
2307c2c66affSColin Finck                 val = read_raw_int(rawdata, ofs, n);
2308c2c66affSColin Finck                 if (val)
2309c2c66affSColin Finck                     MSI_RecordSetInteger( rec, i+1, val-0x8000 );
2310c2c66affSColin Finck                 TRACE(" field %d [0x%04x]\n", i+1, val );
2311c2c66affSColin Finck                 break;
2312c2c66affSColin Finck             case 4:
2313c2c66affSColin Finck                 val = read_raw_int(rawdata, ofs, n);
2314c2c66affSColin Finck                 if (val)
2315c2c66affSColin Finck                     MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
2316c2c66affSColin Finck                 TRACE(" field %d [0x%08x]\n", i+1, val );
2317c2c66affSColin Finck                 break;
2318c2c66affSColin Finck             default:
2319c2c66affSColin Finck                 ERR("oops - unknown column width %d\n", n);
2320c2c66affSColin Finck                 break;
2321c2c66affSColin Finck             }
2322c2c66affSColin Finck             ofs += n;
2323c2c66affSColin Finck         }
2324c2c66affSColin Finck     }
2325c2c66affSColin Finck     return rec;
2326c2c66affSColin Finck }
2327c2c66affSColin Finck 
2328c2c66affSColin Finck static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize )
2329c2c66affSColin Finck {
2330c2c66affSColin Finck     UINT i;
2331c2c66affSColin Finck     for (i = 0; i < rawsize / 2; i++)
2332c2c66affSColin Finck     {
2333c2c66affSColin Finck         int len;
2334c2c66affSColin Finck         const WCHAR *sval = msi_string_lookup( st, rawdata[i], &len );
2335c2c66affSColin Finck         MESSAGE(" %04x %s\n", rawdata[i], debugstr_wn(sval, len) );
2336c2c66affSColin Finck     }
2337c2c66affSColin Finck }
2338c2c66affSColin Finck 
2339c2c66affSColin Finck static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec )
2340c2c66affSColin Finck {
2341c2c66affSColin Finck     UINT i, r, *data;
2342c2c66affSColin Finck 
2343c2c66affSColin Finck     data = msi_alloc( tv->num_cols *sizeof (UINT) );
2344c2c66affSColin Finck     for( i=0; i<tv->num_cols; i++ )
2345c2c66affSColin Finck     {
2346c2c66affSColin Finck         data[i] = 0;
2347c2c66affSColin Finck 
2348c2c66affSColin Finck         if ( ~tv->columns[i].type & MSITYPE_KEY )
2349c2c66affSColin Finck             continue;
2350c2c66affSColin Finck 
2351c2c66affSColin Finck         /* turn the transform column value into a row value */
2352c2c66affSColin Finck         if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
2353c2c66affSColin Finck              ! MSITYPE_IS_BINARY(tv->columns[i].type) )
2354c2c66affSColin Finck         {
2355c2c66affSColin Finck             int len;
2356c2c66affSColin Finck             const WCHAR *str = msi_record_get_string( rec, i+1, &len );
2357c2c66affSColin Finck             if (str)
2358c2c66affSColin Finck             {
2359c2c66affSColin Finck                 r = msi_string2id( tv->db->strings, str, len, &data[i] );
2360c2c66affSColin Finck 
2361c2c66affSColin Finck                 /* if there's no matching string in the string table,
2362c2c66affSColin Finck                    these keys can't match any record, so fail now. */
2363c2c66affSColin Finck                 if (r != ERROR_SUCCESS)
2364c2c66affSColin Finck                 {
2365c2c66affSColin Finck                     msi_free( data );
2366c2c66affSColin Finck                     return NULL;
2367c2c66affSColin Finck                 }
2368c2c66affSColin Finck             }
2369c2c66affSColin Finck             else data[i] = 0;
2370c2c66affSColin Finck         }
2371c2c66affSColin Finck         else
2372c2c66affSColin Finck         {
2373c2c66affSColin Finck             data[i] = MSI_RecordGetInteger( rec, i+1 );
2374c2c66affSColin Finck 
2375c2c66affSColin Finck             if (data[i] == MSI_NULL_INTEGER)
2376c2c66affSColin Finck                 data[i] = 0;
2377c2c66affSColin Finck             else if ((tv->columns[i].type&0xff) == 2)
2378c2c66affSColin Finck                 data[i] += 0x8000;
2379c2c66affSColin Finck             else
2380c2c66affSColin Finck                 data[i] += 0x80000000;
2381c2c66affSColin Finck         }
2382c2c66affSColin Finck     }
2383c2c66affSColin Finck     return data;
2384c2c66affSColin Finck }
2385c2c66affSColin Finck 
2386c2c66affSColin Finck static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column )
2387c2c66affSColin Finck {
2388c2c66affSColin Finck     UINT i, r, x, ret = ERROR_FUNCTION_FAILED;
2389c2c66affSColin Finck 
2390c2c66affSColin Finck     for( i=0; i<tv->num_cols; i++ )
2391c2c66affSColin Finck     {
2392c2c66affSColin Finck         if ( ~tv->columns[i].type & MSITYPE_KEY )
2393c2c66affSColin Finck             continue;
2394c2c66affSColin Finck 
2395c2c66affSColin Finck         /* turn the transform column value into a row value */
2396c2c66affSColin Finck         r = TABLE_fetch_int( &tv->view, row, i+1, &x );
2397c2c66affSColin Finck         if ( r != ERROR_SUCCESS )
2398c2c66affSColin Finck         {
2399c2c66affSColin Finck             ERR("TABLE_fetch_int shouldn't fail here\n");
2400c2c66affSColin Finck             break;
2401c2c66affSColin Finck         }
2402c2c66affSColin Finck 
2403c2c66affSColin Finck         /* if this key matches, move to the next column */
2404c2c66affSColin Finck         if ( x != data[i] )
2405c2c66affSColin Finck         {
2406c2c66affSColin Finck             ret = ERROR_FUNCTION_FAILED;
2407c2c66affSColin Finck             break;
2408c2c66affSColin Finck         }
2409c2c66affSColin Finck         if (column) *column = i;
2410c2c66affSColin Finck         ret = ERROR_SUCCESS;
2411c2c66affSColin Finck     }
2412c2c66affSColin Finck     return ret;
2413c2c66affSColin Finck }
2414c2c66affSColin Finck 
2415c2c66affSColin Finck static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column )
2416c2c66affSColin Finck {
2417c2c66affSColin Finck     UINT i, r = ERROR_FUNCTION_FAILED, *data;
2418c2c66affSColin Finck 
2419c2c66affSColin Finck     data = msi_record_to_row( tv, rec );
2420c2c66affSColin Finck     if( !data )
2421c2c66affSColin Finck         return r;
2422c2c66affSColin Finck     for( i = 0; i < tv->table->row_count; i++ )
2423c2c66affSColin Finck     {
2424c2c66affSColin Finck         r = msi_row_matches( tv, i, data, column );
2425c2c66affSColin Finck         if( r == ERROR_SUCCESS )
2426c2c66affSColin Finck         {
2427c2c66affSColin Finck             *row = i;
2428c2c66affSColin Finck             break;
2429c2c66affSColin Finck         }
2430c2c66affSColin Finck     }
2431c2c66affSColin Finck     msi_free( data );
2432c2c66affSColin Finck     return r;
2433c2c66affSColin Finck }
2434c2c66affSColin Finck 
2435c2c66affSColin Finck typedef struct
2436c2c66affSColin Finck {
2437c2c66affSColin Finck     struct list entry;
2438c2c66affSColin Finck     LPWSTR name;
2439c2c66affSColin Finck } TRANSFORMDATA;
2440c2c66affSColin Finck 
2441c2c66affSColin Finck static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg,
2442c2c66affSColin Finck                                       string_table *st, TRANSFORMDATA *transform,
2443c2c66affSColin Finck                                       UINT bytes_per_strref )
2444c2c66affSColin Finck {
2445c2c66affSColin Finck     BYTE *rawdata = NULL;
2446c2c66affSColin Finck     MSITABLEVIEW *tv = NULL;
2447c2c66affSColin Finck     UINT r, n, sz, i, mask, num_cols, colcol = 0, rawsize = 0;
2448c2c66affSColin Finck     MSIRECORD *rec = NULL;
2449c2c66affSColin Finck     WCHAR coltable[32];
2450c2c66affSColin Finck     const WCHAR *name;
2451c2c66affSColin Finck 
2452c2c66affSColin Finck     if (!transform)
2453c2c66affSColin Finck         return ERROR_SUCCESS;
2454c2c66affSColin Finck 
2455c2c66affSColin Finck     name = transform->name;
2456c2c66affSColin Finck 
2457c2c66affSColin Finck     coltable[0] = 0;
2458c2c66affSColin Finck     TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
2459c2c66affSColin Finck 
2460c2c66affSColin Finck     /* read the transform data */
2461c2c66affSColin Finck     read_stream_data( stg, name, TRUE, &rawdata, &rawsize );
2462c2c66affSColin Finck     if ( !rawdata )
2463c2c66affSColin Finck     {
2464c2c66affSColin Finck         TRACE("table %s empty\n", debugstr_w(name) );
2465c2c66affSColin Finck         return ERROR_INVALID_TABLE;
2466c2c66affSColin Finck     }
2467c2c66affSColin Finck 
2468c2c66affSColin Finck     /* create a table view */
2469c2c66affSColin Finck     r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
2470c2c66affSColin Finck     if( r != ERROR_SUCCESS )
2471c2c66affSColin Finck         goto err;
2472c2c66affSColin Finck 
2473c2c66affSColin Finck     r = tv->view.ops->execute( &tv->view, NULL );
2474c2c66affSColin Finck     if( r != ERROR_SUCCESS )
2475c2c66affSColin Finck         goto err;
2476c2c66affSColin Finck 
2477c2c66affSColin Finck     TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
2478c2c66affSColin Finck           debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
2479c2c66affSColin Finck 
2480c2c66affSColin Finck     /* interpret the data */
2481c2c66affSColin Finck     for (n = 0; n < rawsize;)
2482c2c66affSColin Finck     {
2483c2c66affSColin Finck         mask = rawdata[n] | (rawdata[n + 1] << 8);
2484c2c66affSColin Finck         if (mask & 1)
2485c2c66affSColin Finck         {
2486c2c66affSColin Finck             /*
2487c2c66affSColin Finck              * if the low bit is set, columns are continuous and
2488c2c66affSColin Finck              * the number of columns is specified in the high byte
2489c2c66affSColin Finck              */
2490c2c66affSColin Finck             sz = 2;
2491c2c66affSColin Finck             num_cols = mask >> 8;
2492c2c66affSColin Finck             if (num_cols > tv->num_cols)
2493c2c66affSColin Finck             {
2494c2c66affSColin Finck                 ERR("excess columns in transform: %u > %u\n", num_cols, tv->num_cols);
2495c2c66affSColin Finck                 break;
2496c2c66affSColin Finck             }
2497c2c66affSColin Finck 
2498c2c66affSColin Finck             for (i = 0; i < num_cols; i++)
2499c2c66affSColin Finck             {
2500c2c66affSColin Finck                 if( (tv->columns[i].type & MSITYPE_STRING) &&
2501c2c66affSColin Finck                     ! MSITYPE_IS_BINARY(tv->columns[i].type) )
2502c2c66affSColin Finck                     sz += bytes_per_strref;
2503c2c66affSColin Finck                 else
2504c2c66affSColin Finck                     sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
2505c2c66affSColin Finck             }
2506c2c66affSColin Finck         }
2507c2c66affSColin Finck         else
2508c2c66affSColin Finck         {
2509c2c66affSColin Finck             /*
2510c2c66affSColin Finck              * If the low bit is not set, mask is a bitmask.
2511c2c66affSColin Finck              * Excepting for key fields, which are always present,
2512c2c66affSColin Finck              *  each bit indicates that a field is present in the transform record.
2513c2c66affSColin Finck              *
2514c2c66affSColin Finck              * mask == 0 is a special case ... only the keys will be present
2515c2c66affSColin Finck              * and it means that this row should be deleted.
2516c2c66affSColin Finck              */
2517c2c66affSColin Finck             sz = 2;
2518c2c66affSColin Finck             num_cols = tv->num_cols;
2519c2c66affSColin Finck             for (i = 0; i < num_cols; i++)
2520c2c66affSColin Finck             {
2521c2c66affSColin Finck                 if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask))
2522c2c66affSColin Finck                 {
2523c2c66affSColin Finck                     if ((tv->columns[i].type & MSITYPE_STRING) &&
2524c2c66affSColin Finck                         !MSITYPE_IS_BINARY(tv->columns[i].type))
2525c2c66affSColin Finck                         sz += bytes_per_strref;
2526c2c66affSColin Finck                     else
2527c2c66affSColin Finck                         sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
2528c2c66affSColin Finck                 }
2529c2c66affSColin Finck             }
2530c2c66affSColin Finck         }
2531c2c66affSColin Finck 
2532c2c66affSColin Finck         /* check we didn't run of the end of the table */
2533c2c66affSColin Finck         if (n + sz > rawsize)
2534c2c66affSColin Finck         {
2535c2c66affSColin Finck             ERR("borked.\n");
2536c2c66affSColin Finck             dump_table( st, (USHORT *)rawdata, rawsize );
2537c2c66affSColin Finck             break;
2538c2c66affSColin Finck         }
2539c2c66affSColin Finck 
2540c2c66affSColin Finck         rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref );
2541c2c66affSColin Finck         if (rec)
2542c2c66affSColin Finck         {
2543c2c66affSColin Finck             WCHAR table[32];
2544c2c66affSColin Finck             DWORD sz = 32;
2545c2c66affSColin Finck             UINT number = MSI_NULL_INTEGER;
2546c2c66affSColin Finck             UINT row = 0;
2547c2c66affSColin Finck 
2548c2c66affSColin Finck             if (!strcmpW( name, szColumns ))
2549c2c66affSColin Finck             {
2550c2c66affSColin Finck                 MSI_RecordGetStringW( rec, 1, table, &sz );
2551c2c66affSColin Finck                 number = MSI_RecordGetInteger( rec, 2 );
2552c2c66affSColin Finck 
2553c2c66affSColin Finck                 /*
2554c2c66affSColin Finck                  * Native msi seems writes nul into the Number (2nd) column of
2555c2c66affSColin Finck                  * the _Columns table when there are new columns
2556c2c66affSColin Finck                  */
2557c2c66affSColin Finck                 if ( number == MSI_NULL_INTEGER )
2558c2c66affSColin Finck                 {
2559c2c66affSColin Finck                     /* reset the column number on a new table */
2560c2c66affSColin Finck                     if (strcmpW( coltable, table ))
2561c2c66affSColin Finck                     {
2562c2c66affSColin Finck                         colcol = 0;
2563c2c66affSColin Finck                         lstrcpyW( coltable, table );
2564c2c66affSColin Finck                     }
2565c2c66affSColin Finck 
2566c2c66affSColin Finck                     /* fix nul column numbers */
2567c2c66affSColin Finck                     MSI_RecordSetInteger( rec, 2, ++colcol );
2568c2c66affSColin Finck                 }
2569c2c66affSColin Finck             }
2570c2c66affSColin Finck 
2571c2c66affSColin Finck             if (TRACE_ON(msidb)) dump_record( rec );
2572c2c66affSColin Finck 
2573c2c66affSColin Finck             r = msi_table_find_row( tv, rec, &row, NULL );
2574c2c66affSColin Finck             if (r == ERROR_SUCCESS)
2575c2c66affSColin Finck             {
2576c2c66affSColin Finck                 if (!mask)
2577c2c66affSColin Finck                 {
2578c2c66affSColin Finck                     TRACE("deleting row [%d]:\n", row);
2579c2c66affSColin Finck                     r = TABLE_delete_row( &tv->view, row );
2580c2c66affSColin Finck                     if (r != ERROR_SUCCESS)
2581c2c66affSColin Finck                         WARN("failed to delete row %u\n", r);
2582c2c66affSColin Finck                 }
2583c2c66affSColin Finck                 else if (mask & 1)
2584c2c66affSColin Finck                 {
2585c2c66affSColin Finck                     TRACE("modifying full row [%d]:\n", row);
2586c2c66affSColin Finck                     r = TABLE_set_row( &tv->view, row, rec, (1 << tv->num_cols) - 1 );
2587c2c66affSColin Finck                     if (r != ERROR_SUCCESS)
2588c2c66affSColin Finck                         WARN("failed to modify row %u\n", r);
2589c2c66affSColin Finck                 }
2590c2c66affSColin Finck                 else
2591c2c66affSColin Finck                 {
2592c2c66affSColin Finck                     TRACE("modifying masked row [%d]:\n", row);
2593c2c66affSColin Finck                     r = TABLE_set_row( &tv->view, row, rec, mask );
2594c2c66affSColin Finck                     if (r != ERROR_SUCCESS)
2595c2c66affSColin Finck                         WARN("failed to modify row %u\n", r);
2596c2c66affSColin Finck                 }
2597c2c66affSColin Finck             }
2598c2c66affSColin Finck             else
2599c2c66affSColin Finck             {
2600c2c66affSColin Finck                 TRACE("inserting row\n");
2601c2c66affSColin Finck                 r = TABLE_insert_row( &tv->view, rec, -1, FALSE );
2602c2c66affSColin Finck                 if (r != ERROR_SUCCESS)
2603c2c66affSColin Finck                     WARN("failed to insert row %u\n", r);
2604c2c66affSColin Finck             }
2605c2c66affSColin Finck 
2606c2c66affSColin Finck             if (!strcmpW( name, szColumns ))
2607c2c66affSColin Finck                 msi_update_table_columns( db, table );
2608c2c66affSColin Finck 
2609c2c66affSColin Finck             msiobj_release( &rec->hdr );
2610c2c66affSColin Finck         }
2611c2c66affSColin Finck 
2612c2c66affSColin Finck         n += sz;
2613c2c66affSColin Finck     }
2614c2c66affSColin Finck 
2615c2c66affSColin Finck err:
2616c2c66affSColin Finck     /* no need to free the table, it's associated with the database */
2617c2c66affSColin Finck     msi_free( rawdata );
2618c2c66affSColin Finck     if( tv )
2619c2c66affSColin Finck         tv->view.ops->delete( &tv->view );
2620c2c66affSColin Finck 
2621c2c66affSColin Finck     return ERROR_SUCCESS;
2622c2c66affSColin Finck }
2623c2c66affSColin Finck 
2624c2c66affSColin Finck /*
2625c2c66affSColin Finck  * msi_table_apply_transform
2626c2c66affSColin Finck  *
2627c2c66affSColin Finck  * Enumerate the table transforms in a transform storage and apply each one.
2628c2c66affSColin Finck  */
2629c2c66affSColin Finck UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg )
2630c2c66affSColin Finck {
2631c2c66affSColin Finck     struct list transforms;
2632c2c66affSColin Finck     IEnumSTATSTG *stgenum = NULL;
2633c2c66affSColin Finck     TRANSFORMDATA *transform;
2634c2c66affSColin Finck     TRANSFORMDATA *tables = NULL, *columns = NULL;
2635c2c66affSColin Finck     HRESULT hr;
2636c2c66affSColin Finck     STATSTG stat;
2637c2c66affSColin Finck     string_table *strings;
2638c2c66affSColin Finck     UINT ret = ERROR_FUNCTION_FAILED;
2639c2c66affSColin Finck     UINT bytes_per_strref;
2640c2c66affSColin Finck     BOOL property_update = FALSE;
2641c2c66affSColin Finck 
2642c2c66affSColin Finck     TRACE("%p %p\n", db, stg );
2643c2c66affSColin Finck 
2644c2c66affSColin Finck     strings = msi_load_string_table( stg, &bytes_per_strref );
2645c2c66affSColin Finck     if( !strings )
2646c2c66affSColin Finck         goto end;
2647c2c66affSColin Finck 
2648c2c66affSColin Finck     hr = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
2649c2c66affSColin Finck     if (FAILED( hr ))
2650c2c66affSColin Finck         goto end;
2651c2c66affSColin Finck 
2652c2c66affSColin Finck     list_init(&transforms);
2653c2c66affSColin Finck 
2654c2c66affSColin Finck     while ( TRUE )
2655c2c66affSColin Finck     {
2656c2c66affSColin Finck         MSITABLEVIEW *tv = NULL;
2657c2c66affSColin Finck         WCHAR name[0x40];
2658c2c66affSColin Finck         ULONG count = 0;
2659c2c66affSColin Finck 
2660c2c66affSColin Finck         hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
2661c2c66affSColin Finck         if (FAILED( hr ) || !count)
2662c2c66affSColin Finck             break;
2663c2c66affSColin Finck 
2664c2c66affSColin Finck         decode_streamname( stat.pwcsName, name );
2665c2c66affSColin Finck         CoTaskMemFree( stat.pwcsName );
2666c2c66affSColin Finck         if ( name[0] != 0x4840 )
2667c2c66affSColin Finck             continue;
2668c2c66affSColin Finck 
2669c2c66affSColin Finck         if ( !strcmpW( name+1, szStringPool ) ||
2670c2c66affSColin Finck              !strcmpW( name+1, szStringData ) )
2671c2c66affSColin Finck             continue;
2672c2c66affSColin Finck 
2673c2c66affSColin Finck         transform = msi_alloc_zero( sizeof(TRANSFORMDATA) );
2674c2c66affSColin Finck         if ( !transform )
2675c2c66affSColin Finck             break;
2676c2c66affSColin Finck 
2677c2c66affSColin Finck         list_add_tail( &transforms, &transform->entry );
2678c2c66affSColin Finck 
2679c2c66affSColin Finck         transform->name = strdupW( name + 1 );
2680c2c66affSColin Finck 
2681c2c66affSColin Finck         if ( !strcmpW( transform->name, szTables ) )
2682c2c66affSColin Finck             tables = transform;
2683c2c66affSColin Finck         else if (!strcmpW( transform->name, szColumns ) )
2684c2c66affSColin Finck             columns = transform;
2685c2c66affSColin Finck         else if (!strcmpW( transform->name, szProperty ))
2686c2c66affSColin Finck             property_update = TRUE;
2687c2c66affSColin Finck 
2688c2c66affSColin Finck         TRACE("transform contains stream %s\n", debugstr_w(name));
2689c2c66affSColin Finck 
2690c2c66affSColin Finck         /* load the table */
2691c2c66affSColin Finck         if (TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv ) != ERROR_SUCCESS)
2692c2c66affSColin Finck             continue;
2693c2c66affSColin Finck 
2694c2c66affSColin Finck         if (tv->view.ops->execute( &tv->view, NULL ) != ERROR_SUCCESS)
2695c2c66affSColin Finck         {
2696c2c66affSColin Finck             tv->view.ops->delete( &tv->view );
2697c2c66affSColin Finck             continue;
2698c2c66affSColin Finck         }
2699c2c66affSColin Finck 
2700c2c66affSColin Finck         tv->view.ops->delete( &tv->view );
2701c2c66affSColin Finck     }
2702c2c66affSColin Finck 
2703c2c66affSColin Finck     /*
2704c2c66affSColin Finck      * Apply _Tables and _Columns transforms first so that
2705c2c66affSColin Finck      * the table metadata is correct, and empty tables exist.
2706c2c66affSColin Finck      */
2707c2c66affSColin Finck     ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref );
2708c2c66affSColin Finck     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2709c2c66affSColin Finck         goto end;
2710c2c66affSColin Finck 
2711c2c66affSColin Finck     ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref );
2712c2c66affSColin Finck     if (ret != ERROR_SUCCESS && ret != ERROR_INVALID_TABLE)
2713c2c66affSColin Finck         goto end;
2714c2c66affSColin Finck 
2715c2c66affSColin Finck     ret = ERROR_SUCCESS;
2716c2c66affSColin Finck 
2717c2c66affSColin Finck     while ( !list_empty( &transforms ) )
2718c2c66affSColin Finck     {
2719c2c66affSColin Finck         transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry );
2720c2c66affSColin Finck 
2721c2c66affSColin Finck         if ( strcmpW( transform->name, szColumns ) &&
2722c2c66affSColin Finck              strcmpW( transform->name, szTables ) &&
2723c2c66affSColin Finck              ret == ERROR_SUCCESS )
2724c2c66affSColin Finck         {
2725c2c66affSColin Finck             ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref );
2726c2c66affSColin Finck         }
2727c2c66affSColin Finck 
2728c2c66affSColin Finck         list_remove( &transform->entry );
2729c2c66affSColin Finck         msi_free( transform->name );
2730c2c66affSColin Finck         msi_free( transform );
2731c2c66affSColin Finck     }
2732c2c66affSColin Finck 
2733c2c66affSColin Finck     if ( ret == ERROR_SUCCESS )
2734c2c66affSColin Finck     {
2735c2c66affSColin Finck         append_storage_to_db( db, stg );
2736c2c66affSColin Finck         if (property_update) msi_clone_properties( db );
2737c2c66affSColin Finck     }
2738c2c66affSColin Finck 
2739c2c66affSColin Finck end:
2740c2c66affSColin Finck     if ( stgenum )
2741c2c66affSColin Finck         IEnumSTATSTG_Release( stgenum );
2742c2c66affSColin Finck     if ( strings )
2743c2c66affSColin Finck         msi_destroy_stringtable( strings );
2744c2c66affSColin Finck 
2745c2c66affSColin Finck     return ret;
2746c2c66affSColin Finck }
2747