xref: /reactos/dll/win32/msi/insert.c (revision 4561998a)
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     LPCWSTR a, b;
113     UINT i;
114 
115     for (i = 1; i <= col_count; i++)
116     {
117         iv->sv->ops->get_column_info(iv->sv, i, &a, NULL, NULL, NULL);
118         iv->table->ops->get_column_info(iv->table, i, &b, NULL, NULL, NULL);
119 
120         if (strcmpW( a, b )) return FALSE;
121     }
122     return TRUE;
123 }
124 
125 /* rearranges the data in the record to be inserted based on column order,
126  * and pads the record for any missing columns in the INSERT query
127  */
128 static UINT msi_arrange_record(MSIINSERTVIEW *iv, MSIRECORD **values)
129 {
130     MSIRECORD *padded;
131     UINT col_count, val_count;
132     UINT r, i, colidx;
133     LPCWSTR a, b;
134 
135     r = iv->table->ops->get_dimensions(iv->table, NULL, &col_count);
136     if (r != ERROR_SUCCESS)
137         return r;
138 
139     val_count = MSI_RecordGetFieldCount(*values);
140 
141     /* check to see if the columns are arranged already
142      * to avoid unnecessary copying
143      */
144     if (col_count == val_count && msi_columns_in_order(iv, col_count))
145         return ERROR_SUCCESS;
146 
147     padded = MSI_CreateRecord(col_count);
148     if (!padded)
149         return ERROR_OUTOFMEMORY;
150 
151     for (colidx = 1; colidx <= val_count; colidx++)
152     {
153         r = iv->sv->ops->get_column_info(iv->sv, colidx, &a, NULL, NULL, NULL);
154         if (r != ERROR_SUCCESS)
155             goto err;
156 
157         for (i = 1; i <= col_count; i++)
158         {
159             r = iv->table->ops->get_column_info(iv->table, i, &b, NULL,
160                                                 NULL, NULL);
161             if (r != ERROR_SUCCESS)
162                 goto err;
163 
164             if (!strcmpW( a, b ))
165             {
166                 MSI_RecordCopyField(*values, colidx, padded, i);
167                 break;
168             }
169         }
170     }
171     msiobj_release(&(*values)->hdr);
172     *values = padded;
173     return ERROR_SUCCESS;
174 
175 err:
176     msiobj_release(&padded->hdr);
177     return r;
178 }
179 
180 static BOOL row_has_null_primary_keys(MSIINSERTVIEW *iv, MSIRECORD *row)
181 {
182     UINT r, i, col_count, type;
183 
184     r = iv->table->ops->get_dimensions( iv->table, NULL, &col_count );
185     if (r != ERROR_SUCCESS)
186         return FALSE;
187 
188     for (i = 1; i <= col_count; i++)
189     {
190         r = iv->table->ops->get_column_info(iv->table, i, NULL, &type,
191                                             NULL, NULL);
192         if (r != ERROR_SUCCESS)
193             return FALSE;
194 
195         if (!(type & MSITYPE_KEY))
196             continue;
197 
198         if (MSI_RecordIsNull(row, i))
199             return TRUE;
200     }
201 
202     return FALSE;
203 }
204 
205 static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
206 {
207     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
208     UINT r, row = -1, col_count = 0;
209     MSIVIEW *sv;
210     MSIRECORD *values = NULL;
211 
212     TRACE("%p %p\n", iv, record );
213 
214     sv = iv->sv;
215     if( !sv )
216         return ERROR_FUNCTION_FAILED;
217 
218     r = sv->ops->execute( sv, 0 );
219     TRACE("sv execute returned %x\n", r);
220     if( r )
221         return r;
222 
223     r = sv->ops->get_dimensions( sv, NULL, &col_count );
224     if( r )
225         goto err;
226 
227     /*
228      * Merge the wildcard values into the list of values provided
229      * in the query, and create a record containing both.
230      */
231     values = msi_query_merge_record( col_count, iv->vals, record );
232     if( !values )
233         goto err;
234 
235     r = msi_arrange_record( iv, &values );
236     if( r != ERROR_SUCCESS )
237         goto err;
238 
239     /* rows with NULL primary keys are inserted at the beginning of the table */
240     if( row_has_null_primary_keys( iv, values ) )
241         row = 0;
242 
243     r = iv->table->ops->insert_row( iv->table, values, row, iv->bIsTemp );
244 
245 err:
246     if( values )
247         msiobj_release( &values->hdr );
248 
249     return r;
250 }
251 
252 
253 static UINT INSERT_close( struct tagMSIVIEW *view )
254 {
255     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
256     MSIVIEW *sv;
257 
258     TRACE("%p\n", iv);
259 
260     sv = iv->sv;
261     if( !sv )
262         return ERROR_FUNCTION_FAILED;
263 
264     return sv->ops->close( sv );
265 }
266 
267 static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
268 {
269     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
270     MSIVIEW *sv;
271 
272     TRACE("%p %p %p\n", iv, rows, cols );
273 
274     sv = iv->sv;
275     if( !sv )
276         return ERROR_FUNCTION_FAILED;
277 
278     return sv->ops->get_dimensions( sv, rows, cols );
279 }
280 
281 static UINT INSERT_get_column_info( struct tagMSIVIEW *view, UINT n, LPCWSTR *name,
282                                     UINT *type, BOOL *temporary, LPCWSTR *table_name )
283 {
284     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
285     MSIVIEW *sv;
286 
287     TRACE("%p %d %p %p %p %p\n", iv, n, name, type, temporary, table_name );
288 
289     sv = iv->sv;
290     if( !sv )
291         return ERROR_FUNCTION_FAILED;
292 
293     return sv->ops->get_column_info( sv, n, name, type, temporary, table_name );
294 }
295 
296 static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
297 {
298     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
299 
300     TRACE("%p %d %p\n", iv, eModifyMode, rec );
301 
302     return ERROR_FUNCTION_FAILED;
303 }
304 
305 static UINT INSERT_delete( struct tagMSIVIEW *view )
306 {
307     MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
308     MSIVIEW *sv;
309 
310     TRACE("%p\n", iv );
311 
312     sv = iv->sv;
313     if( sv )
314         sv->ops->delete( sv );
315     msiobj_release( &iv->db->hdr );
316     msi_free( iv );
317 
318     return ERROR_SUCCESS;
319 }
320 
321 static UINT INSERT_find_matching_rows( struct tagMSIVIEW *view, UINT col,
322     UINT val, UINT *row, MSIITERHANDLE *handle )
323 {
324     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
325 
326     return ERROR_FUNCTION_FAILED;
327 }
328 
329 
330 static const MSIVIEWOPS insert_ops =
331 {
332     INSERT_fetch_int,
333     NULL,
334     NULL,
335     NULL,
336     NULL,
337     NULL,
338     INSERT_execute,
339     INSERT_close,
340     INSERT_get_dimensions,
341     INSERT_get_column_info,
342     INSERT_modify,
343     INSERT_delete,
344     INSERT_find_matching_rows,
345     NULL,
346     NULL,
347     NULL,
348     NULL,
349     NULL,
350     NULL,
351 };
352 
353 static UINT count_column_info( const column_info *ci )
354 {
355     UINT n = 0;
356     for ( ; ci; ci = ci->next )
357         n++;
358     return n;
359 }
360 
361 UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table,
362                         column_info *columns, column_info *values, BOOL temp )
363 {
364     MSIINSERTVIEW *iv = NULL;
365     UINT r;
366     MSIVIEW *tv = NULL, *sv = NULL;
367 
368     TRACE("%p\n", iv );
369 
370     /* there should be one value for each column */
371     if ( count_column_info( columns ) != count_column_info(values) )
372         return ERROR_BAD_QUERY_SYNTAX;
373 
374     r = TABLE_CreateView( db, table, &tv );
375     if( r != ERROR_SUCCESS )
376         return r;
377 
378     r = SELECT_CreateView( db, &sv, tv, columns );
379     if( r != ERROR_SUCCESS )
380     {
381         if( tv )
382             tv->ops->delete( tv );
383         return r;
384     }
385 
386     iv = msi_alloc_zero( sizeof *iv );
387     if( !iv )
388         return ERROR_FUNCTION_FAILED;
389 
390     /* fill the structure */
391     iv->view.ops = &insert_ops;
392     msiobj_addref( &db->hdr );
393     iv->table = tv;
394     iv->db = db;
395     iv->vals = values;
396     iv->bIsTemp = temp;
397     iv->sv = sv;
398     *view = (MSIVIEW*) iv;
399 
400     return ERROR_SUCCESS;
401 }
402