2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2006 Mike McCormack for CodeWeavers
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.
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.
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
26 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(msidb
);
36 typedef struct tagMSIJOINVIEW
40 MSIVIEW
*left
, *right
;
41 UINT left_count
, right_count
;
42 UINT left_key
, right_key
;
47 static UINT
JOIN_fetch_int( struct tagMSIVIEW
*view
, UINT row
, UINT col
, UINT
*val
)
49 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
52 TRACE("%p %d %d %p\n", jv
, row
, col
, val
);
54 if( !jv
->left
|| !jv
->right
)
55 return ERROR_FUNCTION_FAILED
;
57 if( (col
==0) || (col
>(jv
->left_count
+ jv
->right_count
)) )
58 return ERROR_FUNCTION_FAILED
;
60 if( row
>= jv
->pair_count
)
61 return ERROR_FUNCTION_FAILED
;
63 if( col
<= jv
->left_count
)
66 row
= jv
->pairs
[ row
*2 ];
71 row
= jv
->pairs
[ row
*2 + 1 ];
72 col
-= jv
->left_count
;
75 return table
->ops
->fetch_int( table
, row
, col
, val
);
78 static UINT
JOIN_fetch_stream( struct tagMSIVIEW
*view
, UINT row
, UINT col
, IStream
**stm
)
80 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
83 TRACE("%p %d %d %p\n", jv
, row
, col
, stm
);
85 if( !jv
->left
|| !jv
->right
)
86 return ERROR_FUNCTION_FAILED
;
88 if( (col
==0) || (col
>(jv
->left_count
+ jv
->right_count
)) )
89 return ERROR_FUNCTION_FAILED
;
91 if( row
<= jv
->left_count
)
94 row
= jv
->pairs
[ row
*2 ];
99 row
= jv
->pairs
[ row
*2 + 1 ];
100 col
-= jv
->left_count
;
103 return table
->ops
->fetch_stream( table
, row
, col
, stm
);
106 static int join_key_compare(const void *l
, const void *r
)
108 const UINT
*left
= l
, *right
= r
;
109 if (left
[1] < right
[1])
111 if (left
[1] == right
[1])
116 static UINT
join_load_key_column( MSIJOINVIEW
*jv
, MSIVIEW
*table
, UINT column
,
117 UINT
**pdata
, UINT
*pcount
)
119 UINT r
, i
, count
= 0, *data
= NULL
;
121 r
= table
->ops
->get_dimensions( table
, &count
, NULL
);
122 if( r
!= ERROR_SUCCESS
)
128 data
= msi_alloc( count
* 2 * sizeof (UINT
) );
130 return ERROR_SUCCESS
;
132 for (i
=0; i
<count
; i
++)
135 r
= table
->ops
->fetch_int( table
, i
, column
, &data
[i
*2+1] );
136 if (r
!= ERROR_SUCCESS
)
137 ERR("fetch data (%u,%u) failed\n", i
, column
);
140 qsort( data
, count
, 2 * sizeof (UINT
), join_key_compare
);
146 return ERROR_SUCCESS
;
149 static UINT
join_match( UINT
*ldata
, UINT lcount
,
150 UINT
*rdata
, UINT rcount
,
151 UINT
**ppairs
, UINT
*ppair_count
)
156 TRACE("left %u right %u\n", rcount
, lcount
);
158 /* there can be at most max(lcount, rcount) matches */
164 pairs
= msi_alloc( n
* 2 * sizeof(UINT
) );
166 return ERROR_OUTOFMEMORY
;
168 for (n
=0, i
=0, j
=0; i
<lcount
&& j
<rcount
; )
170 /* values match... store the row numbers */
171 if (ldata
[i
*2+1] == rdata
[j
*2+1])
173 pairs
[n
*2] = ldata
[i
*2];
174 pairs
[n
*2+1] = rdata
[j
*2];
177 if ( ldata
[i
*2+3] < rdata
[j
*2+3])
183 /* values differ... move along */
184 else if (ldata
[i
*2+1] < rdata
[j
*2+1])
193 return ERROR_SUCCESS
;
196 static UINT
JOIN_execute( struct tagMSIVIEW
*view
, MSIRECORD
*record
)
198 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
199 UINT r
, *ldata
= NULL
, *rdata
= NULL
, lcount
= 0, rcount
= 0;
201 TRACE("%p %p\n", jv
, record
);
203 if( !jv
->left
|| !jv
->right
)
204 return ERROR_FUNCTION_FAILED
;
206 r
= jv
->left
->ops
->execute( jv
->left
, NULL
);
207 if (r
!= ERROR_SUCCESS
)
210 r
= jv
->right
->ops
->execute( jv
->right
, NULL
);
211 if (r
!= ERROR_SUCCESS
)
214 r
= join_load_key_column( jv
, jv
->left
, jv
->left_key
, &ldata
, &lcount
);
215 if (r
!= ERROR_SUCCESS
)
218 r
= join_load_key_column( jv
, jv
->right
, jv
->right_key
, &rdata
, &rcount
);
219 if (r
!= ERROR_SUCCESS
)
222 r
= join_match( ldata
, lcount
, rdata
, rcount
, &jv
->pairs
, &jv
->pair_count
);
231 static UINT
JOIN_close( struct tagMSIVIEW
*view
)
233 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
237 if( !jv
->left
|| !jv
->right
)
238 return ERROR_FUNCTION_FAILED
;
240 jv
->left
->ops
->close( jv
->left
);
241 jv
->right
->ops
->close( jv
->right
);
243 return ERROR_SUCCESS
;
246 static UINT
JOIN_get_dimensions( struct tagMSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
248 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
250 TRACE("%p %p %p\n", jv
, rows
, cols
);
253 *cols
= jv
->left_count
+ jv
->right_count
;
257 if( !jv
->left
|| !jv
->right
)
258 return ERROR_FUNCTION_FAILED
;
260 *rows
= jv
->pair_count
;
263 return ERROR_SUCCESS
;
266 static UINT
JOIN_get_column_info( struct tagMSIVIEW
*view
,
267 UINT n
, LPWSTR
*name
, UINT
*type
)
269 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
271 TRACE("%p %d %p %p\n", jv
, n
, name
, type
);
273 if( !jv
->left
|| !jv
->right
)
274 return ERROR_FUNCTION_FAILED
;
276 if( (n
==0) || (n
>(jv
->left_count
+ jv
->right_count
)) )
277 return ERROR_FUNCTION_FAILED
;
279 if( n
<= jv
->left_count
)
280 return jv
->left
->ops
->get_column_info( jv
->left
, n
, name
, type
);
282 n
= n
- jv
->left_count
;
284 return jv
->right
->ops
->get_column_info( jv
->right
, n
, name
, type
);
287 static UINT
JOIN_modify( struct tagMSIVIEW
*view
, MSIMODIFY eModifyMode
,
290 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
292 TRACE("%p %d %p\n", jv
, eModifyMode
, rec
);
294 return ERROR_FUNCTION_FAILED
;
297 static UINT
JOIN_delete( struct tagMSIVIEW
*view
)
299 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
304 jv
->left
->ops
->delete( jv
->left
);
308 jv
->right
->ops
->delete( jv
->right
);
311 msi_free( jv
->pairs
);
316 return ERROR_SUCCESS
;
319 static UINT
JOIN_find_matching_rows( struct tagMSIVIEW
*view
, UINT col
,
320 UINT val
, UINT
*row
, MSIITERHANDLE
*handle
)
322 MSIJOINVIEW
*jv
= (MSIJOINVIEW
*)view
;
324 FIXME("%p, %d, %u, %p\n", jv
, col
, val
, *handle
);
326 return ERROR_FUNCTION_FAILED
;
329 static const MSIVIEWOPS join_ops
=
338 JOIN_get_column_info
,
341 JOIN_find_matching_rows
345 * join_check_condition
347 * This is probably overly strict about what kind of condition we need
350 static UINT
join_check_condition(MSIJOINVIEW
*jv
, struct expr
*cond
)
354 /* assume that we have `KeyColumn` = `SubkeyColumn` */
355 if ( cond
->type
!= EXPR_COMPLEX
)
356 return ERROR_FUNCTION_FAILED
;
358 if ( cond
->u
.expr
.op
!= OP_EQ
)
359 return ERROR_FUNCTION_FAILED
;
361 if ( cond
->u
.expr
.left
->type
!= EXPR_COLUMN
)
362 return ERROR_FUNCTION_FAILED
;
364 if ( cond
->u
.expr
.right
->type
!= EXPR_COLUMN
)
365 return ERROR_FUNCTION_FAILED
;
367 /* make sure both columns exist */
368 r
= VIEW_find_column( jv
->left
, cond
->u
.expr
.left
->u
.column
, &jv
->left_key
);
369 if (r
!= ERROR_SUCCESS
)
370 return ERROR_FUNCTION_FAILED
;
372 r
= VIEW_find_column( jv
->right
, cond
->u
.expr
.right
->u
.column
, &jv
->right_key
);
373 if (r
!= ERROR_SUCCESS
)
374 return ERROR_FUNCTION_FAILED
;
376 TRACE("left %s (%u) right %s (%u)\n",
377 debugstr_w(cond
->u
.expr
.left
->u
.column
), jv
->left_key
,
378 debugstr_w(cond
->u
.expr
.right
->u
.column
), jv
->right_key
);
380 return ERROR_SUCCESS
;
383 UINT
JOIN_CreateView( MSIDATABASE
*db
, MSIVIEW
**view
,
384 LPCWSTR left
, LPCWSTR right
,
387 MSIJOINVIEW
*jv
= NULL
;
388 UINT r
= ERROR_SUCCESS
;
390 TRACE("%p (%s,%s)\n", jv
, debugstr_w(left
), debugstr_w(right
) );
392 jv
= msi_alloc_zero( sizeof *jv
);
394 return ERROR_FUNCTION_FAILED
;
396 /* fill the structure */
397 jv
->view
.ops
= &join_ops
;
400 /* create the tables to join */
401 r
= TABLE_CreateView( db
, left
, &jv
->left
);
402 if( r
!= ERROR_SUCCESS
)
404 ERR("can't create left table\n");
408 r
= TABLE_CreateView( db
, right
, &jv
->right
);
409 if( r
!= ERROR_SUCCESS
)
411 ERR("can't create right table\n");
415 /* get the number of columns in each table */
416 r
= jv
->left
->ops
->get_dimensions( jv
->left
, NULL
, &jv
->left_count
);
417 if( r
!= ERROR_SUCCESS
)
419 ERR("can't get left table dimensions\n");
423 r
= jv
->right
->ops
->get_dimensions( jv
->right
, NULL
, &jv
->right_count
);
424 if( r
!= ERROR_SUCCESS
)
426 ERR("can't get right table dimensions\n");
430 r
= join_check_condition( jv
, cond
);
431 if( r
!= ERROR_SUCCESS
)
433 ERR("can't get join condition\n");
438 return ERROR_SUCCESS
;
441 jv
->view
.ops
->delete( &jv
->view
);