msi: Allow non-key columns to be used with the join query.
[wine] / dlls / msi / select.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-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 "msi.h"
28 #include "msiquery.h"
29 #include "objbase.h"
30 #include "objidl.h"
31 #include "msipriv.h"
32 #include "winnls.h"
33
34 #include "query.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
37
38
39 /* below is the query interface to a table */
40
41 typedef struct tagMSISELECTVIEW
42 {
43     MSIVIEW        view;
44     MSIDATABASE   *db;
45     MSIVIEW       *table;
46     UINT           num_cols;
47     UINT           max_cols;
48     UINT           cols[1];
49 } MSISELECTVIEW;
50
51 static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
52 {
53     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
54
55     TRACE("%p %d %d %p\n", sv, row, col, val );
56
57     if( !sv->table )
58          return ERROR_FUNCTION_FAILED;
59
60     if( (col==0) || (col>sv->num_cols) )
61          return ERROR_FUNCTION_FAILED;
62
63     col = sv->cols[ col - 1 ];
64
65     return sv->table->ops->fetch_int( sv->table, row, col, val );
66 }
67
68 static UINT SELECT_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
69 {
70     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
71
72     TRACE("%p %d %d %p\n", sv, row, col, stm );
73
74     if( !sv->table )
75          return ERROR_FUNCTION_FAILED;
76
77     if( (col==0) || (col>sv->num_cols) )
78          return ERROR_FUNCTION_FAILED;
79
80     col = sv->cols[ col - 1 ];
81
82     return sv->table->ops->fetch_stream( sv->table, row, col, stm );
83 }
84
85 static UINT SELECT_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
86 {
87     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
88
89     TRACE("%p %d %d %04x\n", sv, row, col, val );
90
91     if( !sv->table )
92          return ERROR_FUNCTION_FAILED;
93
94     if( (col==0) || (col>sv->num_cols) )
95          return ERROR_FUNCTION_FAILED;
96
97     col = sv->cols[ col - 1 ];
98
99     return sv->table->ops->set_int( sv->table, row, col, val );
100 }
101
102 static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record )
103 {
104     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
105     UINT i, table_cols, r;
106     MSIRECORD *outrec;
107
108     TRACE("%p %p\n", sv, record );
109
110     if ( !sv->table )
111         return ERROR_FUNCTION_FAILED;
112
113     /* rearrange the record to suit the table */
114     r = sv->table->ops->get_dimensions( sv->table, NULL, &table_cols );
115     if (r != ERROR_SUCCESS)
116         return r;
117
118     outrec = MSI_CreateRecord( table_cols + 1 );
119
120     for (i=0; i<sv->num_cols; i++)
121     {
122         r = MSI_RecordCopyField( record, i+1, outrec, sv->cols[i] );
123         if (r != ERROR_SUCCESS)
124             goto fail;
125     }
126
127     r = sv->table->ops->insert_row( sv->table, outrec );
128
129 fail:
130     msiobj_release( &outrec->hdr );
131
132     return r;
133 }
134
135 static UINT SELECT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
136 {
137     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
138
139     TRACE("%p %p\n", sv, record);
140
141     if( !sv->table )
142          return ERROR_FUNCTION_FAILED;
143
144     return sv->table->ops->execute( sv->table, record );
145 }
146
147 static UINT SELECT_close( struct tagMSIVIEW *view )
148 {
149     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
150
151     TRACE("%p\n", sv );
152
153     if( !sv->table )
154          return ERROR_FUNCTION_FAILED;
155
156     return sv->table->ops->close( sv->table );
157 }
158
159 static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
160 {
161     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
162
163     TRACE("%p %p %p\n", sv, rows, cols );
164
165     if( !sv->table )
166          return ERROR_FUNCTION_FAILED;
167
168     if( cols )
169         *cols = sv->num_cols;
170
171     return sv->table->ops->get_dimensions( sv->table, rows, NULL );
172 }
173
174 static UINT SELECT_get_column_info( struct tagMSIVIEW *view,
175                 UINT n, LPWSTR *name, UINT *type )
176 {
177     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
178
179     TRACE("%p %d %p %p\n", sv, n, name, type );
180
181     if( !sv->table )
182          return ERROR_FUNCTION_FAILED;
183
184     if( (n==0) || (n>sv->num_cols) )
185          return ERROR_FUNCTION_FAILED;
186
187     n = sv->cols[ n - 1 ];
188
189     return sv->table->ops->get_column_info( sv->table, n, name, type );
190 }
191
192 static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
193                 MSIRECORD *rec )
194 {
195     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
196
197     TRACE("%p %d %p\n", sv, eModifyMode, rec );
198
199     if( !sv->table )
200          return ERROR_FUNCTION_FAILED;
201
202     return sv->table->ops->modify( sv->table, eModifyMode, rec );
203 }
204
205 static UINT SELECT_delete( struct tagMSIVIEW *view )
206 {
207     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
208
209     TRACE("%p\n", sv );
210
211     if( sv->table )
212         sv->table->ops->delete( sv->table );
213     sv->table = NULL;
214
215     msi_free( sv );
216
217     return ERROR_SUCCESS;
218 }
219
220 static UINT SELECT_find_matching_rows( struct tagMSIVIEW *view, UINT col,
221     UINT val, UINT *row, MSIITERHANDLE *handle )
222 {
223     MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
224
225     TRACE("%p, %d, %u, %p\n", view, col, val, *handle);
226
227     if( !sv->table )
228          return ERROR_FUNCTION_FAILED;
229
230     if( (col==0) || (col>sv->num_cols) )
231          return ERROR_FUNCTION_FAILED;
232
233     col = sv->cols[ col - 1 ];
234
235     return sv->table->ops->find_matching_rows( sv->table, col, val, row, handle );
236 }
237
238
239 static const MSIVIEWOPS select_ops =
240 {
241     SELECT_fetch_int,
242     SELECT_fetch_stream,
243     SELECT_set_int,
244     SELECT_insert_row,
245     SELECT_execute,
246     SELECT_close,
247     SELECT_get_dimensions,
248     SELECT_get_column_info,
249     SELECT_modify,
250     SELECT_delete,
251     SELECT_find_matching_rows
252 };
253
254 static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name )
255 {
256     UINT r, n=0;
257     MSIVIEW *table;
258
259     TRACE("%p adding %s\n", sv, debugstr_w( name ) );
260
261     if( sv->view.ops != &select_ops )
262         return ERROR_FUNCTION_FAILED;
263
264     table = sv->table;
265     if( !table )
266         return ERROR_FUNCTION_FAILED;
267     if( !table->ops->get_dimensions )
268         return ERROR_FUNCTION_FAILED;
269     if( !table->ops->get_column_info )
270         return ERROR_FUNCTION_FAILED;
271
272     if( sv->num_cols >= sv->max_cols )
273         return ERROR_FUNCTION_FAILED;
274
275     r = VIEW_find_column( table, name, &n );
276     if( r != ERROR_SUCCESS )
277         return r;
278
279     sv->cols[sv->num_cols] = n;
280     TRACE("Translating column %s from %d -> %d\n", 
281           debugstr_w( name ), sv->num_cols, n);
282
283     sv->num_cols++;
284
285     return ERROR_SUCCESS;
286 }
287
288 int select_count_columns( column_info *col )
289 {
290     int n;
291     for (n = 0; col; col = col->next)
292         n++;
293     return n;
294 }
295
296 UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
297                         column_info *columns )
298 {
299     MSISELECTVIEW *sv = NULL;
300     UINT count = 0, r = ERROR_SUCCESS;
301
302     TRACE("%p\n", sv );
303
304     count = select_count_columns( columns );
305
306     sv = msi_alloc_zero( sizeof *sv + count*sizeof (UINT) );
307     if( !sv )
308         return ERROR_FUNCTION_FAILED;
309     
310     /* fill the structure */
311     sv->view.ops = &select_ops;
312     sv->db = db;
313     sv->table = table;
314     sv->num_cols = 0;
315     sv->max_cols = count;
316
317     while( columns )
318     {
319         r = SELECT_AddColumn( sv, columns->column );
320         if( r )
321             break;
322         columns = columns->next;
323     }
324
325     if( r == ERROR_SUCCESS )
326         *view = &sv->view;
327     else
328         msi_free( sv );
329
330     return r;
331 }