Revert "msi: Avoid loading cabinet streams more than once.".
[wine] / dlls / msi / insert.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004 Mike McCormack for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdarg.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winerror.h"
26 #include "wine/debug.h"
27 #include "wine/unicode.h"
28 #include "msi.h"
29 #include "msiquery.h"
30 #include "objbase.h"
31 #include "objidl.h"
32 #include "msipriv.h"
33 #include "winnls.h"
34
35 #include "query.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
38
39
40 /* below is the query interface to a table */
41
42 typedef struct tagMSIINSERTVIEW
43 {
44     MSIVIEW          view;
45     MSIVIEW         *table;
46     MSIDATABASE     *db;
47     BOOL             bIsTemp;
48     MSIVIEW         *sv;
49     column_info     *vals;
50 } MSIINSERTVIEW;
51
52 static UINT INSERT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
53 {
54     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
55
56     TRACE("%p %d %d %p\n", iv, row, col, val );
57
58     return ERROR_FUNCTION_FAILED;
59 }
60
61 /*
62  * msi_query_merge_record
63  *
64  * Merge a value_list and a record to create a second record.
65  * Replace wildcard entries in the valuelist with values from the record
66  */
67 MSIRECORD *msi_query_merge_record( UINT fields, const column_info *vl, MSIRECORD *rec )
68 {
69     MSIRECORD *merged;
70     DWORD wildcard_count = 1, i;
71
72     merged = MSI_CreateRecord( fields );
73     for( i=1; i <= fields; i++ )
74     {
75         if( !vl )
76         {
77             TRACE("Not enough elements in the list to insert\n");
78             goto err;
79         }
80         switch( vl->val->type )
81         {
82         case EXPR_SVAL:
83             TRACE("field %d -> %s\n", i, debugstr_w(vl->val->u.sval));
84             MSI_RecordSetStringW( merged, i, vl->val->u.sval );
85             break;
86         case EXPR_IVAL:
87             MSI_RecordSetInteger( merged, i, vl->val->u.ival );
88             break;
89         case EXPR_WILDCARD:
90             if( !rec )
91                 goto err;
92             MSI_RecordCopyField( rec, wildcard_count, merged, i );
93             wildcard_count++;
94             break;
95         default:
96             ERR("Unknown expression type %d\n", vl->val->type);
97         }
98         vl = vl->next;
99     }
100
101     return merged;
102 err:
103     msiobj_release( &merged->hdr );
104     return NULL;
105 }
106
107 /* checks to see if the column order specified in the INSERT query
108  * matches the column order of the table
109  */
110 static BOOL msi_columns_in_order(MSIINSERTVIEW *iv, UINT col_count)
111 {
112     LPWSTR a, b = NULL;
113     UINT i;
114     int res;
115
116     for (i = 1; i <= col_count; i++)
117     {
118         iv->sv->ops->get_column_info(iv->sv, i, &a, NULL, NULL, NULL);
119         iv->table->ops->get_column_info(iv->table, i, &b, NULL, NULL, NULL);
120
121         res = strcmpW( a, b );
122         msi_free(a);
123         msi_free(b);
124
125         if (res != 0)
126             return FALSE;
127     }
128
129     return TRUE;
130 }
131
132 /* rearranges the data in the record to be inserted based on column order,
133  * and pads the record for any missing columns in the INSERT query
134  */
135 static UINT msi_arrange_record(MSIINSERTVIEW *iv, MSIRECORD **values)
136 {
137     MSIRECORD *padded;
138     UINT col_count, val_count;
139     UINT r, i, colidx;
140     LPWSTR a, b = NULL;
141     int res;
142
143     r = iv->table->ops->get_dimensions(iv->table, NULL, &col_count);
144     if (r != ERROR_SUCCESS)
145         return r;
146
147     val_count = MSI_RecordGetFieldCount(*values);
148
149     /* check to see if the columns are arranged already
150      * to avoid unnecessary copying
151      */
152     if (col_count == val_count && msi_columns_in_order(iv, col_count))
153         return ERROR_SUCCESS;
154
155     padded = MSI_CreateRecord(col_count);
156     if (!padded)
157         return ERROR_OUTOFMEMORY;
158
159     for (colidx = 1; colidx <= val_count; colidx++)
160     {
161         r = iv->sv->ops->get_column_info(iv->sv, colidx, &a, NULL, NULL, NULL);
162         if (r != ERROR_SUCCESS)
163             goto err;
164
165         for (i = 1; i <= col_count; i++)
166         {
167             r = iv->table->ops->get_column_info(iv->table, i, &b, NULL,
168                                                 NULL, NULL);
169             if (r != ERROR_SUCCESS)
170                 goto err;
171
172             res = strcmpW( a, b );
173             msi_free(b);
174
175             if (res == 0)
176             {
177                 MSI_RecordCopyField(*values, colidx, padded, i);
178                 break;
179             }
180         }
181
182         msi_free(a);
183     }
184
185     msiobj_release(&(*values)->hdr);
186     *values = padded;
187
188     return ERROR_SUCCESS;
189
190 err:
191     msiobj_release(&padded->hdr);
192     return r;
193 }
194
195 static BOOL row_has_null_primary_keys(MSIINSERTVIEW *iv, MSIRECORD *row)
196 {
197     UINT r, i, col_count, type;
198
199     r = iv->table->ops->get_dimensions( iv->table, NULL, &col_count );
200     if (r != ERROR_SUCCESS)
201         return FALSE;
202
203     for (i = 1; i <= col_count; i++)
204     {
205         r = iv->table->ops->get_column_info(iv->table, i, NULL, &type,
206                                             NULL, NULL);
207         if (r != ERROR_SUCCESS)
208             return FALSE;
209
210         if (!(type & MSITYPE_KEY))
211             continue;
212
213         if (MSI_RecordIsNull(row, i))
214             return TRUE;
215     }
216
217     return FALSE;
218 }
219
220 static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
221 {
222     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
223     UINT r, row = -1, col_count = 0;
224     MSIVIEW *sv;
225     MSIRECORD *values = NULL;
226
227     TRACE("%p %p\n", iv, record );
228
229     sv = iv->sv;
230     if( !sv )
231         return ERROR_FUNCTION_FAILED;
232
233     r = sv->ops->execute( sv, 0 );
234     TRACE("sv execute returned %x\n", r);
235     if( r )
236         return r;
237
238     r = sv->ops->get_dimensions( sv, NULL, &col_count );
239     if( r )
240         goto err;
241
242     /*
243      * Merge the wildcard values into the list of values provided
244      * in the query, and create a record containing both.
245      */
246     values = msi_query_merge_record( col_count, iv->vals, record );
247     if( !values )
248         goto err;
249
250     r = msi_arrange_record( iv, &values );
251     if( r != ERROR_SUCCESS )
252         goto err;
253
254     /* rows with NULL primary keys are inserted at the beginning of the table */
255     if( row_has_null_primary_keys( iv, values ) )
256         row = 0;
257
258     r = iv->table->ops->insert_row( iv->table, values, row, iv->bIsTemp );
259
260 err:
261     if( values )
262         msiobj_release( &values->hdr );
263
264     return r;
265 }
266
267
268 static UINT INSERT_close( struct tagMSIVIEW *view )
269 {
270     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
271     MSIVIEW *sv;
272
273     TRACE("%p\n", iv);
274
275     sv = iv->sv;
276     if( !sv )
277         return ERROR_FUNCTION_FAILED;
278
279     return sv->ops->close( sv );
280 }
281
282 static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
283 {
284     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
285     MSIVIEW *sv;
286
287     TRACE("%p %p %p\n", iv, rows, cols );
288
289     sv = iv->sv;
290     if( !sv )
291         return ERROR_FUNCTION_FAILED;
292
293     return sv->ops->get_dimensions( sv, rows, cols );
294 }
295
296 static UINT INSERT_get_column_info( struct tagMSIVIEW *view,
297                 UINT n, LPWSTR *name, UINT *type, BOOL *temporary,
298                 LPWSTR *table_name)
299 {
300     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
301     MSIVIEW *sv;
302
303     TRACE("%p %d %p %p %p %p\n", iv, n, name, type, temporary, table_name );
304
305     sv = iv->sv;
306     if( !sv )
307         return ERROR_FUNCTION_FAILED;
308
309     return sv->ops->get_column_info( sv, n, name, type, temporary, table_name );
310 }
311
312 static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
313 {
314     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
315
316     TRACE("%p %d %p\n", iv, eModifyMode, rec );
317
318     return ERROR_FUNCTION_FAILED;
319 }
320
321 static UINT INSERT_delete( struct tagMSIVIEW *view )
322 {
323     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
324     MSIVIEW *sv;
325
326     TRACE("%p\n", iv );
327
328     sv = iv->sv;
329     if( sv )
330         sv->ops->delete( sv );
331     msiobj_release( &iv->db->hdr );
332     msi_free( iv );
333
334     return ERROR_SUCCESS;
335 }
336
337 static UINT INSERT_find_matching_rows( struct tagMSIVIEW *view, UINT col,
338     UINT val, UINT *row, MSIITERHANDLE *handle )
339 {
340     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
341
342     return ERROR_FUNCTION_FAILED;
343 }
344
345
346 static const MSIVIEWOPS insert_ops =
347 {
348     INSERT_fetch_int,
349     NULL,
350     NULL,
351     NULL,
352     NULL,
353     NULL,
354     INSERT_execute,
355     INSERT_close,
356     INSERT_get_dimensions,
357     INSERT_get_column_info,
358     INSERT_modify,
359     INSERT_delete,
360     INSERT_find_matching_rows,
361     NULL,
362     NULL,
363     NULL,
364     NULL,
365     NULL,
366     NULL,
367 };
368
369 static UINT count_column_info( const column_info *ci )
370 {
371     UINT n = 0;
372     for ( ; ci; ci = ci->next )
373         n++;
374     return n;
375 }
376
377 UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table,
378                         column_info *columns, column_info *values, BOOL temp )
379 {
380     MSIINSERTVIEW *iv = NULL;
381     UINT r;
382     MSIVIEW *tv = NULL, *sv = NULL;
383
384     TRACE("%p\n", iv );
385
386     /* there should be one value for each column */
387     if ( count_column_info( columns ) != count_column_info(values) )
388         return ERROR_BAD_QUERY_SYNTAX;
389
390     r = TABLE_CreateView( db, table, &tv );
391     if( r != ERROR_SUCCESS )
392         return r;
393
394     r = SELECT_CreateView( db, &sv, tv, columns );
395     if( r != ERROR_SUCCESS )
396     {
397         if( tv )
398             tv->ops->delete( tv );
399         return r;
400     }
401
402     iv = msi_alloc_zero( sizeof *iv );
403     if( !iv )
404         return ERROR_FUNCTION_FAILED;
405
406     /* fill the structure */
407     iv->view.ops = &insert_ops;
408     msiobj_addref( &db->hdr );
409     iv->table = tv;
410     iv->db = db;
411     iv->vals = values;
412     iv->bIsTemp = temp;
413     iv->sv = sv;
414     *view = (MSIVIEW*) iv;
415
416     return ERROR_SUCCESS;
417 }