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