msi: Add tests for MSI SQL join queries.
[wine] / dlls / msi / tests / db.c
1 /*
2  * Copyright (C) 2005 Mike McCormack for CodeWeavers
3  *
4  * A test program for MSI database files.
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 #include <stdio.h>
21
22 #include <windows.h>
23 #include <msi.h>
24 #include <msiquery.h>
25
26 #include "wine/test.h"
27
28 static const char *msifile = "winetest.msi";
29 static const char *msifile2 = "winetst2.msi";
30 static const char *mstfile = "winetst.mst";
31
32 #ifndef ERROR_INSTALL_TRANSFORM_FAILURE
33 #define ERROR_INSTALL_TRANSFORM_FAILURE 1624
34 #endif
35
36 static void test_msidatabase(void)
37 {
38     MSIHANDLE hdb = 0;
39     UINT res;
40
41     DeleteFile(msifile);
42
43     /* create an empty database */
44     res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
45     ok( res == ERROR_SUCCESS , "Failed to create database\n" );
46
47     res = MsiDatabaseCommit( hdb );
48     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
49
50     res = MsiCloseHandle( hdb );
51     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
52
53     res = DeleteFile( msifile );
54     ok( res == TRUE, "Failed to delete database\n" );
55 }
56
57 static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec)
58 {
59     MSIHANDLE hview = 0;
60     UINT r, ret;
61
62     /* open a select query */
63     r = MsiDatabaseOpenView(hdb, query, &hview);
64     if (r != ERROR_SUCCESS)
65         return r;
66     r = MsiViewExecute(hview, 0);
67     if (r != ERROR_SUCCESS)
68         return r;
69     ret = MsiViewFetch(hview, phrec);
70     r = MsiViewClose(hview);
71     if (r != ERROR_SUCCESS)
72         return r;
73     r = MsiCloseHandle(hview);
74     if (r != ERROR_SUCCESS)
75         return r;
76     return ret;
77 }
78
79 static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query )
80 {
81     MSIHANDLE hview = 0;
82     UINT r;
83
84     r = MsiDatabaseOpenView(hdb, query, &hview);
85     if( r != ERROR_SUCCESS )
86         return r;
87
88     r = MsiViewExecute(hview, hrec);
89     if( r == ERROR_SUCCESS )
90         r = MsiViewClose(hview);
91     MsiCloseHandle(hview);
92     return r;
93 }
94
95 static UINT create_component_table( MSIHANDLE hdb )
96 {
97     return run_query( hdb, 0,
98             "CREATE TABLE `Component` ( "
99             "`Component` CHAR(72) NOT NULL, "
100             "`ComponentId` CHAR(38), "
101             "`Directory_` CHAR(72) NOT NULL, "
102             "`Attributes` SHORT NOT NULL, "
103             "`Condition` CHAR(255), "
104             "`KeyPath` CHAR(72) "
105             "PRIMARY KEY `Component`)" );
106 }
107
108 static UINT create_feature_components_table( MSIHANDLE hdb )
109 {
110     return run_query( hdb, 0,
111             "CREATE TABLE `FeatureComponents` ( "
112             "`Feature_` CHAR(38) NOT NULL, "
113             "`Component_` CHAR(72) NOT NULL "
114             "PRIMARY KEY `Feature_`, `Component_` )" );
115 }
116
117 static UINT create_std_dlls_table( MSIHANDLE hdb )
118 {
119     return run_query( hdb, 0,
120             "CREATE TABLE `StdDlls` ( "
121             "`File` CHAR(255) NOT NULL, "
122             "`Binary_` CHAR(72) NOT NULL "
123             "PRIMARY KEY `File` )" );
124 }
125
126 static UINT create_binary_table( MSIHANDLE hdb )
127 {
128     return run_query( hdb, 0,
129             "CREATE TABLE `Binary` ( "
130             "`Name` CHAR(72) NOT NULL, "
131             "`Data` CHAR(72) NOT NULL "
132             "PRIMARY KEY `Name` )" );
133 }
134
135 static UINT add_component_entry( MSIHANDLE hdb, const char *values )
136 {
137     char insert[] = "INSERT INTO `Component`  "
138             "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
139             "VALUES( %s )";
140     char *query;
141     UINT sz, r;
142
143     sz = strlen(values) + sizeof insert;
144     query = HeapAlloc(GetProcessHeap(),0,sz);
145     sprintf(query,insert,values);
146     r = run_query( hdb, 0, query );
147     HeapFree(GetProcessHeap(), 0, query);
148     return r;
149 }
150
151 static UINT add_feature_components_entry( MSIHANDLE hdb, const char *values )
152 {
153     char insert[] = "INSERT INTO `FeatureComponents` "
154             "(`Feature_`, `Component_`) "
155             "VALUES( %s )";
156     char *query;
157     UINT sz, r;
158
159     sz = strlen(values) + sizeof insert;
160     query = HeapAlloc(GetProcessHeap(),0,sz);
161     sprintf(query,insert,values);
162     r = run_query( hdb, 0, query );
163     HeapFree(GetProcessHeap(), 0, query);
164     return r;
165 }
166
167 static UINT add_std_dlls_entry( MSIHANDLE hdb, const char *values )
168 {
169     char insert[] = "INSERT INTO `StdDlls` "
170             "(`File`, `Binary_`) "
171             "VALUES( %s )";
172     char *query;
173     UINT sz, r;
174
175     sz = strlen(values) + sizeof insert;
176     query = HeapAlloc(GetProcessHeap(),0,sz);
177     sprintf(query,insert,values);
178     r = run_query( hdb, 0, query );
179     HeapFree(GetProcessHeap(), 0, query);
180     return r;
181 }
182
183 static UINT add_binary_entry( MSIHANDLE hdb, const char *values )
184 {
185     char insert[] = "INSERT INTO `Binary` "
186             "(`Name`, `Data`) "
187             "VALUES( %s )";
188     char *query;
189     UINT sz, r;
190
191     sz = strlen(values) + sizeof insert;
192     query = HeapAlloc(GetProcessHeap(),0,sz);
193     sprintf(query,insert,values);
194     r = run_query( hdb, 0, query );
195     HeapFree(GetProcessHeap(), 0, query);
196     return r;
197 }
198
199 static void test_msiinsert(void)
200 {
201     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
202     UINT r;
203     const char *query;
204     char buf[80];
205     DWORD sz;
206
207     DeleteFile(msifile);
208
209     /* just MsiOpenDatabase should not create a file */
210     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
211     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
212
213     /* create a table */
214     query = "CREATE TABLE `phone` ( "
215             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
216             "PRIMARY KEY `id`)";
217     r = MsiDatabaseOpenView(hdb, query, &hview);
218     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
219     r = MsiViewExecute(hview, 0);
220     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
221     r = MsiViewClose(hview);
222     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
223     r = MsiCloseHandle(hview);
224     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
225
226     /* insert a value into it */
227     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
228         "VALUES('1', 'Abe', '8675309')";
229     r = MsiDatabaseOpenView(hdb, query, &hview);
230     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
231     r = MsiViewExecute(hview, 0);
232     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
233     r = MsiViewClose(hview);
234     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
235     r = MsiCloseHandle(hview);
236     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
237
238     query = "SELECT * FROM `phone` WHERE `id` = 1";
239     r = do_query(hdb, query, &hrec);
240     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
241
242     /* check the record contains what we put in it */
243     r = MsiRecordGetFieldCount(hrec);
244     ok(r == 3, "record count wrong\n");
245
246     todo_wine {
247     r = MsiRecordIsNull(hrec, 0);
248     ok(r == FALSE, "field 0 not null\n");
249     }
250
251     r = MsiRecordGetInteger(hrec, 1);
252     ok(r == 1, "field 1 contents wrong\n");
253     sz = sizeof buf;
254     r = MsiRecordGetString(hrec, 2, buf, &sz);
255     ok(r == ERROR_SUCCESS, "field 2 content fetch failed\n");
256     ok(!strcmp(buf,"Abe"), "field 2 content incorrect\n");
257     sz = sizeof buf;
258     r = MsiRecordGetString(hrec, 3, buf, &sz);
259     ok(r == ERROR_SUCCESS, "field 3 content fetch failed\n");
260     ok(!strcmp(buf,"8675309"), "field 3 content incorrect\n");
261
262     r = MsiCloseHandle(hrec);
263     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
264
265     /* open a select query */
266     hrec = 100;
267     query = "SELECT * FROM `phone` WHERE `id` >= 10";
268     r = do_query(hdb, query, &hrec);
269     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
270     ok(hrec == 0, "hrec should be null\n");
271
272     r = MsiCloseHandle(hrec);
273     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
274
275     query = "SELECT * FROM `phone` WHERE `id` < 0";
276     r = do_query(hdb, query, &hrec);
277     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
278
279     query = "SELECT * FROM `phone` WHERE `id` <= 0";
280     r = do_query(hdb, query, &hrec);
281     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
282
283     query = "SELECT * FROM `phone` WHERE `id` <> 1";
284     r = do_query(hdb, query, &hrec);
285     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
286
287     query = "SELECT * FROM `phone` WHERE `id` > 10";
288     r = do_query(hdb, query, &hrec);
289     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
290
291     /* now try a few bad INSERT xqueries */
292     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
293         "VALUES(?, ?)";
294     r = MsiDatabaseOpenView(hdb, query, &hview);
295     ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n");
296
297     /* construct a record to insert */
298     hrec = MsiCreateRecord(4);
299     r = MsiRecordSetInteger(hrec, 1, 2);
300     ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n");
301     r = MsiRecordSetString(hrec, 2, "Adam");
302     ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
303     r = MsiRecordSetString(hrec, 3, "96905305");
304     ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
305
306     /* insert another value, using a record and wildcards */
307     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
308         "VALUES(?, ?, ?)";
309     r = MsiDatabaseOpenView(hdb, query, &hview);
310     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
311
312     if (r == ERROR_SUCCESS)
313     {
314         r = MsiViewExecute(hview, hrec);
315         ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
316         r = MsiViewClose(hview);
317         ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
318         r = MsiCloseHandle(hview);
319         ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
320     }
321     r = MsiCloseHandle(hrec);
322     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
323
324     r = MsiViewFetch(0, NULL);
325     ok(r == ERROR_INVALID_PARAMETER, "MsiViewFetch failed\n");
326
327     r = MsiDatabaseCommit(hdb);
328     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
329
330     r = MsiCloseHandle(hdb);
331     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
332
333     r = DeleteFile(msifile);
334     ok(r == TRUE, "file didn't exist after commit\n");
335 }
336
337 typedef UINT (WINAPI *fnMsiDecomposeDescriptorA)(LPCSTR, LPCSTR, LPSTR, LPSTR, DWORD *);
338 static fnMsiDecomposeDescriptorA pMsiDecomposeDescriptorA;
339
340 static void test_msidecomposedesc(void)
341 {
342     char prod[MAX_FEATURE_CHARS+1], comp[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1];
343     const char *desc;
344     UINT r;
345     DWORD len;
346     HMODULE hmod;
347
348     hmod = GetModuleHandle("msi.dll");
349     if (!hmod)
350         return;
351     pMsiDecomposeDescriptorA = (fnMsiDecomposeDescriptorA)
352         GetProcAddress(hmod, "MsiDecomposeDescriptorA");
353     if (!pMsiDecomposeDescriptorA)
354         return;
355
356     /* test a valid feature descriptor */
357     desc = "']gAVn-}f(ZXfeAR6.jiFollowTheWhiteRabbit>3w2x^IGfe?CxI5heAvk.";
358     len = 0;
359     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
360     ok(r == ERROR_SUCCESS, "returned an error\n");
361     ok(len == strlen(desc), "length was wrong\n");
362     ok(strcmp(prod,"{90110409-6000-11D3-8CFE-0150048383C9}")==0, "product wrong\n");
363     ok(strcmp(feature,"FollowTheWhiteRabbit")==0, "feature wrong\n");
364     ok(strcmp(comp,"{A7CD68DB-EF74-49C8-FBB2-A7C463B2AC24}")==0,"component wrong\n");
365
366     /* test an invalid feature descriptor with too many characters */
367     desc = "']gAVn-}f(ZXfeAR6.ji"
368            "ThisWillFailIfTheresMoreThanAGuidsChars>"
369            "3w2x^IGfe?CxI5heAvk.";
370     len = 0;
371     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
372     ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
373
374     /*
375      * Test a valid feature descriptor with the
376      * maximum number of characters and some trailing characters.
377      */
378     desc = "']gAVn-}f(ZXfeAR6.ji"
379            "ThisWillWorkIfTheresLTEThanAGuidsChars>"
380            "3w2x^IGfe?CxI5heAvk."
381            "extra";
382     len = 0;
383     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
384     ok(r == ERROR_SUCCESS, "returned wrong error\n");
385     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
386
387     len = 0;
388     r = pMsiDecomposeDescriptorA(desc, prod, feature, NULL, &len);
389     ok(r == ERROR_SUCCESS, "returned wrong error\n");
390     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
391
392     len = 0;
393     r = pMsiDecomposeDescriptorA(desc, prod, NULL, NULL, &len);
394     ok(r == ERROR_SUCCESS, "returned wrong error\n");
395     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
396
397     len = 0;
398     r = pMsiDecomposeDescriptorA(desc, NULL, NULL, NULL, &len);
399     ok(r == ERROR_SUCCESS, "returned wrong error\n");
400     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
401
402     len = 0;
403     r = pMsiDecomposeDescriptorA(NULL, NULL, NULL, NULL, &len);
404     ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
405     ok(len == 0, "length wrong\n");
406 }
407
408 static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
409 {
410     MSIHANDLE htab = 0;
411     UINT res;
412
413     res = MsiDatabaseOpenView( hdb, szQuery, &htab );
414     if(res == ERROR_SUCCESS )
415     {
416         UINT r;
417
418         r = MsiViewExecute( htab, hrec );
419         if(r != ERROR_SUCCESS )
420             res = r;
421
422         r = MsiViewClose( htab );
423         if(r != ERROR_SUCCESS )
424             res = r;
425
426         r = MsiCloseHandle( htab );
427         if(r != ERROR_SUCCESS )
428             res = r;
429     }
430     return res;
431 }
432
433 static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
434 {
435     return try_query_param( hdb, szQuery, 0 );
436 }
437
438 static UINT try_insert_query( MSIHANDLE hdb, LPCSTR szQuery )
439 {
440     MSIHANDLE hrec = 0;
441     UINT r;
442
443     hrec = MsiCreateRecord( 1 );
444     MsiRecordSetString( hrec, 1, "Hello");
445
446     r = try_query_param( hdb, szQuery, hrec );
447
448     MsiCloseHandle( hrec );
449     return r;
450 }
451
452 static void test_msibadqueries(void)
453 {
454     MSIHANDLE hdb = 0;
455     UINT r;
456
457     DeleteFile(msifile);
458
459     /* just MsiOpenDatabase should not create a file */
460     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
461     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
462
463     r = MsiDatabaseCommit( hdb );
464     ok(r == ERROR_SUCCESS , "Failed to commit database\n");
465
466     r = MsiCloseHandle( hdb );
467     ok(r == ERROR_SUCCESS , "Failed to close database\n");
468
469     /* open it readonly */
470     r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb );
471     ok(r == ERROR_SUCCESS , "Failed to open database r/o\n");
472
473     /* add a table to it */
474     r = try_query( hdb, "select * from _Tables");
475     ok(r == ERROR_SUCCESS , "query 1 failed\n");
476
477     r = MsiCloseHandle( hdb );
478     ok(r == ERROR_SUCCESS , "Failed to close database r/o\n");
479
480     /* open it read/write */
481     r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb );
482     ok(r == ERROR_SUCCESS , "Failed to open database r/w\n");
483
484     /* a bunch of test queries that fail with the native MSI */
485
486     r = try_query( hdb, "CREATE TABLE");
487     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2a return code\n");
488
489     r = try_query( hdb, "CREATE TABLE `a`");
490     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2b return code\n");
491
492     r = try_query( hdb, "CREATE TABLE `a` ()");
493     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2c return code\n");
494
495     r = try_query( hdb, "CREATE TABLE `a` (`b`)");
496     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2d return code\n");
497
498     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) )");
499     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2e return code\n");
500
501     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL)");
502     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2f return code\n");
503
504     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY)");
505     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2g return code\n");
506
507     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
508     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2h return code\n");
509
510     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
511     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2i return code\n");
512
513     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY 'b')");
514     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2j return code\n");
515
516     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
517     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2k return code\n");
518
519     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
520     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2l return code\n");
521
522     r = try_query( hdb, "CREATE TABLE `a` (`b` CHA(72) NOT NULL PRIMARY KEY `b`)");
523     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2m return code\n");
524
525     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(-1) NOT NULL PRIMARY KEY `b`)");
526     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2n return code\n");
527
528     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(720) NOT NULL PRIMARY KEY `b`)");
529     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2o return code\n");
530
531     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL KEY `b`)");
532     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
533
534     r = try_query( hdb, "CREATE TABLE `a` (`` CHAR(72) NOT NULL PRIMARY KEY `b`)");
535     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
536
537     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
538     ok(r == ERROR_SUCCESS , "valid query 2z failed\n");
539
540     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
541     ok(r == ERROR_BAD_QUERY_SYNTAX , "created same table again\n");
542
543     r = try_query( hdb, "CREATE TABLE `aa` (`b` CHAR(72) NOT NULL, `c` "
544           "CHAR(72), `d` CHAR(255) NOT NULL LOCALIZABLE PRIMARY KEY `b`)");
545     ok(r == ERROR_SUCCESS , "query 4 failed\n");
546
547     r = MsiDatabaseCommit( hdb );
548     ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
549
550     r = try_query( hdb, "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL "
551                           "PRIMARY KEY `foo`)");
552     ok(r == ERROR_SUCCESS , "query 4 failed\n");
553
554     r = try_insert_query( hdb, "insert into a  ( `b` ) VALUES ( ? )");
555     ok(r == ERROR_SUCCESS , "failed to insert record in db\n");
556
557     r = MsiDatabaseCommit( hdb );
558     ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
559
560     r = try_query( hdb, "CREATE TABLE `boo` (`foo` CHAR(72) NOT NULL "
561                           "PRIMARY KEY `ba`)");
562     ok(r != ERROR_SUCCESS , "query 5 succeeded\n");
563
564     r = try_query( hdb,"CREATE TABLE `bee` (`foo` CHAR(72) NOT NULL )");
565     ok(r != ERROR_SUCCESS , "query 6 succeeded\n");
566
567     r = try_query( hdb, "CREATE TABLE `temp` (`t` CHAR(72) NOT NULL "
568                           "PRIMARY KEY `t`)");
569     ok(r == ERROR_SUCCESS , "query 7 failed\n");
570
571     r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)");
572     ok(r == ERROR_SUCCESS , "query 8 failed\n");
573
574     r = MsiCloseHandle( hdb );
575     ok(r == ERROR_SUCCESS , "Failed to close database transact\n");
576
577     r = DeleteFile( msifile );
578     ok(r == TRUE, "file didn't exist after commit\n");
579 }
580
581 static void test_viewmodify(void)
582 {
583     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
584     UINT r;
585     const char *query;
586     char buffer[0x100];
587     DWORD sz;
588
589     DeleteFile(msifile);
590
591     /* just MsiOpenDatabase should not create a file */
592     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
593     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
594
595     query = "CREATE TABLE `phone` ( "
596             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
597             "PRIMARY KEY `id`)";
598     r = run_query( hdb, 0, query );
599     ok(r == ERROR_SUCCESS, "query failed\n");
600
601     /* check what the error function reports without doing anything */
602     sz = 0;
603     /* passing NULL as the 3rd param make function to crash on older platforms */
604     r = MsiViewGetError( 0, NULL, &sz );
605     ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
606
607     /* open a view */
608     query = "SELECT * FROM `phone`";
609     r = MsiDatabaseOpenView(hdb, query, &hview);
610     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
611
612     /* see what happens with a good hview and bad args */
613     r = MsiViewGetError( hview, NULL, NULL );
614     ok(r == MSIDBERROR_INVALIDARG || r == MSIDBERROR_NOERROR,
615        "MsiViewGetError returns %u (expected -3)\n", r);
616     r = MsiViewGetError( hview, buffer, NULL );
617     ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
618
619     /* see what happens with a zero length buffer */
620     sz = 0;
621     buffer[0] = 'x';
622     r = MsiViewGetError( hview, buffer, &sz );
623     ok(r == MSIDBERROR_MOREDATA, "MsiViewGetError return\n");
624     ok(buffer[0] == 'x', "buffer cleared\n");
625     ok(sz == 0, "size not zero\n");
626
627     /* ok this one is strange */
628     sz = 0;
629     r = MsiViewGetError( hview, NULL, &sz );
630     ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
631     ok(sz == 0, "size not zero\n");
632
633     /* see if it really has an error */
634     sz = sizeof buffer;
635     buffer[0] = 'x';
636     r = MsiViewGetError( hview, buffer, &sz );
637     ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
638     ok(buffer[0] == 0, "buffer not cleared\n");
639     ok(sz == 0, "size not zero\n");
640
641     r = MsiViewExecute(hview, 0);
642     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
643
644     /* try some invalid records */
645     r = MsiViewModify(hview, MSIMODIFY_INSERT, 0 );
646     ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
647     r = MsiViewModify(hview, -1, 0 );
648     ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
649
650     /* try an small record */
651     hrec = MsiCreateRecord(1);
652     r = MsiViewModify(hview, -1, hrec );
653     ok(r == ERROR_INVALID_DATA, "MsiViewModify failed\n");
654
655     r = MsiCloseHandle(hrec);
656     ok(r == ERROR_SUCCESS, "failed to close record\n");
657
658     /* insert a valid record */
659     hrec = MsiCreateRecord(3);
660
661     r = MsiRecordSetInteger(hrec, 2, 1);
662     ok(r == ERROR_SUCCESS, "failed to set integer\n");
663     r = MsiRecordSetString(hrec, 2, "bob");
664     ok(r == ERROR_SUCCESS, "failed to set integer\n");
665     r = MsiRecordSetString(hrec, 3, "7654321");
666     ok(r == ERROR_SUCCESS, "failed to set integer\n");
667
668     r = MsiViewExecute(hview, 0);
669     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
670     r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
671     ok(r == ERROR_SUCCESS, "MsiViewModify failed\n");
672
673     /* insert the same thing again */
674     r = MsiViewExecute(hview, 0);
675     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
676
677     /* should fail ... */
678     todo_wine {
679     r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
680     ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n");
681     }
682
683     r = MsiCloseHandle(hrec);
684     ok(r == ERROR_SUCCESS, "failed to close record\n");
685
686     r = MsiViewClose(hview);
687     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
688     r = MsiCloseHandle(hview);
689     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
690
691     r = MsiCloseHandle( hdb );
692     ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
693 }
694
695 static MSIHANDLE create_db(void)
696 {
697     MSIHANDLE hdb = 0;
698     UINT res;
699
700     DeleteFile(msifile);
701
702     /* create an empty database */
703     res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
704     ok( res == ERROR_SUCCESS , "Failed to create database\n" );
705     if( res != ERROR_SUCCESS )
706         return hdb;
707
708     res = MsiDatabaseCommit( hdb );
709     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
710
711     return hdb;
712 }
713
714 static void test_getcolinfo(void)
715 {
716     MSIHANDLE hdb, hview = 0, rec = 0;
717     UINT r;
718     DWORD sz;
719     char buffer[0x20];
720
721     /* create an empty db */
722     hdb = create_db();
723     ok( hdb, "failed to create db\n");
724
725     /* tables should be present */
726     r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview);
727     ok( r == ERROR_SUCCESS, "failed to open query\n");
728
729     r = MsiViewExecute(hview, 0);
730     ok( r == ERROR_SUCCESS, "failed to execute query\n");
731
732     /* check that NAMES works */
733     rec = 0;
734     r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec );
735     ok( r == ERROR_SUCCESS, "failed to get names\n");
736     sz = sizeof buffer;
737     r = MsiRecordGetString(rec, 1, buffer, &sz );
738     ok( r == ERROR_SUCCESS, "failed to get string\n");
739     ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n");
740     r = MsiCloseHandle( rec );
741     ok( r == ERROR_SUCCESS, "failed to close record handle\n");
742
743     /* check that TYPES works */
744     rec = 0;
745     r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec );
746     ok( r == ERROR_SUCCESS, "failed to get names\n");
747     sz = sizeof buffer;
748     r = MsiRecordGetString(rec, 1, buffer, &sz );
749     ok( r == ERROR_SUCCESS, "failed to get string\n");
750     ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n");
751     r = MsiCloseHandle( rec );
752     ok( r == ERROR_SUCCESS, "failed to close record handle\n");
753
754     /* check that invalid values fail */
755     rec = 0;
756     r = MsiViewGetColumnInfo( hview, 100, &rec );
757     ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
758     ok( rec == 0, "returned a record\n");
759
760     r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL );
761     ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
762
763     r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec );
764     ok( r == ERROR_INVALID_HANDLE, "wrong error code\n");
765
766     r = MsiViewClose(hview);
767     ok( r == ERROR_SUCCESS, "failed to close view\n");
768     r = MsiCloseHandle(hview);
769     ok( r == ERROR_SUCCESS, "failed to close view handle\n");
770     r = MsiCloseHandle(hdb);
771     ok( r == ERROR_SUCCESS, "failed to close database\n");
772 }
773
774 static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type)
775 {
776     MSIHANDLE hview = 0, rec = 0;
777     UINT r;
778
779     r = MsiDatabaseOpenView(hdb, query, &hview);
780     if( r != ERROR_SUCCESS )
781         return r;
782
783     r = MsiViewExecute(hview, 0);
784     if( r == ERROR_SUCCESS )
785     {
786         MsiViewGetColumnInfo( hview, type, &rec );
787         MsiViewClose(hview);
788     }
789     MsiCloseHandle(hview);
790     return rec;
791 }
792
793 static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field)
794 {
795     MSIHANDLE hview = 0, rec = 0;
796     UINT r, type = 0;
797     char query[0x100];
798
799     sprintf(query, "select * from `_Columns` where  `Table` = '%s'", table );
800
801     r = MsiDatabaseOpenView(hdb, query, &hview);
802     if( r != ERROR_SUCCESS )
803         return r;
804
805     r = MsiViewExecute(hview, 0);
806     if( r == ERROR_SUCCESS )
807     {
808         while (1)
809         {
810             r = MsiViewFetch( hview, &rec );
811             if( r != ERROR_SUCCESS)
812                 break;
813             r = MsiRecordGetInteger( rec, 2 );
814             if (r == field)
815                 type = MsiRecordGetInteger( rec, 4 );
816             MsiCloseHandle( rec );
817         }
818
819         MsiViewClose(hview);
820     }
821     MsiCloseHandle(hview);
822     return type;
823 }
824
825 static BOOL check_record( MSIHANDLE rec, UINT field, LPCSTR val )
826 {
827     CHAR buffer[0x20];
828     UINT r;
829     DWORD sz;
830
831     sz = sizeof buffer;
832     r = MsiRecordGetString( rec, field, buffer, &sz );
833     return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
834 }
835
836 static void test_viewgetcolumninfo(void)
837 {
838     MSIHANDLE hdb = 0, rec;
839     UINT r;
840
841     hdb = create_db();
842     ok( hdb, "failed to create db\n");
843
844     r = run_query( hdb, 0,
845             "CREATE TABLE `Properties` "
846             "( `Property` CHAR(255), `Value` CHAR(1)  PRIMARY KEY `Property`)" );
847     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
848
849     /* check the column types */
850     rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES );
851     ok( rec, "failed to get column info record\n" );
852
853     ok( check_record( rec, 1, "S255"), "wrong record type\n");
854     ok( check_record( rec, 2, "S1"), "wrong record type\n");
855
856     MsiCloseHandle( rec );
857
858     /* check the type in _Columns */
859     ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n");
860     ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n");
861
862     /* now try the names */
863     rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES );
864     ok( rec, "failed to get column info record\n" );
865
866     ok( check_record( rec, 1, "Property"), "wrong record type\n");
867     ok( check_record( rec, 2, "Value"), "wrong record type\n");
868
869     MsiCloseHandle( rec );
870
871     r = run_query( hdb, 0,
872             "CREATE TABLE `Binary` "
873             "( `Name` CHAR(255), `Data` OBJECT  PRIMARY KEY `Name`)" );
874     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
875
876     /* check the column types */
877     rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES );
878     ok( rec, "failed to get column info record\n" );
879
880     ok( check_record( rec, 1, "S255"), "wrong record type\n");
881     ok( check_record( rec, 2, "V0"), "wrong record type\n");
882
883     MsiCloseHandle( rec );
884
885     /* check the type in _Columns */
886     ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n");
887     ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n");
888
889     /* now try the names */
890     rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES );
891     ok( rec, "failed to get column info record\n" );
892
893     ok( check_record( rec, 1, "Name"), "wrong record type\n");
894     ok( check_record( rec, 2, "Data"), "wrong record type\n");
895     MsiCloseHandle( rec );
896
897     r = run_query( hdb, 0,
898             "CREATE TABLE `UIText` "
899             "( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" );
900     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
901
902     ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n");
903     ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n");
904
905     rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES );
906     ok( rec, "failed to get column info record\n" );
907     ok( check_record( rec, 1, "Key"), "wrong record type\n");
908     ok( check_record( rec, 2, "Text"), "wrong record type\n");
909     MsiCloseHandle( rec );
910
911     rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES );
912     ok( rec, "failed to get column info record\n" );
913     ok( check_record( rec, 1, "s72"), "wrong record type\n");
914     ok( check_record( rec, 2, "L255"), "wrong record type\n");
915     MsiCloseHandle( rec );
916
917     MsiCloseHandle( hdb );
918 }
919
920 static void test_msiexport(void)
921 {
922     MSIHANDLE hdb = 0, hview = 0;
923     UINT r;
924     const char *query;
925     char path[MAX_PATH];
926     const char file[] = "phone.txt";
927     HANDLE handle;
928     char buffer[0x100];
929     DWORD length;
930     const char expected[] =
931         "id\tname\tnumber\r\n"
932         "I2\tS32\tS32\r\n"
933         "phone\tid\r\n"
934         "1\tAbe\t8675309\r\n";
935
936     DeleteFile(msifile);
937
938     /* just MsiOpenDatabase should not create a file */
939     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
940     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
941
942     /* create a table */
943     query = "CREATE TABLE `phone` ( "
944             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
945             "PRIMARY KEY `id`)";
946     r = MsiDatabaseOpenView(hdb, query, &hview);
947     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
948     r = MsiViewExecute(hview, 0);
949     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
950     r = MsiViewClose(hview);
951     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
952     r = MsiCloseHandle(hview);
953     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
954
955     /* insert a value into it */
956     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
957         "VALUES('1', 'Abe', '8675309')";
958     r = MsiDatabaseOpenView(hdb, query, &hview);
959     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
960     r = MsiViewExecute(hview, 0);
961     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
962     r = MsiViewClose(hview);
963     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
964     r = MsiCloseHandle(hview);
965     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
966
967     GetCurrentDirectory(MAX_PATH, path);
968
969     r = MsiDatabaseExport(hdb, "phone", path, file);
970     ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n");
971
972     MsiCloseHandle(hdb);
973
974     lstrcat(path, "\\");
975     lstrcat(path, file);
976
977     /* check the data that was written */
978     length = 0;
979     memset(buffer, 0, sizeof buffer);
980     handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
981     if (handle != INVALID_HANDLE_VALUE)
982     {
983         ReadFile(handle, buffer, sizeof buffer, &length, NULL);
984         CloseHandle(handle);
985         DeleteFile(path);
986     }
987     else
988         ok(0, "failed to open file %s\n", path);
989
990     ok( length == strlen(expected), "length of data wrong\n");
991     ok( !lstrcmp(buffer, expected), "data doesn't match\n");
992     DeleteFile(msifile);
993 }
994
995 static void test_longstrings(void)
996 {
997     const char insert_query[] = 
998         "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')";
999     char *str;
1000     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
1001     DWORD len;
1002     UINT r;
1003     const DWORD STRING_LENGTH = 0x10005;
1004
1005     DeleteFile(msifile);
1006     /* just MsiOpenDatabase should not create a file */
1007     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
1008     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
1009
1010     /* create a table */
1011     r = try_query( hdb, 
1012         "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)");
1013     ok(r == ERROR_SUCCESS, "query failed\n");
1014
1015     /* try a insert a very long string */
1016     str = HeapAlloc(GetProcessHeap(), 0, STRING_LENGTH+sizeof insert_query);
1017     len = strchr(insert_query, 'Z') - insert_query;
1018     strcpy(str, insert_query);
1019     memset(str+len, 'Z', STRING_LENGTH);
1020     strcpy(str+len+STRING_LENGTH, insert_query+len+1);
1021     r = try_query( hdb, str );
1022     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1023
1024     HeapFree(GetProcessHeap(), 0, str);
1025
1026     MsiDatabaseCommit(hdb);
1027     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
1028     MsiCloseHandle(hdb);
1029
1030     r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb);
1031     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
1032
1033     r = MsiDatabaseOpenView(hdb, "select * from `strings` where `id` = 1", &hview);
1034     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1035
1036     r = MsiViewExecute(hview, 0);
1037     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
1038
1039     r = MsiViewFetch(hview, &hrec);
1040     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
1041
1042     MsiCloseHandle(hview);
1043
1044     r = MsiRecordGetString(hrec, 2, NULL, &len);
1045     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
1046     ok(len == STRING_LENGTH, "string length wrong\n");
1047
1048     MsiCloseHandle(hrec);
1049     MsiCloseHandle(hdb);
1050     DeleteFile(msifile);
1051 }
1052
1053 static void create_file(const CHAR *name)
1054 {
1055     HANDLE file;
1056     DWORD written;
1057
1058     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1059     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
1060     WriteFile(file, name, strlen(name), &written, NULL);
1061     WriteFile(file, "\n", strlen("\n"), &written, NULL);
1062     CloseHandle(file);
1063 }
1064  
1065 static void test_streamtable(void)
1066 {
1067     MSIHANDLE hdb = 0, rec, view;
1068     char file[MAX_PATH];
1069     char buf[MAX_PATH];
1070     DWORD size;
1071     UINT r;
1072
1073     hdb = create_db();
1074     ok( hdb, "failed to create db\n");
1075
1076     r = run_query( hdb, 0,
1077             "CREATE TABLE `Properties` "
1078             "( `Property` CHAR(255), `Value` CHAR(1)  PRIMARY KEY `Property`)" );
1079     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
1080
1081     /* check the column types */
1082     rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES );
1083     ok( rec, "failed to get column info record\n" );
1084
1085     todo_wine {
1086     ok( check_record( rec, 1, "s62"), "wrong record type\n");
1087     ok( check_record( rec, 2, "V0"), "wrong record type\n");
1088     }
1089
1090     MsiCloseHandle( rec );
1091
1092     /* now try the names */
1093     rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES );
1094     ok( rec, "failed to get column info record\n" );
1095
1096     todo_wine {
1097     ok( check_record( rec, 1, "Name"), "wrong record type\n");
1098     ok( check_record( rec, 2, "Data"), "wrong record type\n");
1099     }
1100
1101     MsiCloseHandle( rec );
1102
1103     /* insert a file into the _Streams table */
1104     create_file( "test.txt" );
1105
1106     rec = MsiCreateRecord( 2 );
1107     MsiRecordSetString( rec, 1, "data" );
1108
1109     r = MsiRecordSetStream( rec, 2, "test.txt" );
1110     ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r);
1111
1112     DeleteFile("test.txt");
1113
1114     r = MsiDatabaseOpenView( hdb,
1115             "INSERT INTO `_Streams` ( `Name`, `Data` ) VALUES ( ?, ? )", &view );
1116     todo_wine
1117     {
1118         ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r);
1119     }
1120
1121     r = MsiViewExecute( view, rec );
1122     todo_wine
1123     {
1124         ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r);
1125     }
1126
1127     MsiCloseHandle( rec );
1128     MsiCloseHandle( view );
1129
1130     r = MsiDatabaseOpenView( hdb,
1131             "SELECT `Name`, `Data` FROM `_Streams`", &view );
1132     todo_wine
1133     {
1134         ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r);
1135     }
1136
1137     r = MsiViewExecute( view, 0 );
1138     todo_wine
1139     {
1140         ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r);
1141     }
1142
1143     r = MsiViewFetch( view, &rec );
1144     todo_wine
1145     {
1146         ok( r == ERROR_SUCCESS, "Failed to fetch record: %d\n", r);
1147     }
1148
1149     size = MAX_PATH;
1150     r = MsiRecordGetString( rec, 1, file, &size );
1151     todo_wine
1152     {
1153         ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r);
1154         ok( !lstrcmp(file, "data"), "Expected 'data', got %s\n", file);
1155     }
1156
1157     size = MAX_PATH;
1158     memset(buf, 0, MAX_PATH);
1159     r = MsiRecordReadStream( rec, 2, buf, &size );
1160     todo_wine
1161     {
1162         ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r);
1163         ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf);
1164     }
1165
1166     MsiCloseHandle( rec );
1167
1168     r = MsiViewFetch( view, &rec );
1169     todo_wine
1170     {
1171         ok( r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
1172     }
1173
1174     MsiCloseHandle( view );
1175     MsiCloseHandle( hdb );
1176     DeleteFile(msifile);
1177 }
1178
1179 static void test_where(void)
1180 {
1181     MSIHANDLE hdb = 0, rec;
1182     LPCSTR query;
1183     UINT r;
1184
1185     hdb = create_db();
1186     ok( hdb, "failed to create db\n");
1187
1188     r = run_query( hdb, 0,
1189             "CREATE TABLE `Media` ("
1190             "`DiskId` SHORT NOT NULL, "
1191             "`LastSequence` LONG, "
1192             "`DiskPrompt` CHAR(64) LOCALIZABLE, "
1193             "`Cabinet` CHAR(255), "
1194             "`VolumeLabel` CHAR(32), "
1195             "`Source` CHAR(72) "
1196             "PRIMARY KEY `DiskId`)" );
1197     ok( r == S_OK, "cannot create Media table: %d\n", r );
1198
1199     r = run_query( hdb, 0, "INSERT INTO `Media` "
1200             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1201             "VALUES ( 1, 0, '', 'zero.cab', '', '' )" );
1202     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1203
1204     r = run_query( hdb, 0, "INSERT INTO `Media` "
1205             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1206             "VALUES ( 2, 1, '', 'one.cab', '', '' )" );
1207     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1208
1209     r = run_query( hdb, 0, "INSERT INTO `Media` "
1210             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1211             "VALUES ( 3, 2, '', 'two.cab', '', '' )" );
1212     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1213
1214     query = "SELECT * FROM `Media`";
1215     r = do_query(hdb, query, &rec);
1216     ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1217     ok( check_record( rec, 4, "zero.cab"), "wrong cabinet\n");
1218     MsiCloseHandle( rec );
1219
1220     query = "SELECT * FROM `Media` WHERE `LastSequence` >= 1";
1221     r = do_query(hdb, query, &rec);
1222     ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1223     ok( check_record( rec, 4, "one.cab"), "wrong cabinet\n");
1224
1225     r = MsiRecordGetInteger(rec, 1);
1226     ok( 2 == r, "field wrong\n");
1227     r = MsiRecordGetInteger(rec, 2);
1228     ok( 1 == r, "field wrong\n");
1229
1230     MsiCloseHandle( rec );
1231
1232     MsiCloseHandle( hdb );
1233     DeleteFile(msifile);
1234 }
1235
1236 static CHAR CURR_DIR[MAX_PATH];
1237
1238 static const CHAR test_data[] = "FirstPrimaryColumn\tSecondPrimaryColumn\tShortInt\tShortIntNullable\tLongInt\tLongIntNullable\tString\tLocalizableString\tLocalizableStringNullable\n"
1239                                 "s255\ti2\ti2\tI2\ti4\tI4\tS255\tS0\ts0\n"
1240                                 "TestTable\tFirstPrimaryColumn\n"
1241                                 "stringage\t5\t2\t\t2147483640\t-2147483640\tanother string\tlocalizable\tduh\n";
1242
1243 static void write_file(const CHAR *filename, const char *data, int data_size)
1244 {
1245     DWORD size;
1246
1247     HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
1248                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1249
1250     WriteFile(hf, data, data_size, &size, NULL);
1251     CloseHandle(hf);
1252 }
1253
1254 static UINT add_table_to_db(MSIHANDLE hdb, LPCSTR table_data)
1255 {
1256     UINT r;
1257
1258     write_file("temp_file", table_data, (lstrlen(table_data) - 1) * sizeof(char));
1259     r = MsiDatabaseImportA(hdb, CURR_DIR, "temp_file");
1260     DeleteFileA("temp_file");
1261
1262     return r;
1263 }
1264
1265 static void test_msiimport(void)
1266 {
1267     MSIHANDLE hdb, view, rec;
1268     LPCSTR query;
1269     UINT r, count;
1270     signed int i;
1271
1272     GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
1273
1274     r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb);
1275     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1276
1277     r = add_table_to_db(hdb, test_data);
1278     todo_wine
1279     {
1280         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1281     }
1282
1283     query = "SELECT * FROM `TestTable`";
1284     r = MsiDatabaseOpenView(hdb, query, &view);
1285     todo_wine
1286     {
1287         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1288     }
1289
1290     r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1291     count = MsiRecordGetFieldCount(rec);
1292     todo_wine
1293     {
1294         ok(count == 9, "Expected 9, got %d\n", count);
1295         ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n");
1296         ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n");
1297         ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n");
1298         ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n");
1299         ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n");
1300         ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n");
1301         ok(check_record(rec, 7, "String"), "Expected String\n");
1302         ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n");
1303         ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n");
1304     }
1305
1306     r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1307     count = MsiRecordGetFieldCount(rec);
1308     todo_wine
1309     {
1310         ok(count == 9, "Expected 9, got %d\n", count);
1311         ok(check_record(rec, 1, "s255"), "Expected s255\n");
1312         ok(check_record(rec, 2, "i2"), "Expected i2\n");
1313         ok(check_record(rec, 3, "i2"), "Expected i2\n");
1314         ok(check_record(rec, 4, "I2"), "Expected I2\n");
1315         ok(check_record(rec, 5, "i4"), "Expected i4\n");
1316         ok(check_record(rec, 6, "I4"), "Expected I4\n");
1317         ok(check_record(rec, 7, "S255"), "Expected S255\n");
1318         ok(check_record(rec, 8, "S0"), "Expected S0\n");
1319         ok(check_record(rec, 9, "s0"), "Expected s0\n");
1320     }
1321
1322     query = "SELECT * FROM `TestTable`";
1323     r = do_query(hdb, query, &rec);
1324     todo_wine
1325     {
1326         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1327     }
1328
1329     todo_wine
1330     {
1331         ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n");
1332         ok(check_record(rec, 7, "another string"), "Expected 'another string'\n");
1333         ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n");
1334         ok(check_record(rec, 9, "duh"), "Expected 'duh'\n");
1335     }
1336
1337     i = MsiRecordGetInteger(rec, 2);
1338     todo_wine
1339     {
1340         ok(i == 5, "Expected 5, got %d\n", i);
1341     }
1342
1343     i = MsiRecordGetInteger(rec, 3);
1344     todo_wine
1345     {
1346         ok(i == 2, "Expected 2, got %d\n", i);
1347     }
1348
1349     i = MsiRecordGetInteger(rec, 4);
1350     ok(i == 0x80000000, "Expected 0x80000000, got %d\n", i);
1351
1352     i = MsiRecordGetInteger(rec, 5);
1353     todo_wine
1354     {
1355         ok(i == 2147483640, "Expected 2147483640, got %d\n", i);
1356     }
1357
1358     i = MsiRecordGetInteger(rec, 6);
1359     todo_wine
1360     {
1361         ok(i == -2147483640, "Expected -2147483640, got %d\n", i);
1362     }
1363
1364     MsiCloseHandle(rec);
1365     MsiCloseHandle(view);
1366     MsiCloseHandle(hdb);
1367     DeleteFileA(msifile);
1368 }
1369
1370 static void test_markers(void)
1371 {
1372     MSIHANDLE hdb, rec;
1373     LPCSTR query;
1374     UINT r;
1375
1376     hdb = create_db();
1377     ok( hdb, "failed to create db\n");
1378
1379     rec = MsiCreateRecord(3);
1380     MsiRecordSetString(rec, 1, "Table");
1381     MsiRecordSetString(rec, 2, "Apples");
1382     MsiRecordSetString(rec, 3, "Oranges");
1383
1384     /* try a legit create */
1385     query = "CREATE TABLE `Table` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1386     r = run_query(hdb, 0, query);
1387     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1388     MsiCloseHandle(rec);
1389
1390     /* try table name as marker */
1391     rec = MsiCreateRecord(1);
1392     MsiRecordSetString(rec, 1, "Fable");
1393     query = "CREATE TABLE `?` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1394     r = run_query(hdb, rec, query);
1395     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1396
1397     /* verify that we just created a table called '?', not 'Fable' */
1398     r = try_query(hdb, "SELECT * from `Fable`");
1399     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1400
1401     r = try_query(hdb, "SELECT * from `?`");
1402     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1403
1404     /* try table name as marker without backticks */
1405     MsiRecordSetString(rec, 1, "Mable");
1406     query = "CREATE TABLE ? ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1407     r = run_query(hdb, rec, query);
1408     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1409
1410     /* try one column name as marker */
1411     MsiRecordSetString(rec, 1, "One");
1412     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1413     r = run_query(hdb, rec, query);
1414     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1415
1416     /* try column names as markers */
1417     MsiCloseHandle(rec);
1418     rec = MsiCreateRecord(2);
1419     MsiRecordSetString(rec, 1, "One");
1420     MsiRecordSetString(rec, 2, "Two");
1421     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `One`)";
1422     r = run_query(hdb, rec, query);
1423     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1424
1425     /* try names with backticks */
1426     MsiCloseHandle(rec);
1427     rec = MsiCreateRecord(3);
1428     MsiRecordSetString(rec, 1, "One");
1429     MsiRecordSetString(rec, 2, "Two");
1430     MsiRecordSetString(rec, 3, "One");
1431     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1432     r = run_query(hdb, rec, query);
1433     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1434
1435     /* try names with backticks, minus definitions */
1436     query = "CREATE TABLE `Mable` ( `?`, `?` PRIMARY KEY `?`)";
1437     r = run_query(hdb, rec, query);
1438     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1439
1440     /* try names without backticks */
1441     query = "CREATE TABLE `Mable` ( ? SHORT NOT NULL, ? CHAR(255) PRIMARY KEY ?)";
1442     r = run_query(hdb, rec, query);
1443     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1444
1445     /* try one long marker */
1446     MsiCloseHandle(rec);
1447     rec = MsiCreateRecord(1);
1448     MsiRecordSetString(rec, 1, "`One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`");
1449     query = "CREATE TABLE `Mable` ( ? )";
1450     r = run_query(hdb, rec, query);
1451     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1452
1453     /* try all names as markers */
1454     MsiCloseHandle(rec);
1455     rec = MsiCreateRecord(4);
1456     MsiRecordSetString(rec, 1, "Mable");
1457     MsiRecordSetString(rec, 2, "One");
1458     MsiRecordSetString(rec, 3, "Two");
1459     MsiRecordSetString(rec, 4, "One");
1460     query = "CREATE TABLE `?` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1461     r = run_query(hdb, rec, query);
1462     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1463
1464     /* try a legit insert */
1465     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( 5, 'hello' )";
1466     r = run_query(hdb, 0, query);
1467     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1468
1469     r = try_query(hdb, "SELECT * from `Table`");
1470     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1471
1472     /* try values as markers */
1473     MsiCloseHandle(rec);
1474     rec = MsiCreateRecord(2);
1475     MsiRecordSetInteger(rec, 1, 4);
1476     MsiRecordSetString(rec, 2, "hi");
1477     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1478     r = run_query(hdb, rec, query);
1479     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1480
1481     /* try column names and values as markers */
1482     MsiCloseHandle(rec);
1483     rec = MsiCreateRecord(4);
1484     MsiRecordSetString(rec, 1, "One");
1485     MsiRecordSetString(rec, 2, "Two");
1486     MsiRecordSetInteger(rec, 3, 5);
1487     MsiRecordSetString(rec, 4, "hi");
1488     query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( ?, '?' )";
1489     r = run_query(hdb, rec, query);
1490     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1491
1492     /* try column names as markers */
1493     MsiCloseHandle(rec);
1494     rec = MsiCreateRecord(2);
1495     MsiRecordSetString(rec, 1, "One");
1496     MsiRecordSetString(rec, 2, "Two");
1497     query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( 3, 'yellow' )";
1498     r = run_query(hdb, rec, query);
1499     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1500
1501     /* try table name as a marker */
1502     MsiCloseHandle(rec);
1503     rec = MsiCreateRecord(1);
1504     MsiRecordSetString(rec, 1, "Table");
1505     query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( 2, 'green' )";
1506     r = run_query(hdb, rec, query);
1507     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1508
1509     /* try table name and values as markers */
1510     MsiCloseHandle(rec);
1511     rec = MsiCreateRecord(3);
1512     MsiRecordSetString(rec, 1, "Table");
1513     MsiRecordSetInteger(rec, 2, 10);
1514     MsiRecordSetString(rec, 3, "haha");
1515     query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( ?, '?' )";
1516     r = run_query(hdb, rec, query);
1517     ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
1518
1519     /* try all markers */
1520     MsiCloseHandle(rec);
1521     rec = MsiCreateRecord(5);
1522     MsiRecordSetString(rec, 1, "Table");
1523     MsiRecordSetString(rec, 1, "One");
1524     MsiRecordSetString(rec, 1, "Two");
1525     MsiRecordSetInteger(rec, 2, 10);
1526     MsiRecordSetString(rec, 3, "haha");
1527     query = "INSERT INTO `?` ( `?`, `?` ) VALUES ( ?, '?' )";
1528     r = run_query(hdb, rec, query);
1529     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1530
1531     /* insert an integer as a string */
1532     MsiCloseHandle(rec);
1533     rec = MsiCreateRecord(2);
1534     MsiRecordSetString(rec, 1, "11");
1535     MsiRecordSetString(rec, 2, "hi");
1536     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1537     r = run_query(hdb, rec, query);
1538     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1539
1540     /* leave off the '' for the string */
1541     MsiCloseHandle(rec);
1542     rec = MsiCreateRecord(2);
1543     MsiRecordSetInteger(rec, 1, 12);
1544     MsiRecordSetString(rec, 2, "hi");
1545     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, ? )";
1546     r = run_query(hdb, rec, query);
1547     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1548     MsiCloseHandle(rec);
1549
1550     MsiCloseHandle(hdb);
1551     DeleteFileA(msifile);
1552 }
1553
1554 #define MY_NVIEWS 4000    /* Largest installer I've seen uses < 2k */
1555 static void test_handle_limit(void)
1556 {
1557     int i;
1558     MSIHANDLE hdb;
1559     MSIHANDLE hviews[MY_NVIEWS];
1560     UINT r;
1561
1562     /* create an empty db */
1563     hdb = create_db();
1564     ok( hdb, "failed to create db\n");
1565
1566     memset(hviews, 0, sizeof(hviews));
1567
1568     for (i=0; i<MY_NVIEWS; i++) {
1569         static char szQueryBuf[256] = "SELECT * from `_Tables`";
1570         hviews[i] = 0xdeadbeeb;
1571         r = MsiDatabaseOpenView(hdb, szQueryBuf, &hviews[i]);
1572         if( r != ERROR_SUCCESS || hviews[i] == 0xdeadbeeb || 
1573             hviews[i] == 0 || (i && (hviews[i] == hviews[i-1])))
1574             break;
1575     }
1576
1577     ok( i == MY_NVIEWS, "problem opening views\n");
1578
1579     for (i=0; i<MY_NVIEWS; i++) {
1580         if (hviews[i] != 0 && hviews[i] != 0xdeadbeeb) {
1581             r = MsiCloseHandle(hviews[i]);
1582             if (r != ERROR_SUCCESS)
1583                 break;
1584         }
1585     }
1586
1587     ok( i == MY_NVIEWS, "problem closing views\n");
1588
1589     r = MsiCloseHandle(hdb);
1590     ok( r == ERROR_SUCCESS, "failed to close database\n");
1591 }
1592
1593 static void test_generate_transform(void)
1594 {
1595     MSIHANDLE hdb1, hdb2;
1596     LPCSTR query;
1597     UINT r;
1598
1599     DeleteFile(msifile);
1600     DeleteFile(msifile2);
1601     DeleteFile(mstfile);
1602
1603     /* create an empty database */
1604     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb1 );
1605     ok( r == ERROR_SUCCESS , "Failed to create database\n" );
1606
1607     r = MsiDatabaseCommit( hdb1 );
1608     ok( r == ERROR_SUCCESS , "Failed to commit database\n" );
1609
1610     /* create another empty database */
1611     r = MsiOpenDatabase(msifile2, MSIDBOPEN_CREATE, &hdb2 );
1612     ok( r == ERROR_SUCCESS , "Failed to create database\n" );
1613
1614     r = MsiDatabaseCommit( hdb2 );
1615     ok( r == ERROR_SUCCESS , "Failed to commit database\n" );
1616
1617     /* the transform between two empty database should be empty */
1618     r = MsiDatabaseGenerateTransform(hdb1, hdb2, NULL, 0, 0);
1619     todo_wine {
1620     ok( r == ERROR_NO_DATA, "return code %d, should be ERROR_NO_DATA\n", r );
1621     }
1622
1623     query = "CREATE TABLE `AAR` ( `BAR` SHORT NOT NULL, `CAR` CHAR(255) PRIMARY KEY `CAR`)";
1624     r = run_query(hdb1, 0, query);
1625     ok(r == ERROR_SUCCESS, "failed to add table\n");
1626
1627     todo_wine {
1628     r = MsiDatabaseGenerateTransform(hdb1, hdb2, NULL, 0, 0);
1629     ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
1630
1631     r = MsiDatabaseGenerateTransform(hdb1, hdb2, mstfile, 0, 0);
1632     ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
1633
1634     MsiCloseHandle( hdb1 );
1635
1636     r = MsiDatabaseApplyTransform( hdb2, mstfile, 0 );
1637     ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
1638
1639     /* apply the same transform again? */
1640     r = MsiDatabaseApplyTransform( hdb2, mstfile, 0 );
1641     ok( r == ERROR_INSTALL_TRANSFORM_FAILURE,
1642        "return code %d, should be ERROR_INSTALL_TRANSFORM_FAILURE\n", r );
1643     }
1644
1645     MsiCloseHandle( hdb2 );
1646
1647     DeleteFile(msifile);
1648     DeleteFile(msifile2);
1649     DeleteFile(mstfile);
1650 }
1651
1652 struct join_res
1653 {
1654     const CHAR one[MAX_PATH];
1655     const CHAR two[MAX_PATH];
1656 };
1657
1658 static const struct join_res join_res_first[] =
1659 {
1660     { "alveolar", "procerus" },
1661     { "septum", "procerus" },
1662     { "septum", "nasalis" },
1663     { "ramus", "nasalis" },
1664     { "malar", "mentalis" },
1665 };
1666
1667 static const struct join_res join_res_second[] =
1668 {
1669     { "nasal", "septum" },
1670     { "mandible", "ramus" },
1671 };
1672
1673 static const struct join_res join_res_third[] =
1674 {
1675     { "msvcp.dll", "abcdefgh" },
1676     { "msvcr.dll", "ijklmnop" },
1677 };
1678
1679 static void test_join(void)
1680 {
1681     MSIHANDLE hdb, hview, hrec;
1682     LPCSTR query;
1683     CHAR buf[MAX_PATH];
1684     UINT r, count;
1685     DWORD size, i;
1686
1687     hdb = create_db();
1688     ok( hdb, "failed to create db\n");
1689
1690     r = create_component_table( hdb );
1691     ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r );
1692
1693     r = add_component_entry( hdb, "'zygomatic', 'malar', 'INSTALLDIR', 0, '', ''" );
1694     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1695
1696     r = add_component_entry( hdb, "'maxilla', 'alveolar', 'INSTALLDIR', 0, '', ''" );
1697     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1698
1699     r = add_component_entry( hdb, "'nasal', 'septum', 'INSTALLDIR', 0, '', ''" );
1700     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1701
1702     r = add_component_entry( hdb, "'mandible', 'ramus', 'INSTALLDIR', 0, '', ''" );
1703     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1704
1705     r = create_feature_components_table( hdb );
1706     ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r );
1707
1708     r = add_feature_components_entry( hdb, "'procerus', 'maxilla'" );
1709     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1710
1711     r = add_feature_components_entry( hdb, "'procerus', 'nasal'" );
1712     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1713
1714     r = add_feature_components_entry( hdb, "'nasalis', 'nasal'" );
1715     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1716
1717     r = add_feature_components_entry( hdb, "'nasalis', 'mandible'" );
1718     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1719
1720     r = add_feature_components_entry( hdb, "'nasalis', 'notacomponent'" );
1721     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1722
1723     r = add_feature_components_entry( hdb, "'mentalis', 'zygomatic'" );
1724     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1725
1726     r = create_std_dlls_table( hdb );
1727     ok( r == ERROR_SUCCESS, "cannot create StdDlls table: %d\n", r );
1728
1729     r = add_std_dlls_entry( hdb, "'msvcp.dll', 'msvcp.dll.01234'" );
1730     ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r );
1731
1732     r = add_std_dlls_entry( hdb, "'msvcr.dll', 'msvcr.dll.56789'" );
1733     ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r );
1734
1735     r = create_binary_table( hdb );
1736     ok( r == ERROR_SUCCESS, "cannot create Binary table: %d\n", r );
1737
1738     r = add_binary_entry( hdb, "'msvcp.dll.01234', 'abcdefgh'" );
1739     ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r );
1740
1741     r = add_binary_entry( hdb, "'msvcr.dll.56789', 'ijklmnop'" );
1742     ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r );
1743
1744     query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` "
1745             "FROM `Component`, `FeatureComponents` "
1746             "WHERE `Component`.`Component` = `FeatureComponents`.`Component_` "
1747             "ORDER BY `Feature_`";
1748     r = MsiDatabaseOpenView(hdb, query, &hview);
1749     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
1750
1751     r = MsiViewExecute(hview, 0);
1752     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
1753
1754     i = 0;
1755     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
1756     {
1757         count = MsiRecordGetFieldCount( hrec );
1758         ok( count == 2, "Expected 2 record fields, got %d\n", count );
1759
1760         size = MAX_PATH;
1761         r = MsiRecordGetString( hrec, 1, buf, &size );
1762         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1763         if (i == 2 || i == 3) todo_wine
1764         {
1765             ok( !lstrcmp( buf, join_res_first[i].one ),
1766                 "Expected '%s', got %s\n", join_res_first[i].one, buf );
1767         }
1768
1769         size = MAX_PATH;
1770         r = MsiRecordGetString( hrec, 2, buf, &size );
1771         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1772         if (i == 3) todo_wine
1773         {
1774             ok( !lstrcmp( buf, join_res_first[i].two ),
1775                 "Expected '%s', got %s\n", join_res_first[i].two, buf );
1776         }
1777
1778         i++;
1779         MsiCloseHandle(hrec);
1780     }
1781
1782     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
1783
1784     MsiViewClose(hview);
1785     MsiCloseHandle(hview);
1786
1787     query = "SELECT DISTINCT Component, ComponentId FROM FeatureComponents, Component "
1788             "WHERE FeatureComponents.Component_=Component.Component "
1789             "AND (Feature_='nasalis') ORDER BY Feature_";
1790     r = MsiDatabaseOpenView(hdb, query, &hview);
1791     todo_wine
1792     {
1793         ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
1794     }
1795
1796     r = MsiViewExecute(hview, 0);
1797     todo_wine
1798     {
1799         ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
1800     }
1801
1802     i = 0;
1803     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
1804     {
1805         count = MsiRecordGetFieldCount( hrec );
1806         ok( count == 2, "Expected 2 record fields, got %d\n", count );
1807
1808         size = MAX_PATH;
1809         r = MsiRecordGetString( hrec, 1, buf, &size );
1810         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1811         ok( !lstrcmp( buf, join_res_second[i].one ),
1812             "Expected '%s', got %s\n", join_res_second[i].one, buf );
1813
1814         size = MAX_PATH;
1815         r = MsiRecordGetString( hrec, 2, buf, &size );
1816         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1817         ok( !lstrcmp( buf, join_res_second[i].two ),
1818             "Expected '%s', got %s\n", join_res_second[i].two, buf );
1819
1820         i++;
1821         MsiCloseHandle(hrec);
1822     }
1823
1824     todo_wine
1825     {
1826         ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
1827     }
1828
1829     MsiViewClose(hview);
1830     MsiCloseHandle(hview);
1831
1832     query = "SELECT `StdDlls`.`File`, `Binary`.`Data` "
1833             "FROM `StdDlls`, `Binary` "
1834             "WHERE `StdDlls`.`Binary_` = `Binary`.`Name` "
1835             "ORDER BY `File`";
1836     r = MsiDatabaseOpenView(hdb, query, &hview);
1837     todo_wine
1838     {
1839         ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
1840     }
1841
1842     r = MsiViewExecute(hview, 0);
1843     todo_wine
1844     {
1845         ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
1846     }
1847
1848     i = 0;
1849     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
1850     {
1851         count = MsiRecordGetFieldCount( hrec );
1852         ok( count == 2, "Expected 2 record fields, got %d\n", count );
1853
1854         size = MAX_PATH;
1855         r = MsiRecordGetString( hrec, 1, buf, &size );
1856         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1857         ok( !lstrcmp( buf, join_res_third[i].one ),
1858             "Expected '%s', got %s\n", join_res_third[i].one, buf );
1859
1860         size = MAX_PATH;
1861         r = MsiRecordGetString( hrec, 2, buf, &size );
1862         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1863         ok( !lstrcmp( buf, join_res_third[i].two ),
1864             "Expected '%s', got %s\n", join_res_third[i].two, buf );
1865
1866         i++;
1867         MsiCloseHandle(hrec);
1868     }
1869
1870     todo_wine
1871     {
1872         ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
1873     }
1874
1875     MsiViewClose(hview);
1876     MsiCloseHandle(hview);
1877     MsiCloseHandle(hdb);
1878     DeleteFile(msifile);
1879 }
1880
1881 START_TEST(db)
1882 {
1883     test_msidatabase();
1884     test_msiinsert();
1885     test_msidecomposedesc();
1886     test_msibadqueries();
1887     test_viewmodify();
1888     test_viewgetcolumninfo();
1889     test_getcolinfo();
1890     test_msiexport();
1891     test_longstrings();
1892     test_streamtable();
1893     test_where();
1894     test_msiimport();
1895     test_markers();
1896     test_handle_limit();
1897     test_generate_transform();
1898     test_join();
1899 }