msi: Add tests for using markers in SELECT clauses.
[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
21 #define COBJMACROS
22
23 #include <stdio.h>
24
25 #include <windows.h>
26 #include <msi.h>
27 #include <msiquery.h>
28
29 #include <objidl.h>
30
31 #include "wine/test.h"
32
33 static const char *msifile = "winetest.msi";
34 static const char *msifile2 = "winetst2.msi";
35 static const char *mstfile = "winetst.mst";
36
37 static void test_msidatabase(void)
38 {
39     MSIHANDLE hdb = 0, hdb2 = 0;
40     UINT res;
41
42     DeleteFile(msifile);
43
44     res = MsiOpenDatabase( msifile, msifile2, &hdb );
45     ok( res == ERROR_OPEN_FAILED, "expected failure\n");
46
47     res = MsiOpenDatabase( msifile, (LPSTR) 0xff, &hdb );
48     ok( res == ERROR_INVALID_PARAMETER, "expected failure\n");
49
50     res = MsiCloseHandle( hdb );
51     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
52
53     /* create an empty database */
54     res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
55     ok( res == ERROR_SUCCESS , "Failed to create database\n" );
56
57     res = MsiDatabaseCommit( hdb );
58     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
59
60     ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n");
61
62     res = MsiCloseHandle( hdb );
63     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
64     res = MsiOpenDatabase( msifile, msifile2, &hdb2 );
65     ok( res == ERROR_SUCCESS , "Failed to open database\n" );
66
67     ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 ), "database should exist\n");
68
69     res = MsiDatabaseCommit( hdb2 );
70     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
71
72     res = MsiCloseHandle( hdb2 );
73     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
74
75     res = MsiOpenDatabase( msifile, msifile2, &hdb2 );
76     ok( res == ERROR_SUCCESS , "Failed to open database\n" );
77
78     res = MsiCloseHandle( hdb2 );
79     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
80
81     ok( INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile2 ), "uncommitted database should not exist\n");
82
83     res = MsiOpenDatabase( msifile, msifile2, &hdb2 );
84     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
85
86     res = MsiDatabaseCommit( hdb2 );
87     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
88
89     res = MsiCloseHandle( hdb2 );
90     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
91
92     ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 ), "committed database should exist\n");
93
94     res = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY, &hdb );
95     ok( res == ERROR_SUCCESS , "Failed to open database\n" );
96
97     res = MsiCloseHandle( hdb );
98     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
99
100     res = MsiOpenDatabase( msifile, MSIDBOPEN_DIRECT, &hdb );
101     ok( res == ERROR_SUCCESS , "Failed to open database\n" );
102
103     res = MsiCloseHandle( hdb );
104     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
105
106     res = MsiOpenDatabase( msifile, MSIDBOPEN_TRANSACT, &hdb );
107     ok( res == ERROR_SUCCESS , "Failed to open database\n" );
108
109     res = MsiCloseHandle( hdb );
110     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
111     ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n");
112
113     /* MSIDBOPEN_CREATE deletes the database if MsiCommitDatabase isn't called */
114     res = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb );
115     ok( res == ERROR_SUCCESS , "Failed to open database\n" );
116
117     ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n");
118
119     res = MsiCloseHandle( hdb );
120     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
121
122     ok( INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile ), "database should exist\n");
123
124     res = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb );
125     ok( res == ERROR_SUCCESS , "Failed to open database\n" );
126
127     res = MsiDatabaseCommit( hdb );
128     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
129
130     ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n");
131
132     res = MsiCloseHandle( hdb );
133     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
134
135     res = DeleteFile( msifile2 );
136     ok( res == TRUE, "Failed to delete database\n" );
137
138     res = DeleteFile( msifile );
139     ok( res == TRUE, "Failed to delete database\n" );
140 }
141
142 static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec)
143 {
144     MSIHANDLE hview = 0;
145     UINT r, ret;
146
147     /* open a select query */
148     r = MsiDatabaseOpenView(hdb, query, &hview);
149     if (r != ERROR_SUCCESS)
150         return r;
151     r = MsiViewExecute(hview, 0);
152     if (r != ERROR_SUCCESS)
153         return r;
154     ret = MsiViewFetch(hview, phrec);
155     r = MsiViewClose(hview);
156     if (r != ERROR_SUCCESS)
157         return r;
158     r = MsiCloseHandle(hview);
159     if (r != ERROR_SUCCESS)
160         return r;
161     return ret;
162 }
163
164 static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query )
165 {
166     MSIHANDLE hview = 0;
167     UINT r;
168
169     r = MsiDatabaseOpenView(hdb, query, &hview);
170     if( r != ERROR_SUCCESS )
171         return r;
172
173     r = MsiViewExecute(hview, hrec);
174     if( r == ERROR_SUCCESS )
175         r = MsiViewClose(hview);
176     MsiCloseHandle(hview);
177     return r;
178 }
179
180 static UINT create_component_table( MSIHANDLE hdb )
181 {
182     return run_query( hdb, 0,
183             "CREATE TABLE `Component` ( "
184             "`Component` CHAR(72) NOT NULL, "
185             "`ComponentId` CHAR(38), "
186             "`Directory_` CHAR(72) NOT NULL, "
187             "`Attributes` SHORT NOT NULL, "
188             "`Condition` CHAR(255), "
189             "`KeyPath` CHAR(72) "
190             "PRIMARY KEY `Component`)" );
191 }
192
193 static UINT create_feature_components_table( MSIHANDLE hdb )
194 {
195     return run_query( hdb, 0,
196             "CREATE TABLE `FeatureComponents` ( "
197             "`Feature_` CHAR(38) NOT NULL, "
198             "`Component_` CHAR(72) NOT NULL "
199             "PRIMARY KEY `Feature_`, `Component_` )" );
200 }
201
202 static UINT create_std_dlls_table( MSIHANDLE hdb )
203 {
204     return run_query( hdb, 0,
205             "CREATE TABLE `StdDlls` ( "
206             "`File` CHAR(255) NOT NULL, "
207             "`Binary_` CHAR(72) NOT NULL "
208             "PRIMARY KEY `File` )" );
209 }
210
211 static UINT create_binary_table( MSIHANDLE hdb )
212 {
213     return run_query( hdb, 0,
214             "CREATE TABLE `Binary` ( "
215             "`Name` CHAR(72) NOT NULL, "
216             "`Data` CHAR(72) NOT NULL "
217             "PRIMARY KEY `Name` )" );
218 }
219
220 static UINT add_component_entry( MSIHANDLE hdb, const char *values )
221 {
222     char insert[] = "INSERT INTO `Component`  "
223             "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
224             "VALUES( %s )";
225     char *query;
226     UINT sz, r;
227
228     sz = strlen(values) + sizeof insert;
229     query = HeapAlloc(GetProcessHeap(),0,sz);
230     sprintf(query,insert,values);
231     r = run_query( hdb, 0, query );
232     HeapFree(GetProcessHeap(), 0, query);
233     return r;
234 }
235
236 static UINT add_feature_components_entry( MSIHANDLE hdb, const char *values )
237 {
238     char insert[] = "INSERT INTO `FeatureComponents` "
239             "(`Feature_`, `Component_`) "
240             "VALUES( %s )";
241     char *query;
242     UINT sz, r;
243
244     sz = strlen(values) + sizeof insert;
245     query = HeapAlloc(GetProcessHeap(),0,sz);
246     sprintf(query,insert,values);
247     r = run_query( hdb, 0, query );
248     HeapFree(GetProcessHeap(), 0, query);
249     return r;
250 }
251
252 static UINT add_std_dlls_entry( MSIHANDLE hdb, const char *values )
253 {
254     char insert[] = "INSERT INTO `StdDlls` "
255             "(`File`, `Binary_`) "
256             "VALUES( %s )";
257     char *query;
258     UINT sz, r;
259
260     sz = strlen(values) + sizeof insert;
261     query = HeapAlloc(GetProcessHeap(),0,sz);
262     sprintf(query,insert,values);
263     r = run_query( hdb, 0, query );
264     HeapFree(GetProcessHeap(), 0, query);
265     return r;
266 }
267
268 static UINT add_binary_entry( MSIHANDLE hdb, const char *values )
269 {
270     char insert[] = "INSERT INTO `Binary` "
271             "(`Name`, `Data`) "
272             "VALUES( %s )";
273     char *query;
274     UINT sz, r;
275
276     sz = strlen(values) + sizeof insert;
277     query = HeapAlloc(GetProcessHeap(),0,sz);
278     sprintf(query,insert,values);
279     r = run_query( hdb, 0, query );
280     HeapFree(GetProcessHeap(), 0, query);
281     return r;
282 }
283
284 static void test_msiinsert(void)
285 {
286     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
287     UINT r;
288     const char *query;
289     char buf[80];
290     DWORD sz;
291
292     DeleteFile(msifile);
293
294     /* just MsiOpenDatabase should not create a file */
295     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
296     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
297
298     /* create a table */
299     query = "CREATE TABLE `phone` ( "
300             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
301             "PRIMARY KEY `id`)";
302     r = MsiDatabaseOpenView(hdb, query, &hview);
303     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
304     r = MsiViewExecute(hview, 0);
305     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
306     r = MsiViewClose(hview);
307     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
308     r = MsiCloseHandle(hview);
309     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
310
311     /* insert a value into it */
312     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
313         "VALUES('1', 'Abe', '8675309')";
314     r = MsiDatabaseOpenView(hdb, query, &hview);
315     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
316     r = MsiViewExecute(hview, 0);
317     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
318     r = MsiViewClose(hview);
319     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
320     r = MsiCloseHandle(hview);
321     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
322
323     query = "SELECT * FROM `phone` WHERE `id` = 1";
324     r = do_query(hdb, query, &hrec);
325     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
326
327     /* check the record contains what we put in it */
328     r = MsiRecordGetFieldCount(hrec);
329     ok(r == 3, "record count wrong\n");
330
331     todo_wine {
332     r = MsiRecordIsNull(hrec, 0);
333     ok(r == FALSE, "field 0 not null\n");
334     }
335
336     r = MsiRecordGetInteger(hrec, 1);
337     ok(r == 1, "field 1 contents wrong\n");
338     sz = sizeof buf;
339     r = MsiRecordGetString(hrec, 2, buf, &sz);
340     ok(r == ERROR_SUCCESS, "field 2 content fetch failed\n");
341     ok(!strcmp(buf,"Abe"), "field 2 content incorrect\n");
342     sz = sizeof buf;
343     r = MsiRecordGetString(hrec, 3, buf, &sz);
344     ok(r == ERROR_SUCCESS, "field 3 content fetch failed\n");
345     ok(!strcmp(buf,"8675309"), "field 3 content incorrect\n");
346
347     r = MsiCloseHandle(hrec);
348     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
349
350     /* open a select query */
351     hrec = 100;
352     query = "SELECT * FROM `phone` WHERE `id` >= 10";
353     r = do_query(hdb, query, &hrec);
354     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
355     ok(hrec == 0, "hrec should be null\n");
356
357     r = MsiCloseHandle(hrec);
358     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
359
360     query = "SELECT * FROM `phone` WHERE `id` < 0";
361     r = do_query(hdb, query, &hrec);
362     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
363
364     query = "SELECT * FROM `phone` WHERE `id` <= 0";
365     r = do_query(hdb, query, &hrec);
366     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
367
368     query = "SELECT * FROM `phone` WHERE `id` <> 1";
369     r = do_query(hdb, query, &hrec);
370     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
371
372     query = "SELECT * FROM `phone` WHERE `id` > 10";
373     r = do_query(hdb, query, &hrec);
374     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
375
376     /* now try a few bad INSERT xqueries */
377     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
378         "VALUES(?, ?)";
379     r = MsiDatabaseOpenView(hdb, query, &hview);
380     ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n");
381
382     /* construct a record to insert */
383     hrec = MsiCreateRecord(4);
384     r = MsiRecordSetInteger(hrec, 1, 2);
385     ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n");
386     r = MsiRecordSetString(hrec, 2, "Adam");
387     ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
388     r = MsiRecordSetString(hrec, 3, "96905305");
389     ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
390
391     /* insert another value, using a record and wildcards */
392     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
393         "VALUES(?, ?, ?)";
394     r = MsiDatabaseOpenView(hdb, query, &hview);
395     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
396
397     if (r == ERROR_SUCCESS)
398     {
399         r = MsiViewExecute(hview, hrec);
400         ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
401         r = MsiViewClose(hview);
402         ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
403         r = MsiCloseHandle(hview);
404         ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
405     }
406     r = MsiCloseHandle(hrec);
407     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
408
409     r = MsiViewFetch(0, NULL);
410     ok(r == ERROR_INVALID_PARAMETER, "MsiViewFetch failed\n");
411
412     r = MsiDatabaseCommit(hdb);
413     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
414
415     r = MsiCloseHandle(hdb);
416     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
417
418     r = DeleteFile(msifile);
419     ok(r == TRUE, "file didn't exist after commit\n");
420 }
421
422 typedef UINT (WINAPI *fnMsiDecomposeDescriptorA)(LPCSTR, LPCSTR, LPSTR, LPSTR, DWORD *);
423 static fnMsiDecomposeDescriptorA pMsiDecomposeDescriptorA;
424
425 static void test_msidecomposedesc(void)
426 {
427     char prod[MAX_FEATURE_CHARS+1], comp[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1];
428     const char *desc;
429     UINT r;
430     DWORD len;
431     HMODULE hmod;
432
433     hmod = GetModuleHandle("msi.dll");
434     if (!hmod)
435         return;
436     pMsiDecomposeDescriptorA = (fnMsiDecomposeDescriptorA)
437         GetProcAddress(hmod, "MsiDecomposeDescriptorA");
438     if (!pMsiDecomposeDescriptorA)
439         return;
440
441     /* test a valid feature descriptor */
442     desc = "']gAVn-}f(ZXfeAR6.jiFollowTheWhiteRabbit>3w2x^IGfe?CxI5heAvk.";
443     len = 0;
444     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
445     ok(r == ERROR_SUCCESS, "returned an error\n");
446     ok(len == strlen(desc), "length was wrong\n");
447     ok(strcmp(prod,"{90110409-6000-11D3-8CFE-0150048383C9}")==0, "product wrong\n");
448     ok(strcmp(feature,"FollowTheWhiteRabbit")==0, "feature wrong\n");
449     ok(strcmp(comp,"{A7CD68DB-EF74-49C8-FBB2-A7C463B2AC24}")==0,"component wrong\n");
450
451     /* test an invalid feature descriptor with too many characters */
452     desc = "']gAVn-}f(ZXfeAR6.ji"
453            "ThisWillFailIfTheresMoreThanAGuidsChars>"
454            "3w2x^IGfe?CxI5heAvk.";
455     len = 0;
456     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
457     ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
458
459     /*
460      * Test a valid feature descriptor with the
461      * maximum number of characters and some trailing characters.
462      */
463     desc = "']gAVn-}f(ZXfeAR6.ji"
464            "ThisWillWorkIfTheresLTEThanAGuidsChars>"
465            "3w2x^IGfe?CxI5heAvk."
466            "extra";
467     len = 0;
468     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
469     ok(r == ERROR_SUCCESS, "returned wrong error\n");
470     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
471
472     len = 0;
473     r = pMsiDecomposeDescriptorA(desc, prod, feature, NULL, &len);
474     ok(r == ERROR_SUCCESS, "returned wrong error\n");
475     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
476
477     len = 0;
478     r = pMsiDecomposeDescriptorA(desc, prod, NULL, NULL, &len);
479     ok(r == ERROR_SUCCESS, "returned wrong error\n");
480     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
481
482     len = 0;
483     r = pMsiDecomposeDescriptorA(desc, NULL, NULL, NULL, &len);
484     ok(r == ERROR_SUCCESS, "returned wrong error\n");
485     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
486
487     len = 0;
488     r = pMsiDecomposeDescriptorA(NULL, NULL, NULL, NULL, &len);
489     ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
490     ok(len == 0, "length wrong\n");
491 }
492
493 static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
494 {
495     MSIHANDLE htab = 0;
496     UINT res;
497
498     res = MsiDatabaseOpenView( hdb, szQuery, &htab );
499     if(res == ERROR_SUCCESS )
500     {
501         UINT r;
502
503         r = MsiViewExecute( htab, hrec );
504         if(r != ERROR_SUCCESS )
505             res = r;
506
507         r = MsiViewClose( htab );
508         if(r != ERROR_SUCCESS )
509             res = r;
510
511         r = MsiCloseHandle( htab );
512         if(r != ERROR_SUCCESS )
513             res = r;
514     }
515     return res;
516 }
517
518 static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
519 {
520     return try_query_param( hdb, szQuery, 0 );
521 }
522
523 static UINT try_insert_query( MSIHANDLE hdb, LPCSTR szQuery )
524 {
525     MSIHANDLE hrec = 0;
526     UINT r;
527
528     hrec = MsiCreateRecord( 1 );
529     MsiRecordSetString( hrec, 1, "Hello");
530
531     r = try_query_param( hdb, szQuery, hrec );
532
533     MsiCloseHandle( hrec );
534     return r;
535 }
536
537 static void test_msibadqueries(void)
538 {
539     MSIHANDLE hdb = 0;
540     UINT r;
541
542     DeleteFile(msifile);
543
544     /* just MsiOpenDatabase should not create a file */
545     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
546     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
547
548     r = MsiDatabaseCommit( hdb );
549     ok(r == ERROR_SUCCESS , "Failed to commit database\n");
550
551     r = MsiCloseHandle( hdb );
552     ok(r == ERROR_SUCCESS , "Failed to close database\n");
553
554     /* open it readonly */
555     r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb );
556     ok(r == ERROR_SUCCESS , "Failed to open database r/o\n");
557
558     /* add a table to it */
559     r = try_query( hdb, "select * from _Tables");
560     ok(r == ERROR_SUCCESS , "query 1 failed\n");
561
562     r = MsiCloseHandle( hdb );
563     ok(r == ERROR_SUCCESS , "Failed to close database r/o\n");
564
565     /* open it read/write */
566     r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb );
567     ok(r == ERROR_SUCCESS , "Failed to open database r/w\n");
568
569     /* a bunch of test queries that fail with the native MSI */
570
571     r = try_query( hdb, "CREATE TABLE");
572     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2a return code\n");
573
574     r = try_query( hdb, "CREATE TABLE `a`");
575     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2b return code\n");
576
577     r = try_query( hdb, "CREATE TABLE `a` ()");
578     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2c return code\n");
579
580     r = try_query( hdb, "CREATE TABLE `a` (`b`)");
581     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2d return code\n");
582
583     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) )");
584     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2e return code\n");
585
586     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL)");
587     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2f return code\n");
588
589     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY)");
590     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2g return code\n");
591
592     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
593     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2h return code\n");
594
595     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
596     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2i return code\n");
597
598     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY 'b')");
599     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2j return code\n");
600
601     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
602     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2k return code\n");
603
604     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
605     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2l return code\n");
606
607     r = try_query( hdb, "CREATE TABLE `a` (`b` CHA(72) NOT NULL PRIMARY KEY `b`)");
608     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2m return code\n");
609
610     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(-1) NOT NULL PRIMARY KEY `b`)");
611     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2n return code\n");
612
613     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(720) NOT NULL PRIMARY KEY `b`)");
614     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2o return code\n");
615
616     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL KEY `b`)");
617     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
618
619     r = try_query( hdb, "CREATE TABLE `a` (`` CHAR(72) NOT NULL PRIMARY KEY `b`)");
620     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
621
622     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
623     ok(r == ERROR_SUCCESS , "valid query 2z failed\n");
624
625     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
626     ok(r == ERROR_BAD_QUERY_SYNTAX , "created same table again\n");
627
628     r = try_query( hdb, "CREATE TABLE `aa` (`b` CHAR(72) NOT NULL, `c` "
629           "CHAR(72), `d` CHAR(255) NOT NULL LOCALIZABLE PRIMARY KEY `b`)");
630     ok(r == ERROR_SUCCESS , "query 4 failed\n");
631
632     r = MsiDatabaseCommit( hdb );
633     ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
634
635     r = try_query( hdb, "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL "
636                           "PRIMARY KEY `foo`)");
637     ok(r == ERROR_SUCCESS , "query 4 failed\n");
638
639     r = try_insert_query( hdb, "insert into a  ( `b` ) VALUES ( ? )");
640     ok(r == ERROR_SUCCESS , "failed to insert record in db\n");
641
642     r = MsiDatabaseCommit( hdb );
643     ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
644
645     r = try_query( hdb, "CREATE TABLE `boo` (`foo` CHAR(72) NOT NULL "
646                           "PRIMARY KEY `ba`)");
647     ok(r != ERROR_SUCCESS , "query 5 succeeded\n");
648
649     r = try_query( hdb,"CREATE TABLE `bee` (`foo` CHAR(72) NOT NULL )");
650     ok(r != ERROR_SUCCESS , "query 6 succeeded\n");
651
652     r = try_query( hdb, "CREATE TABLE `temp` (`t` CHAR(72) NOT NULL "
653                           "PRIMARY KEY `t`)");
654     ok(r == ERROR_SUCCESS , "query 7 failed\n");
655
656     r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)");
657     ok(r == ERROR_SUCCESS , "query 8 failed\n");
658
659     r = try_query( hdb, "select * from c");
660     ok(r == ERROR_SUCCESS , "query failed\n");
661
662     r = try_query( hdb, "select * from c where b = 'x");
663     todo_wine ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
664
665     r = try_query( hdb, "select * from 'c'");
666     ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
667
668     r = try_query( hdb, "select * from ''");
669     ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
670
671     r = try_query( hdb, "select * from c where b = x");
672     ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
673
674     r = try_query( hdb, "select * from c where b = \"x\"");
675     ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
676
677     r = try_query( hdb, "select * from c where b = 'x'");
678     ok(r == ERROR_SUCCESS, "query failed\n");
679
680     r = try_query( hdb, "select * from c where b = '\"x'");
681     ok(r == ERROR_SUCCESS, "query failed\n");
682
683     if (0)  /* FIXME: this query causes trouble with other tests */
684     {
685         r = try_query( hdb, "select * from c where b = '\\\'x'");
686         ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
687     }
688
689     r = try_query( hdb, "select * from 'c'");
690     ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
691
692     r = MsiCloseHandle( hdb );
693     ok(r == ERROR_SUCCESS , "Failed to close database transact\n");
694
695     r = DeleteFile( msifile );
696     ok(r == TRUE, "file didn't exist after commit\n");
697 }
698
699 static void test_viewmodify(void)
700 {
701     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
702     UINT r;
703     const char *query;
704     char buffer[0x100];
705     DWORD sz;
706
707     DeleteFile(msifile);
708
709     /* just MsiOpenDatabase should not create a file */
710     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
711     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
712
713     query = "CREATE TABLE `phone` ( "
714             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
715             "PRIMARY KEY `id`)";
716     r = run_query( hdb, 0, query );
717     ok(r == ERROR_SUCCESS, "query failed\n");
718
719     /* check what the error function reports without doing anything */
720     sz = 0;
721     /* passing NULL as the 3rd param make function to crash on older platforms */
722     r = MsiViewGetError( 0, NULL, &sz );
723     ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
724
725     /* open a view */
726     query = "SELECT * FROM `phone`";
727     r = MsiDatabaseOpenView(hdb, query, &hview);
728     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
729
730     /* see what happens with a good hview and bad args */
731     r = MsiViewGetError( hview, NULL, NULL );
732     ok(r == MSIDBERROR_INVALIDARG || r == MSIDBERROR_NOERROR,
733        "MsiViewGetError returns %u (expected -3)\n", r);
734     r = MsiViewGetError( hview, buffer, NULL );
735     ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
736
737     /* see what happens with a zero length buffer */
738     sz = 0;
739     buffer[0] = 'x';
740     r = MsiViewGetError( hview, buffer, &sz );
741     ok(r == MSIDBERROR_MOREDATA, "MsiViewGetError return\n");
742     ok(buffer[0] == 'x', "buffer cleared\n");
743     ok(sz == 0, "size not zero\n");
744
745     /* ok this one is strange */
746     sz = 0;
747     r = MsiViewGetError( hview, NULL, &sz );
748     ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
749     ok(sz == 0, "size not zero\n");
750
751     /* see if it really has an error */
752     sz = sizeof buffer;
753     buffer[0] = 'x';
754     r = MsiViewGetError( hview, buffer, &sz );
755     ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
756     ok(buffer[0] == 0, "buffer not cleared\n");
757     ok(sz == 0, "size not zero\n");
758
759     r = MsiViewExecute(hview, 0);
760     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
761
762     /* try some invalid records */
763     r = MsiViewModify(hview, MSIMODIFY_INSERT, 0 );
764     ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
765     r = MsiViewModify(hview, -1, 0 );
766     ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
767
768     /* try an small record */
769     hrec = MsiCreateRecord(1);
770     r = MsiViewModify(hview, -1, hrec );
771     ok(r == ERROR_INVALID_DATA, "MsiViewModify failed\n");
772
773     r = MsiCloseHandle(hrec);
774     ok(r == ERROR_SUCCESS, "failed to close record\n");
775
776     /* insert a valid record */
777     hrec = MsiCreateRecord(3);
778
779     r = MsiRecordSetInteger(hrec, 2, 1);
780     ok(r == ERROR_SUCCESS, "failed to set integer\n");
781     r = MsiRecordSetString(hrec, 2, "bob");
782     ok(r == ERROR_SUCCESS, "failed to set integer\n");
783     r = MsiRecordSetString(hrec, 3, "7654321");
784     ok(r == ERROR_SUCCESS, "failed to set integer\n");
785
786     r = MsiViewExecute(hview, 0);
787     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
788     r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
789     ok(r == ERROR_SUCCESS, "MsiViewModify failed\n");
790
791     /* insert the same thing again */
792     r = MsiViewExecute(hview, 0);
793     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
794
795     /* should fail ... */
796     todo_wine {
797     r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
798     ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n");
799     }
800
801     r = MsiCloseHandle(hrec);
802     ok(r == ERROR_SUCCESS, "failed to close record\n");
803
804     r = MsiViewClose(hview);
805     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
806     r = MsiCloseHandle(hview);
807     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
808
809     r = MsiCloseHandle( hdb );
810     ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
811 }
812
813 static MSIHANDLE create_db(void)
814 {
815     MSIHANDLE hdb = 0;
816     UINT res;
817
818     DeleteFile(msifile);
819
820     /* create an empty database */
821     res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
822     ok( res == ERROR_SUCCESS , "Failed to create database\n" );
823     if( res != ERROR_SUCCESS )
824         return hdb;
825
826     res = MsiDatabaseCommit( hdb );
827     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
828
829     return hdb;
830 }
831
832 static void test_getcolinfo(void)
833 {
834     MSIHANDLE hdb, hview = 0, rec = 0;
835     UINT r;
836     DWORD sz;
837     char buffer[0x20];
838
839     /* create an empty db */
840     hdb = create_db();
841     ok( hdb, "failed to create db\n");
842
843     /* tables should be present */
844     r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview);
845     ok( r == ERROR_SUCCESS, "failed to open query\n");
846
847     r = MsiViewExecute(hview, 0);
848     ok( r == ERROR_SUCCESS, "failed to execute query\n");
849
850     /* check that NAMES works */
851     rec = 0;
852     r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec );
853     ok( r == ERROR_SUCCESS, "failed to get names\n");
854     sz = sizeof buffer;
855     r = MsiRecordGetString(rec, 1, buffer, &sz );
856     ok( r == ERROR_SUCCESS, "failed to get string\n");
857     ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n");
858     r = MsiCloseHandle( rec );
859     ok( r == ERROR_SUCCESS, "failed to close record handle\n");
860
861     /* check that TYPES works */
862     rec = 0;
863     r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec );
864     ok( r == ERROR_SUCCESS, "failed to get names\n");
865     sz = sizeof buffer;
866     r = MsiRecordGetString(rec, 1, buffer, &sz );
867     ok( r == ERROR_SUCCESS, "failed to get string\n");
868     ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n");
869     r = MsiCloseHandle( rec );
870     ok( r == ERROR_SUCCESS, "failed to close record handle\n");
871
872     /* check that invalid values fail */
873     rec = 0;
874     r = MsiViewGetColumnInfo( hview, 100, &rec );
875     ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
876     ok( rec == 0, "returned a record\n");
877
878     r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL );
879     ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
880
881     r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec );
882     ok( r == ERROR_INVALID_HANDLE, "wrong error code\n");
883
884     r = MsiViewClose(hview);
885     ok( r == ERROR_SUCCESS, "failed to close view\n");
886     r = MsiCloseHandle(hview);
887     ok( r == ERROR_SUCCESS, "failed to close view handle\n");
888     r = MsiCloseHandle(hdb);
889     ok( r == ERROR_SUCCESS, "failed to close database\n");
890 }
891
892 static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type)
893 {
894     MSIHANDLE hview = 0, rec = 0;
895     UINT r;
896
897     r = MsiDatabaseOpenView(hdb, query, &hview);
898     if( r != ERROR_SUCCESS )
899         return r;
900
901     r = MsiViewExecute(hview, 0);
902     if( r == ERROR_SUCCESS )
903     {
904         MsiViewGetColumnInfo( hview, type, &rec );
905         MsiViewClose(hview);
906     }
907     MsiCloseHandle(hview);
908     return rec;
909 }
910
911 static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field)
912 {
913     MSIHANDLE hview = 0, rec = 0;
914     UINT r, type = 0;
915     char query[0x100];
916
917     sprintf(query, "select * from `_Columns` where  `Table` = '%s'", table );
918
919     r = MsiDatabaseOpenView(hdb, query, &hview);
920     if( r != ERROR_SUCCESS )
921         return r;
922
923     r = MsiViewExecute(hview, 0);
924     if( r == ERROR_SUCCESS )
925     {
926         while (1)
927         {
928             r = MsiViewFetch( hview, &rec );
929             if( r != ERROR_SUCCESS)
930                 break;
931             r = MsiRecordGetInteger( rec, 2 );
932             if (r == field)
933                 type = MsiRecordGetInteger( rec, 4 );
934             MsiCloseHandle( rec );
935         }
936
937         MsiViewClose(hview);
938     }
939     MsiCloseHandle(hview);
940     return type;
941 }
942
943 static BOOL check_record( MSIHANDLE rec, UINT field, LPCSTR val )
944 {
945     CHAR buffer[0x20];
946     UINT r;
947     DWORD sz;
948
949     sz = sizeof buffer;
950     r = MsiRecordGetString( rec, field, buffer, &sz );
951     return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
952 }
953
954 static void test_viewgetcolumninfo(void)
955 {
956     MSIHANDLE hdb = 0, rec;
957     UINT r;
958
959     hdb = create_db();
960     ok( hdb, "failed to create db\n");
961
962     r = run_query( hdb, 0,
963             "CREATE TABLE `Properties` "
964             "( `Property` CHAR(255), `Value` CHAR(1)  PRIMARY KEY `Property`)" );
965     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
966
967     /* check the column types */
968     rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES );
969     ok( rec, "failed to get column info record\n" );
970
971     ok( check_record( rec, 1, "S255"), "wrong record type\n");
972     ok( check_record( rec, 2, "S1"), "wrong record type\n");
973
974     MsiCloseHandle( rec );
975
976     /* check the type in _Columns */
977     ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n");
978     ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n");
979
980     /* now try the names */
981     rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES );
982     ok( rec, "failed to get column info record\n" );
983
984     ok( check_record( rec, 1, "Property"), "wrong record type\n");
985     ok( check_record( rec, 2, "Value"), "wrong record type\n");
986
987     MsiCloseHandle( rec );
988
989     r = run_query( hdb, 0,
990             "CREATE TABLE `Binary` "
991             "( `Name` CHAR(255), `Data` OBJECT  PRIMARY KEY `Name`)" );
992     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
993
994     /* check the column types */
995     rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES );
996     ok( rec, "failed to get column info record\n" );
997
998     ok( check_record( rec, 1, "S255"), "wrong record type\n");
999     ok( check_record( rec, 2, "V0"), "wrong record type\n");
1000
1001     MsiCloseHandle( rec );
1002
1003     /* check the type in _Columns */
1004     ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n");
1005     ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n");
1006
1007     /* now try the names */
1008     rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES );
1009     ok( rec, "failed to get column info record\n" );
1010
1011     ok( check_record( rec, 1, "Name"), "wrong record type\n");
1012     ok( check_record( rec, 2, "Data"), "wrong record type\n");
1013     MsiCloseHandle( rec );
1014
1015     r = run_query( hdb, 0,
1016             "CREATE TABLE `UIText` "
1017             "( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" );
1018     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
1019
1020     ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n");
1021     ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n");
1022
1023     rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES );
1024     ok( rec, "failed to get column info record\n" );
1025     ok( check_record( rec, 1, "Key"), "wrong record type\n");
1026     ok( check_record( rec, 2, "Text"), "wrong record type\n");
1027     MsiCloseHandle( rec );
1028
1029     rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES );
1030     ok( rec, "failed to get column info record\n" );
1031     ok( check_record( rec, 1, "s72"), "wrong record type\n");
1032     ok( check_record( rec, 2, "L255"), "wrong record type\n");
1033     MsiCloseHandle( rec );
1034
1035     MsiCloseHandle( hdb );
1036 }
1037
1038 static void test_msiexport(void)
1039 {
1040     MSIHANDLE hdb = 0, hview = 0;
1041     UINT r;
1042     const char *query;
1043     char path[MAX_PATH];
1044     const char file[] = "phone.txt";
1045     HANDLE handle;
1046     char buffer[0x100];
1047     DWORD length;
1048     const char expected[] =
1049         "id\tname\tnumber\r\n"
1050         "I2\tS32\tS32\r\n"
1051         "phone\tid\r\n"
1052         "1\tAbe\t8675309\r\n";
1053
1054     DeleteFile(msifile);
1055
1056     /* just MsiOpenDatabase should not create a file */
1057     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
1058     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
1059
1060     /* create a table */
1061     query = "CREATE TABLE `phone` ( "
1062             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
1063             "PRIMARY KEY `id`)";
1064     r = MsiDatabaseOpenView(hdb, query, &hview);
1065     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1066     r = MsiViewExecute(hview, 0);
1067     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
1068     r = MsiViewClose(hview);
1069     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
1070     r = MsiCloseHandle(hview);
1071     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
1072
1073     /* insert a value into it */
1074     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
1075         "VALUES('1', 'Abe', '8675309')";
1076     r = MsiDatabaseOpenView(hdb, query, &hview);
1077     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1078     r = MsiViewExecute(hview, 0);
1079     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
1080     r = MsiViewClose(hview);
1081     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
1082     r = MsiCloseHandle(hview);
1083     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
1084
1085     GetCurrentDirectory(MAX_PATH, path);
1086
1087     r = MsiDatabaseExport(hdb, "phone", path, file);
1088     ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n");
1089
1090     MsiCloseHandle(hdb);
1091
1092     lstrcat(path, "\\");
1093     lstrcat(path, file);
1094
1095     /* check the data that was written */
1096     length = 0;
1097     memset(buffer, 0, sizeof buffer);
1098     handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
1099     if (handle != INVALID_HANDLE_VALUE)
1100     {
1101         ReadFile(handle, buffer, sizeof buffer, &length, NULL);
1102         CloseHandle(handle);
1103         DeleteFile(path);
1104     }
1105     else
1106         ok(0, "failed to open file %s\n", path);
1107
1108     ok( length == strlen(expected), "length of data wrong\n");
1109     ok( !lstrcmp(buffer, expected), "data doesn't match\n");
1110     DeleteFile(msifile);
1111 }
1112
1113 static void test_longstrings(void)
1114 {
1115     const char insert_query[] = 
1116         "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')";
1117     char *str;
1118     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
1119     DWORD len;
1120     UINT r;
1121     const DWORD STRING_LENGTH = 0x10005;
1122
1123     DeleteFile(msifile);
1124     /* just MsiOpenDatabase should not create a file */
1125     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
1126     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
1127
1128     /* create a table */
1129     r = try_query( hdb, 
1130         "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)");
1131     ok(r == ERROR_SUCCESS, "query failed\n");
1132
1133     /* try a insert a very long string */
1134     str = HeapAlloc(GetProcessHeap(), 0, STRING_LENGTH+sizeof insert_query);
1135     len = strchr(insert_query, 'Z') - insert_query;
1136     strcpy(str, insert_query);
1137     memset(str+len, 'Z', STRING_LENGTH);
1138     strcpy(str+len+STRING_LENGTH, insert_query+len+1);
1139     r = try_query( hdb, str );
1140     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1141
1142     HeapFree(GetProcessHeap(), 0, str);
1143
1144     MsiDatabaseCommit(hdb);
1145     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
1146     MsiCloseHandle(hdb);
1147
1148     r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb);
1149     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
1150
1151     r = MsiDatabaseOpenView(hdb, "select * from `strings` where `id` = 1", &hview);
1152     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1153
1154     r = MsiViewExecute(hview, 0);
1155     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
1156
1157     r = MsiViewFetch(hview, &hrec);
1158     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
1159
1160     MsiCloseHandle(hview);
1161
1162     r = MsiRecordGetString(hrec, 2, NULL, &len);
1163     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
1164     ok(len == STRING_LENGTH, "string length wrong\n");
1165
1166     MsiCloseHandle(hrec);
1167     MsiCloseHandle(hdb);
1168     DeleteFile(msifile);
1169 }
1170
1171 static void create_file(const CHAR *name)
1172 {
1173     HANDLE file;
1174     DWORD written;
1175
1176     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1177     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
1178     WriteFile(file, name, strlen(name), &written, NULL);
1179     WriteFile(file, "\n", strlen("\n"), &written, NULL);
1180     CloseHandle(file);
1181 }
1182  
1183 static void test_streamtable(void)
1184 {
1185     MSIHANDLE hdb = 0, rec, view;
1186     char file[MAX_PATH];
1187     char buf[MAX_PATH];
1188     DWORD size;
1189     UINT r;
1190
1191     hdb = create_db();
1192     ok( hdb, "failed to create db\n");
1193
1194     r = run_query( hdb, 0,
1195             "CREATE TABLE `Properties` "
1196             "( `Property` CHAR(255), `Value` CHAR(1)  PRIMARY KEY `Property`)" );
1197     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
1198
1199     /* check the column types */
1200     rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES );
1201     ok( rec, "failed to get column info record\n" );
1202
1203     todo_wine {
1204     ok( check_record( rec, 1, "s62"), "wrong record type\n");
1205     ok( check_record( rec, 2, "V0"), "wrong record type\n");
1206     }
1207
1208     MsiCloseHandle( rec );
1209
1210     /* now try the names */
1211     rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES );
1212     ok( rec, "failed to get column info record\n" );
1213
1214     todo_wine {
1215     ok( check_record( rec, 1, "Name"), "wrong record type\n");
1216     ok( check_record( rec, 2, "Data"), "wrong record type\n");
1217     }
1218
1219     MsiCloseHandle( rec );
1220
1221     /* insert a file into the _Streams table */
1222     create_file( "test.txt" );
1223
1224     rec = MsiCreateRecord( 2 );
1225     MsiRecordSetString( rec, 1, "data" );
1226
1227     r = MsiRecordSetStream( rec, 2, "test.txt" );
1228     ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r);
1229
1230     DeleteFile("test.txt");
1231
1232     r = MsiDatabaseOpenView( hdb,
1233             "INSERT INTO `_Streams` ( `Name`, `Data` ) VALUES ( ?, ? )", &view );
1234     todo_wine
1235     {
1236         ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r);
1237     }
1238
1239     r = MsiViewExecute( view, rec );
1240     todo_wine
1241     {
1242         ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r);
1243     }
1244
1245     MsiCloseHandle( rec );
1246     MsiCloseHandle( view );
1247
1248     r = MsiDatabaseOpenView( hdb,
1249             "SELECT `Name`, `Data` FROM `_Streams`", &view );
1250     todo_wine
1251     {
1252         ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r);
1253     }
1254
1255     r = MsiViewExecute( view, 0 );
1256     todo_wine
1257     {
1258         ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r);
1259     }
1260
1261     r = MsiViewFetch( view, &rec );
1262     todo_wine
1263     {
1264         ok( r == ERROR_SUCCESS, "Failed to fetch record: %d\n", r);
1265     }
1266
1267     size = MAX_PATH;
1268     r = MsiRecordGetString( rec, 1, file, &size );
1269     todo_wine
1270     {
1271         ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r);
1272         ok( !lstrcmp(file, "data"), "Expected 'data', got %s\n", file);
1273     }
1274
1275     size = MAX_PATH;
1276     memset(buf, 0, MAX_PATH);
1277     r = MsiRecordReadStream( rec, 2, buf, &size );
1278     todo_wine
1279     {
1280         ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r);
1281         ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf);
1282     }
1283
1284     MsiCloseHandle( rec );
1285
1286     r = MsiViewFetch( view, &rec );
1287     todo_wine
1288     {
1289         ok( r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
1290     }
1291
1292     MsiCloseHandle( view );
1293     MsiCloseHandle( hdb );
1294     DeleteFile(msifile);
1295 }
1296
1297 static void test_where(void)
1298 {
1299     MSIHANDLE hdb = 0, rec, view;
1300     LPCSTR query;
1301     UINT r;
1302     DWORD size;
1303     CHAR buf[MAX_PATH];
1304     UINT count;
1305
1306     hdb = create_db();
1307     ok( hdb, "failed to create db\n");
1308
1309     r = run_query( hdb, 0,
1310             "CREATE TABLE `Media` ("
1311             "`DiskId` SHORT NOT NULL, "
1312             "`LastSequence` LONG, "
1313             "`DiskPrompt` CHAR(64) LOCALIZABLE, "
1314             "`Cabinet` CHAR(255), "
1315             "`VolumeLabel` CHAR(32), "
1316             "`Source` CHAR(72) "
1317             "PRIMARY KEY `DiskId`)" );
1318     ok( r == S_OK, "cannot create Media table: %d\n", r );
1319
1320     r = run_query( hdb, 0, "INSERT INTO `Media` "
1321             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1322             "VALUES ( 1, 0, '', 'zero.cab', '', '' )" );
1323     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1324
1325     r = run_query( hdb, 0, "INSERT INTO `Media` "
1326             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1327             "VALUES ( 2, 1, '', 'one.cab', '', '' )" );
1328     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1329
1330     r = run_query( hdb, 0, "INSERT INTO `Media` "
1331             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1332             "VALUES ( 3, 2, '', 'two.cab', '', '' )" );
1333     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1334
1335     query = "SELECT * FROM `Media`";
1336     r = do_query(hdb, query, &rec);
1337     ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1338     ok( check_record( rec, 4, "zero.cab"), "wrong cabinet\n");
1339     MsiCloseHandle( rec );
1340
1341     query = "SELECT * FROM `Media` WHERE `LastSequence` >= 1";
1342     r = do_query(hdb, query, &rec);
1343     ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1344     ok( check_record( rec, 4, "one.cab"), "wrong cabinet\n");
1345
1346     r = MsiRecordGetInteger(rec, 1);
1347     ok( 2 == r, "field wrong\n");
1348     r = MsiRecordGetInteger(rec, 2);
1349     ok( 1 == r, "field wrong\n");
1350     MsiCloseHandle( rec );
1351
1352     query = "SELECT `DiskId` FROM `Media` WHERE `LastSequence` >= 1 AND DiskId >= 0";
1353     r = MsiDatabaseOpenView(hdb, query, &view);
1354     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
1355
1356     r = MsiViewExecute(view, 0);
1357     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
1358
1359     r = MsiViewFetch(view, &rec);
1360     ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r );
1361
1362     count = MsiRecordGetFieldCount( rec );
1363     ok( count == 1, "Expected 1 record fields, got %d\n", count );
1364
1365     size = MAX_PATH;
1366     r = MsiRecordGetString( rec, 1, buf, &size );
1367     ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1368     ok( !lstrcmp( buf, "2" ),
1369         "For (row %d, column 1) expected '%d', got %s\n", 0, 2, buf );
1370     MsiCloseHandle( rec );
1371
1372     r = MsiViewFetch(view, &rec);
1373     ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r );
1374
1375     size = MAX_PATH;
1376     r = MsiRecordGetString( rec, 1, buf, &size );
1377     ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1378     ok( !lstrcmp( buf, "3" ),
1379         "For (row %d, column 1) expected '%d', got %s\n", 1, 3, buf );
1380     MsiCloseHandle( rec );
1381
1382     r = MsiViewFetch(view, &rec);
1383     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
1384
1385     MsiViewClose(view);
1386     MsiCloseHandle(view);
1387
1388     MsiCloseHandle( rec );
1389
1390     rec = 0;
1391     query = "SELECT * FROM `Media` WHERE `DiskPrompt` IS NULL";
1392     r = do_query(hdb, query, &rec);
1393     ok( r == ERROR_SUCCESS, "query failed: %d\n", r );
1394     MsiCloseHandle( rec );
1395
1396     MsiCloseHandle( hdb );
1397     DeleteFile(msifile);
1398 }
1399
1400 static CHAR CURR_DIR[MAX_PATH];
1401
1402 static const CHAR test_data[] = "FirstPrimaryColumn\tSecondPrimaryColumn\tShortInt\tShortIntNullable\tLongInt\tLongIntNullable\tString\tLocalizableString\tLocalizableStringNullable\n"
1403                                 "s255\ti2\ti2\tI2\ti4\tI4\tS255\tS0\ts0\n"
1404                                 "TestTable\tFirstPrimaryColumn\n"
1405                                 "stringage\t5\t2\t\t2147483640\t-2147483640\tanother string\tlocalizable\tduh\n";
1406
1407 static const CHAR two_primary[] = "PrimaryOne\tPrimaryTwo\n"
1408                                   "s255\ts255\n"
1409                                   "TwoPrimary\tPrimaryOne\tPrimaryTwo\n"
1410                                   "papaya\tleaf\n"
1411                                   "papaya\tflower\n";
1412
1413 static void write_file(const CHAR *filename, const char *data, int data_size)
1414 {
1415     DWORD size;
1416
1417     HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
1418                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1419
1420     WriteFile(hf, data, data_size, &size, NULL);
1421     CloseHandle(hf);
1422 }
1423
1424 static UINT add_table_to_db(MSIHANDLE hdb, LPCSTR table_data)
1425 {
1426     UINT r;
1427
1428     write_file("temp_file", table_data, (lstrlen(table_data) - 1) * sizeof(char));
1429     r = MsiDatabaseImportA(hdb, CURR_DIR, "temp_file");
1430     DeleteFileA("temp_file");
1431
1432     return r;
1433 }
1434
1435 static void test_msiimport(void)
1436 {
1437     MSIHANDLE hdb, view, rec;
1438     LPCSTR query;
1439     UINT r, count;
1440     signed int i;
1441
1442     GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
1443
1444     r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb);
1445     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1446
1447     r = add_table_to_db(hdb, test_data);
1448     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1449
1450     r = add_table_to_db(hdb, two_primary);
1451     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1452
1453     query = "SELECT * FROM `TestTable`";
1454     r = MsiDatabaseOpenView(hdb, query, &view);
1455     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1456
1457     r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1458     count = MsiRecordGetFieldCount(rec);
1459     ok(count == 9, "Expected 9, got %d\n", count);
1460     ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n");
1461     ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n");
1462     ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n");
1463     ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n");
1464     ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n");
1465     ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n");
1466     ok(check_record(rec, 7, "String"), "Expected String\n");
1467     ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n");
1468     ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n");
1469     MsiCloseHandle(rec);
1470
1471     r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1472     count = MsiRecordGetFieldCount(rec);
1473     ok(count == 9, "Expected 9, got %d\n", count);
1474     ok(check_record(rec, 1, "s255"), "Expected s255\n");
1475     ok(check_record(rec, 2, "i2"), "Expected i2\n");
1476     ok(check_record(rec, 3, "i2"), "Expected i2\n");
1477     ok(check_record(rec, 4, "I2"), "Expected I2\n");
1478     ok(check_record(rec, 5, "i4"), "Expected i4\n");
1479     ok(check_record(rec, 6, "I4"), "Expected I4\n");
1480     ok(check_record(rec, 7, "S255"), "Expected S255\n");
1481     ok(check_record(rec, 8, "S0"), "Expected S0\n");
1482     ok(check_record(rec, 9, "s0"), "Expected s0\n");
1483     MsiCloseHandle(rec);
1484
1485     query = "SELECT * FROM `TestTable`";
1486     r = do_query(hdb, query, &rec);
1487     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1488     ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n");
1489     ok(check_record(rec, 7, "another string"), "Expected 'another string'\n");
1490     ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n");
1491     ok(check_record(rec, 9, "duh"), "Expected 'duh'\n");
1492
1493     i = MsiRecordGetInteger(rec, 2);
1494     ok(i == 5, "Expected 5, got %d\n", i);
1495
1496     i = MsiRecordGetInteger(rec, 3);
1497     ok(i == 2, "Expected 2, got %d\n", i);
1498
1499     i = MsiRecordGetInteger(rec, 4);
1500     ok(i == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", i);
1501
1502     i = MsiRecordGetInteger(rec, 5);
1503     ok(i == 2147483640, "Expected 2147483640, got %d\n", i);
1504
1505     i = MsiRecordGetInteger(rec, 6);
1506     ok(i == -2147483640, "Expected -2147483640, got %d\n", i);
1507
1508     MsiCloseHandle(rec);
1509     MsiCloseHandle(view);
1510
1511     query = "SELECT * FROM `TwoPrimary`";
1512     r = MsiDatabaseOpenView(hdb, query, &view);
1513     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1514
1515     r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1516     count = MsiRecordGetFieldCount(rec);
1517     ok(count == 2, "Expected 2, got %d\n", count);
1518     ok(check_record(rec, 1, "PrimaryOne"), "Expected PrimaryOne\n");
1519     ok(check_record(rec, 2, "PrimaryTwo"), "Expected PrimaryTwo\n");
1520
1521     MsiCloseHandle(rec);
1522
1523     r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1524     count = MsiRecordGetFieldCount(rec);
1525     ok(count == 2, "Expected 2, got %d\n", count);
1526     ok(check_record(rec, 1, "s255"), "Expected s255\n");
1527     ok(check_record(rec, 2, "s255"), "Expected s255\n");
1528     MsiCloseHandle(rec);
1529
1530     r = MsiViewExecute(view, 0);
1531     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1532
1533     r = MsiViewFetch(view, &rec);
1534     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1535
1536     ok(check_record(rec, 1, "papaya"), "Expected 'papaya'\n");
1537     ok(check_record(rec, 2, "leaf"), "Expected 'leaf'\n");
1538
1539     MsiCloseHandle(rec);
1540
1541     r = MsiViewFetch(view, &rec);
1542     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1543
1544     ok(check_record(rec, 1, "papaya"), "Expected 'papaya'\n");
1545     ok(check_record(rec, 2, "flower"), "Expected 'flower'\n");
1546
1547     MsiCloseHandle(rec);
1548
1549     r = MsiViewFetch(view, &rec);
1550     ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
1551
1552     r = MsiViewClose(view);
1553     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1554
1555     MsiCloseHandle(view);
1556     MsiCloseHandle(hdb);
1557     DeleteFileA(msifile);
1558 }
1559
1560 static void test_markers(void)
1561 {
1562     MSIHANDLE hdb, rec;
1563     LPCSTR query;
1564     UINT r;
1565
1566     hdb = create_db();
1567     ok( hdb, "failed to create db\n");
1568
1569     rec = MsiCreateRecord(3);
1570     MsiRecordSetString(rec, 1, "Table");
1571     MsiRecordSetString(rec, 2, "Apples");
1572     MsiRecordSetString(rec, 3, "Oranges");
1573
1574     /* try a legit create */
1575     query = "CREATE TABLE `Table` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1576     r = run_query(hdb, 0, query);
1577     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1578     MsiCloseHandle(rec);
1579
1580     /* try table name as marker */
1581     rec = MsiCreateRecord(1);
1582     MsiRecordSetString(rec, 1, "Fable");
1583     query = "CREATE TABLE `?` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1584     r = run_query(hdb, rec, query);
1585     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1586
1587     /* verify that we just created a table called '?', not 'Fable' */
1588     r = try_query(hdb, "SELECT * from `Fable`");
1589     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1590
1591     r = try_query(hdb, "SELECT * from `?`");
1592     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1593
1594     /* try table name as marker without backticks */
1595     MsiRecordSetString(rec, 1, "Mable");
1596     query = "CREATE TABLE ? ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1597     r = run_query(hdb, rec, query);
1598     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1599
1600     /* try one column name as marker */
1601     MsiRecordSetString(rec, 1, "One");
1602     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1603     r = run_query(hdb, rec, query);
1604     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1605     MsiCloseHandle(rec);
1606
1607     /* try column names as markers */
1608     rec = MsiCreateRecord(2);
1609     MsiRecordSetString(rec, 1, "One");
1610     MsiRecordSetString(rec, 2, "Two");
1611     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `One`)";
1612     r = run_query(hdb, rec, query);
1613     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1614     MsiCloseHandle(rec);
1615
1616     /* try names with backticks */
1617     rec = MsiCreateRecord(3);
1618     MsiRecordSetString(rec, 1, "One");
1619     MsiRecordSetString(rec, 2, "Two");
1620     MsiRecordSetString(rec, 3, "One");
1621     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1622     r = run_query(hdb, rec, query);
1623     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1624
1625     /* try names with backticks, minus definitions */
1626     query = "CREATE TABLE `Mable` ( `?`, `?` PRIMARY KEY `?`)";
1627     r = run_query(hdb, rec, query);
1628     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1629
1630     /* try names without backticks */
1631     query = "CREATE TABLE `Mable` ( ? SHORT NOT NULL, ? CHAR(255) PRIMARY KEY ?)";
1632     r = run_query(hdb, rec, query);
1633     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1634     MsiCloseHandle(rec);
1635
1636     /* try one long marker */
1637     rec = MsiCreateRecord(1);
1638     MsiRecordSetString(rec, 1, "`One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`");
1639     query = "CREATE TABLE `Mable` ( ? )";
1640     r = run_query(hdb, rec, query);
1641     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1642     MsiCloseHandle(rec);
1643
1644     /* try all names as markers */
1645     rec = MsiCreateRecord(4);
1646     MsiRecordSetString(rec, 1, "Mable");
1647     MsiRecordSetString(rec, 2, "One");
1648     MsiRecordSetString(rec, 3, "Two");
1649     MsiRecordSetString(rec, 4, "One");
1650     query = "CREATE TABLE `?` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1651     r = run_query(hdb, rec, query);
1652     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1653     MsiCloseHandle(rec);
1654
1655     /* try a legit insert */
1656     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( 5, 'hello' )";
1657     r = run_query(hdb, 0, query);
1658     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1659
1660     r = try_query(hdb, "SELECT * from `Table`");
1661     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1662
1663     /* try values as markers */
1664     rec = MsiCreateRecord(2);
1665     MsiRecordSetInteger(rec, 1, 4);
1666     MsiRecordSetString(rec, 2, "hi");
1667     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1668     r = run_query(hdb, rec, query);
1669     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1670     MsiCloseHandle(rec);
1671
1672     /* try column names and values as markers */
1673     rec = MsiCreateRecord(4);
1674     MsiRecordSetString(rec, 1, "One");
1675     MsiRecordSetString(rec, 2, "Two");
1676     MsiRecordSetInteger(rec, 3, 5);
1677     MsiRecordSetString(rec, 4, "hi");
1678     query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( ?, '?' )";
1679     r = run_query(hdb, rec, query);
1680     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1681     MsiCloseHandle(rec);
1682
1683     /* try column names as markers */
1684     rec = MsiCreateRecord(2);
1685     MsiRecordSetString(rec, 1, "One");
1686     MsiRecordSetString(rec, 2, "Two");
1687     query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( 3, 'yellow' )";
1688     r = run_query(hdb, rec, query);
1689     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1690     MsiCloseHandle(rec);
1691
1692     /* try table name as a marker */
1693     rec = MsiCreateRecord(1);
1694     MsiRecordSetString(rec, 1, "Table");
1695     query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( 2, 'green' )";
1696     r = run_query(hdb, rec, query);
1697     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1698     MsiCloseHandle(rec);
1699
1700     /* try table name and values as markers */
1701     rec = MsiCreateRecord(3);
1702     MsiRecordSetString(rec, 1, "Table");
1703     MsiRecordSetInteger(rec, 2, 10);
1704     MsiRecordSetString(rec, 3, "haha");
1705     query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( ?, '?' )";
1706     r = run_query(hdb, rec, query);
1707     ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
1708     MsiCloseHandle(rec);
1709
1710     /* try all markers */
1711     rec = MsiCreateRecord(5);
1712     MsiRecordSetString(rec, 1, "Table");
1713     MsiRecordSetString(rec, 1, "One");
1714     MsiRecordSetString(rec, 1, "Two");
1715     MsiRecordSetInteger(rec, 2, 10);
1716     MsiRecordSetString(rec, 3, "haha");
1717     query = "INSERT INTO `?` ( `?`, `?` ) VALUES ( ?, '?' )";
1718     r = run_query(hdb, rec, query);
1719     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1720     MsiCloseHandle(rec);
1721
1722     /* insert an integer as a string */
1723     rec = MsiCreateRecord(2);
1724     MsiRecordSetString(rec, 1, "11");
1725     MsiRecordSetString(rec, 2, "hi");
1726     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1727     r = run_query(hdb, rec, query);
1728     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1729     MsiCloseHandle(rec);
1730
1731     /* leave off the '' for the string */
1732     rec = MsiCreateRecord(2);
1733     MsiRecordSetInteger(rec, 1, 12);
1734     MsiRecordSetString(rec, 2, "hi");
1735     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, ? )";
1736     r = run_query(hdb, rec, query);
1737     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1738     MsiCloseHandle(rec);
1739
1740     MsiCloseHandle(hdb);
1741     DeleteFileA(msifile);
1742 }
1743
1744 #define MY_NVIEWS 4000    /* Largest installer I've seen uses < 2k */
1745 static void test_handle_limit(void)
1746 {
1747     int i;
1748     MSIHANDLE hdb;
1749     MSIHANDLE hviews[MY_NVIEWS];
1750     UINT r;
1751
1752     /* create an empty db */
1753     hdb = create_db();
1754     ok( hdb, "failed to create db\n");
1755
1756     memset(hviews, 0, sizeof(hviews));
1757
1758     for (i=0; i<MY_NVIEWS; i++) {
1759         static char szQueryBuf[256] = "SELECT * from `_Tables`";
1760         hviews[i] = 0xdeadbeeb;
1761         r = MsiDatabaseOpenView(hdb, szQueryBuf, &hviews[i]);
1762         if( r != ERROR_SUCCESS || hviews[i] == 0xdeadbeeb || 
1763             hviews[i] == 0 || (i && (hviews[i] == hviews[i-1])))
1764             break;
1765     }
1766
1767     ok( i == MY_NVIEWS, "problem opening views\n");
1768
1769     for (i=0; i<MY_NVIEWS; i++) {
1770         if (hviews[i] != 0 && hviews[i] != 0xdeadbeeb) {
1771             r = MsiCloseHandle(hviews[i]);
1772             if (r != ERROR_SUCCESS)
1773                 break;
1774         }
1775     }
1776
1777     ok( i == MY_NVIEWS, "problem closing views\n");
1778
1779     r = MsiCloseHandle(hdb);
1780     ok( r == ERROR_SUCCESS, "failed to close database\n");
1781 }
1782
1783 static void generate_transform(void)
1784 {
1785     MSIHANDLE hdb1, hdb2, hrec;
1786     LPCSTR query;
1787     UINT r;
1788
1789     /* start with two identical databases */
1790     CopyFile(msifile2, msifile, FALSE);
1791
1792     r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb1 );
1793     ok( r == ERROR_SUCCESS , "Failed to create database\n" );
1794
1795     r = MsiDatabaseCommit( hdb1 );
1796     ok( r == ERROR_SUCCESS , "Failed to commit database\n" );
1797
1798     r = MsiOpenDatabase(msifile2, MSIDBOPEN_READONLY, &hdb2 );
1799     ok( r == ERROR_SUCCESS , "Failed to create database\n" );
1800
1801     /* the transform between two identical database should be empty */
1802     r = MsiDatabaseGenerateTransform(hdb1, hdb2, NULL, 0, 0);
1803     todo_wine {
1804     ok( r == ERROR_NO_DATA, "return code %d, should be ERROR_NO_DATA\n", r );
1805     }
1806
1807     query = "CREATE TABLE `AAR` ( `BAR` SHORT NOT NULL, `CAR` CHAR(255) PRIMARY KEY `CAR`)";
1808     r = run_query(hdb1, 0, query);
1809     ok(r == ERROR_SUCCESS, "failed to add table\n");
1810
1811     query = "INSERT INTO `AAR` ( `BAR`, `CAR` ) VALUES ( 1, 'vw' )";
1812     r = run_query(hdb1, 0, query);
1813     ok(r == ERROR_SUCCESS, "failed to add row 1\n");
1814
1815     query = "INSERT INTO `AAR` ( `BAR`, `CAR` ) VALUES ( 2, 'bmw' )";
1816     r = run_query(hdb1, 0, query);
1817     ok(r == ERROR_SUCCESS, "failed to add row 2\n");
1818
1819     query = "UPDATE `MOO` SET `OOO` = 'c' WHERE `NOO` = 1";
1820     r = run_query(hdb1, 0, query);
1821     ok(r == ERROR_SUCCESS, "failed to modify row\n");
1822
1823     query = "DELETE FROM `MOO` WHERE `NOO` = 3";
1824     r = run_query(hdb1, 0, query);
1825     ok(r == ERROR_SUCCESS, "failed to delete row\n");
1826
1827     hrec = MsiCreateRecord(2);
1828     r = MsiRecordSetInteger(hrec, 1, 1);
1829     ok(r == ERROR_SUCCESS, "failed to set integer\n");
1830
1831     write_file("testdata.bin", "naengmyon", 9);
1832     r = MsiRecordSetStream(hrec, 2, "testdata.bin");
1833     ok(r == ERROR_SUCCESS, "failed to set stream\n");
1834
1835     query = "INSERT INTO `BINARY` ( `ID`, `BLOB` ) VALUES ( ?, ? )";
1836     r = run_query(hdb1, hrec, query);
1837     ok(r == ERROR_SUCCESS, "failed to add row with blob\n");
1838
1839     MsiCloseHandle(hrec);
1840
1841     /* database needs to be committed */
1842     MsiDatabaseCommit(hdb1);
1843
1844     r = MsiDatabaseGenerateTransform(hdb1, hdb2, mstfile, 0, 0);
1845     ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
1846
1847     MsiCloseHandle( hdb1 );
1848     MsiCloseHandle( hdb2 );
1849
1850     DeleteFile("testdata.bin");
1851 }
1852
1853 /* data for generating a transform */
1854
1855 /* tables transform names - encoded as they would be in an msi database file */
1856 static const WCHAR name1[] = { 0x4840, 0x3a8a, 0x481b, 0 }; /* AAR */
1857 static const WCHAR name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
1858 static const WCHAR name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
1859 static const WCHAR name4[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
1860 static const WCHAR name5[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
1861 static const WCHAR name6[] = { 0x4840, 0x3e16, 0x4818, 0}; /* MOO */
1862 static const WCHAR name7[] = { 0x4840, 0x3c8b, 0x3a97, 0x409b, 0 }; /* BINARY */
1863 static const WCHAR name8[] = { 0x3c8b, 0x3a97, 0x409b, 0x387e, 0 }; /* BINARY.1 */
1864
1865 /* data in each table */
1866 static const WCHAR data1[] = { /* AAR */
1867     0x0201, 0x0005, 0x8001,  /* 0x0201 = add row (1), two shorts */
1868     0x0201, 0x0006, 0x8002,
1869 };
1870 static const WCHAR data2[] = { /* _Columns */
1871     0x0401, 0x0002, 0x0000, 0x0003, 0xbdff,  /* 0x0401 = add row (1), 4 shorts */
1872     0x0401, 0x0002, 0x0000, 0x0004, 0x8502,
1873 };
1874 static const WCHAR data3[] = { /* _Tables */
1875     0x0101, 0x0002, /* 0x0101 = add row (1), 1 short */
1876 };
1877 static const char data4[] = /* _StringData */
1878     "cAARCARBARvwbmw";  /* all the strings squashed together */
1879 static const WCHAR data5[] = { /* _StringPool */
1880 /*  len, refs */
1881     0,   0,    /* string 0 ''    */
1882     1,   1,    /* string 1 'c'   */
1883     3,   3,    /* string 2 'AAR' */
1884     3,   1,    /* string 3 'CAR' */
1885     3,   1,    /* string 4 'BAR' */
1886     2,   1,    /* string 5 'vw'  */
1887     3,   1,    /* string 6 'bmw' */
1888 };
1889 /* update row, 0x0002 is a bitmask of present column data, keys are excluded */
1890 static const WCHAR data6[] = { /* MOO */
1891     0x0002, 0x8001, 0x0001, /* update row */
1892     0x0000, 0x8003,         /* delete row */
1893 };
1894
1895 static const WCHAR data7[] = { /* BINARY */
1896     0x0201, 0x8001, 0x0001,
1897 };
1898
1899 static const char data8[] =  /* stream data for the BINARY table */
1900     "naengmyon";
1901
1902 static const struct {
1903     LPCWSTR name;
1904     const void *data;
1905     DWORD size;
1906 } table_transform_data[] =
1907 {
1908     { name1, data1, sizeof data1 },
1909     { name2, data2, sizeof data2 },
1910     { name3, data3, sizeof data3 },
1911     { name4, data4, sizeof data4 - 1 },
1912     { name5, data5, sizeof data5 },
1913     { name6, data6, sizeof data6 },
1914     { name7, data7, sizeof data7 },
1915     { name8, data8, sizeof data8 - 1 },
1916 };
1917
1918 #define NUM_TRANSFORM_TABLES (sizeof table_transform_data/sizeof table_transform_data[0])
1919
1920 static void generate_transform_manual(void)
1921 {
1922     IStorage *stg = NULL;
1923     IStream *stm;
1924     WCHAR name[0x20];
1925     HRESULT r;
1926     DWORD i, count;
1927     const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE;
1928
1929     const CLSID CLSID_MsiTransform = { 0xc1082,0,0,{0xc0,0,0,0,0,0,0,0x46}};
1930
1931     MultiByteToWideChar(CP_ACP, 0, mstfile, -1, name, 0x20);
1932
1933     r = StgCreateDocfile(name, mode, 0, &stg);
1934     ok(r == S_OK, "failed to create storage\n");
1935     if (!stg)
1936         return;
1937
1938     r = IStorage_SetClass( stg, &CLSID_MsiTransform );
1939     ok(r == S_OK, "failed to set storage type\n");
1940
1941     for (i=0; i<NUM_TRANSFORM_TABLES; i++)
1942     {
1943         r = IStorage_CreateStream( stg, table_transform_data[i].name,
1944                             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
1945         if (FAILED(r))
1946         {
1947             ok(0, "failed to create stream %08x\n", r);
1948             continue;
1949         }
1950
1951         r = IStream_Write( stm, table_transform_data[i].data,
1952                           table_transform_data[i].size, &count );
1953         if (FAILED(r) || count != table_transform_data[i].size)
1954             ok(0, "failed to write stream\n");
1955         IStream_Release(stm);
1956     }
1957
1958     IStorage_Release(stg);
1959 }
1960
1961 static void test_try_transform(void)
1962 {
1963     MSIHANDLE hdb, hrec;
1964     LPCSTR query;
1965     UINT r;
1966     DWORD sz;
1967     char buffer[0x10];
1968
1969     DeleteFile(msifile);
1970     DeleteFile(msifile2);
1971     DeleteFile(mstfile);
1972
1973     /* create an empty database */
1974     r = MsiOpenDatabase(msifile2, MSIDBOPEN_CREATE, &hdb );
1975     ok( r == ERROR_SUCCESS , "Failed to create database\n" );
1976
1977     query = "CREATE TABLE `MOO` ( `NOO` SHORT NOT NULL, `OOO` CHAR(255) PRIMARY KEY `NOO`)";
1978     r = run_query(hdb, 0, query);
1979     ok(r == ERROR_SUCCESS, "failed to add table\n");
1980
1981     query = "INSERT INTO `MOO` ( `NOO`, `OOO` ) VALUES ( 1, 'a' )";
1982     r = run_query(hdb, 0, query);
1983     ok(r == ERROR_SUCCESS, "failed to add row\n");
1984
1985     query = "INSERT INTO `MOO` ( `NOO`, `OOO` ) VALUES ( 2, 'b' )";
1986     r = run_query(hdb, 0, query);
1987     ok(r == ERROR_SUCCESS, "failed to add row\n");
1988
1989     query = "INSERT INTO `MOO` ( `NOO`, `OOO` ) VALUES ( 3, 'c' )";
1990     r = run_query(hdb, 0, query);
1991     ok(r == ERROR_SUCCESS, "failed to add row\n");
1992
1993     query = "CREATE TABLE `BINARY` ( `ID` SHORT NOT NULL, `BLOB` OBJECT PRIMARY KEY `ID`)";
1994     r = run_query(hdb, 0, query);
1995     ok(r == ERROR_SUCCESS, "failed to add table\n");
1996
1997     hrec = MsiCreateRecord(2);
1998     r = MsiRecordSetInteger(hrec, 1, 2);
1999     ok(r == ERROR_SUCCESS, "failed to set integer\n");
2000
2001     write_file("testdata.bin", "lamyon", 6);
2002     r = MsiRecordSetStream(hrec, 2, "testdata.bin");
2003     ok(r == ERROR_SUCCESS, "failed to set stream\n");
2004
2005     query = "INSERT INTO `BINARY` ( `ID`, `BLOB` ) VALUES ( ?, ? )";
2006     r = run_query(hdb, hrec, query);
2007     ok(r == ERROR_SUCCESS, "failed to add row with blob\n");
2008
2009     MsiCloseHandle(hrec);
2010
2011     r = MsiDatabaseCommit( hdb );
2012     ok( r == ERROR_SUCCESS , "Failed to commit database\n" );
2013
2014     MsiCloseHandle( hdb );
2015     DeleteFileA("testdata.bin");
2016
2017     /*
2018      * Both these generate an equivilent transform,
2019      *  but the first doesn't work in Wine yet
2020      *  because MsiDatabaseGenerateTransform is unimplemented.
2021      */
2022     if (0)
2023         generate_transform();
2024     else
2025         generate_transform_manual();
2026
2027     r = MsiOpenDatabase(msifile2, MSIDBOPEN_DIRECT, &hdb );
2028     ok( r == ERROR_SUCCESS , "Failed to create database\n" );
2029
2030     r = MsiDatabaseApplyTransform( hdb, mstfile, 0 );
2031     ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
2032
2033     MsiDatabaseCommit( hdb );
2034
2035     /* check new values */
2036     hrec = 0;
2037     query = "select `BAR`,`CAR` from `AAR` where `BAR` = 1 AND `CAR` = 'vw'";
2038     r = do_query(hdb, query, &hrec);
2039     ok(r == ERROR_SUCCESS, "select query failed\n");
2040     MsiCloseHandle(hrec);
2041
2042     query = "select `BAR`,`CAR` from `AAR` where `BAR` = 2 AND `CAR` = 'bmw'";
2043     hrec = 0;
2044     r = do_query(hdb, query, &hrec);
2045     ok(r == ERROR_SUCCESS, "select query failed\n");
2046     MsiCloseHandle(hrec);
2047
2048     /* check updated values */
2049     hrec = 0;
2050     query = "select `NOO`,`OOO` from `MOO` where `NOO` = 1 AND `OOO` = 'c'";
2051     r = do_query(hdb, query, &hrec);
2052     ok(r == ERROR_SUCCESS, "select query failed\n");
2053     MsiCloseHandle(hrec);
2054
2055     /* check unchanged value */
2056     hrec = 0;
2057     query = "select `NOO`,`OOO` from `MOO` where `NOO` = 2 AND `OOO` = 'b'";
2058     r = do_query(hdb, query, &hrec);
2059     ok(r == ERROR_SUCCESS, "select query failed\n");
2060     MsiCloseHandle(hrec);
2061
2062     /* check deleted value */
2063     hrec = 0;
2064     query = "select * from `MOO` where `NOO` = 3";
2065     r = do_query(hdb, query, &hrec);
2066     ok(r == ERROR_NO_MORE_ITEMS, "select query failed\n");
2067     if (hrec) MsiCloseHandle(hrec);
2068
2069     /* check added stream */
2070     hrec = 0;
2071     query = "select `BLOB` from `BINARY` where `ID` = 1";
2072     r = do_query(hdb, query, &hrec);
2073     ok(r == ERROR_SUCCESS, "select query failed\n");
2074
2075     /* check the contents of the stream */
2076     sz = sizeof buffer;
2077     r = MsiRecordReadStream( hrec, 1, buffer, &sz );
2078     ok(r == ERROR_SUCCESS, "read stream failed\n");
2079     ok(!memcmp(buffer, "naengmyon", 9), "stream data was wrong\n");
2080     ok(sz == 9, "stream data was wrong size\n");
2081     if (hrec) MsiCloseHandle(hrec);
2082
2083     MsiCloseHandle( hdb );
2084
2085     DeleteFile(msifile);
2086     DeleteFile(msifile2);
2087     DeleteFile(mstfile);
2088 }
2089
2090 struct join_res
2091 {
2092     const CHAR one[MAX_PATH];
2093     const CHAR two[MAX_PATH];
2094 };
2095
2096 struct join_res_4col
2097 {
2098     const CHAR one[MAX_PATH];
2099     const CHAR two[MAX_PATH];
2100     const CHAR three[MAX_PATH];
2101     const CHAR four[MAX_PATH];
2102 };
2103
2104 static const struct join_res join_res_first[] =
2105 {
2106     { "alveolar", "procerus" },
2107     { "septum", "procerus" },
2108     { "septum", "nasalis" },
2109     { "ramus", "nasalis" },
2110     { "malar", "mentalis" },
2111 };
2112
2113 static const struct join_res join_res_second[] =
2114 {
2115     { "nasal", "septum" },
2116     { "mandible", "ramus" },
2117 };
2118
2119 static const struct join_res join_res_third[] =
2120 {
2121     { "msvcp.dll", "abcdefgh" },
2122     { "msvcr.dll", "ijklmnop" },
2123 };
2124
2125 static const struct join_res join_res_fourth[] =
2126 {
2127     { "msvcp.dll.01234", "single.dll.31415" },
2128 };
2129
2130 static const struct join_res join_res_fifth[] =
2131 {
2132     { "malar", "procerus" },
2133 };
2134
2135 static const struct join_res join_res_sixth[] =
2136 {
2137     { "malar", "procerus" },
2138     { "malar", "procerus" },
2139     { "malar", "nasalis" },
2140     { "malar", "nasalis" },
2141     { "malar", "nasalis" },
2142     { "malar", "mentalis" },
2143 };
2144
2145 static const struct join_res join_res_seventh[] =
2146 {
2147     { "malar", "nasalis" },
2148     { "malar", "nasalis" },
2149     { "malar", "nasalis" },
2150 };
2151
2152 static const struct join_res_4col join_res_eighth[] =
2153 {
2154     { "msvcp.dll", "msvcp.dll.01234", "msvcp.dll.01234", "abcdefgh" },
2155     { "msvcr.dll", "msvcr.dll.56789", "msvcp.dll.01234", "abcdefgh" },
2156     { "msvcp.dll", "msvcp.dll.01234", "msvcr.dll.56789", "ijklmnop" },
2157     { "msvcr.dll", "msvcr.dll.56789", "msvcr.dll.56789", "ijklmnop" },
2158     { "msvcp.dll", "msvcp.dll.01234", "single.dll.31415", "msvcp.dll" },
2159     { "msvcr.dll", "msvcr.dll.56789", "single.dll.31415", "msvcp.dll" },
2160 };
2161
2162 static void test_join(void)
2163 {
2164     MSIHANDLE hdb, hview, hrec;
2165     LPCSTR query;
2166     CHAR buf[MAX_PATH];
2167     UINT r, count;
2168     DWORD size, i;
2169     BOOL data_correct;
2170
2171     hdb = create_db();
2172     ok( hdb, "failed to create db\n");
2173
2174     r = create_component_table( hdb );
2175     ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r );
2176
2177     r = add_component_entry( hdb, "'zygomatic', 'malar', 'INSTALLDIR', 0, '', ''" );
2178     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
2179
2180     r = add_component_entry( hdb, "'maxilla', 'alveolar', 'INSTALLDIR', 0, '', ''" );
2181     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
2182
2183     r = add_component_entry( hdb, "'nasal', 'septum', 'INSTALLDIR', 0, '', ''" );
2184     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
2185
2186     r = add_component_entry( hdb, "'mandible', 'ramus', 'INSTALLDIR', 0, '', ''" );
2187     ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
2188
2189     r = create_feature_components_table( hdb );
2190     ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r );
2191
2192     r = add_feature_components_entry( hdb, "'procerus', 'maxilla'" );
2193     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
2194
2195     r = add_feature_components_entry( hdb, "'procerus', 'nasal'" );
2196     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
2197
2198     r = add_feature_components_entry( hdb, "'nasalis', 'nasal'" );
2199     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
2200
2201     r = add_feature_components_entry( hdb, "'nasalis', 'mandible'" );
2202     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
2203
2204     r = add_feature_components_entry( hdb, "'nasalis', 'notacomponent'" );
2205     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
2206
2207     r = add_feature_components_entry( hdb, "'mentalis', 'zygomatic'" );
2208     ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
2209
2210     r = create_std_dlls_table( hdb );
2211     ok( r == ERROR_SUCCESS, "cannot create StdDlls table: %d\n", r );
2212
2213     r = add_std_dlls_entry( hdb, "'msvcp.dll', 'msvcp.dll.01234'" );
2214     ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r );
2215
2216     r = add_std_dlls_entry( hdb, "'msvcr.dll', 'msvcr.dll.56789'" );
2217     ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r );
2218
2219     r = create_binary_table( hdb );
2220     ok( r == ERROR_SUCCESS, "cannot create Binary table: %d\n", r );
2221
2222     r = add_binary_entry( hdb, "'msvcp.dll.01234', 'abcdefgh'" );
2223     ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r );
2224
2225     r = add_binary_entry( hdb, "'msvcr.dll.56789', 'ijklmnop'" );
2226     ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r );
2227
2228     r = add_binary_entry( hdb, "'single.dll.31415', 'msvcp.dll'" );
2229     ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r );
2230
2231     query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` "
2232             "FROM `Component`, `FeatureComponents` "
2233             "WHERE `Component`.`Component` = `FeatureComponents`.`Component_` "
2234             "ORDER BY `Feature_`";
2235     r = MsiDatabaseOpenView(hdb, query, &hview);
2236     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2237
2238     r = MsiViewExecute(hview, 0);
2239     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2240
2241     i = 0;
2242     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2243     {
2244         count = MsiRecordGetFieldCount( hrec );
2245         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2246
2247         size = MAX_PATH;
2248         r = MsiRecordGetString( hrec, 1, buf, &size );
2249         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2250         ok( !lstrcmp( buf, join_res_first[i].one ),
2251             "For (row %d, column 1) expected '%s', got %s\n", i, join_res_first[i].one, buf );
2252
2253         size = MAX_PATH;
2254         r = MsiRecordGetString( hrec, 2, buf, &size );
2255         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2256         ok( !lstrcmp( buf, join_res_first[i].two ),
2257             "For (row %d, column 2) expected '%s', got %s\n", i, join_res_first[i].two, buf );
2258
2259         i++;
2260         MsiCloseHandle(hrec);
2261     }
2262
2263     ok( i == 5, "Expected 5 rows, got %d\n", i );
2264     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2265
2266     MsiViewClose(hview);
2267     MsiCloseHandle(hview);
2268
2269     /* try a join without a WHERE condition */
2270     query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` "
2271             "FROM `Component`, `FeatureComponents` ";
2272     r = MsiDatabaseOpenView(hdb, query, &hview);
2273     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2274
2275     r = MsiViewExecute(hview, 0);
2276     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2277
2278     i = 0;
2279     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2280     {
2281         i++;
2282         MsiCloseHandle(hrec);
2283     }
2284     ok( i == 24, "Expected 24 rows, got %d\n", i );
2285
2286     MsiViewClose(hview);
2287     MsiCloseHandle(hview);
2288
2289     query = "SELECT DISTINCT Component, ComponentId FROM FeatureComponents, Component "
2290             "WHERE FeatureComponents.Component_=Component.Component "
2291             "AND (Feature_='nasalis') ORDER BY Feature_";
2292     r = MsiDatabaseOpenView(hdb, query, &hview);
2293     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2294
2295     r = MsiViewExecute(hview, 0);
2296     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2297
2298     i = 0;
2299     data_correct = TRUE;
2300     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2301     {
2302         count = MsiRecordGetFieldCount( hrec );
2303         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2304
2305         size = MAX_PATH;
2306         r = MsiRecordGetString( hrec, 1, buf, &size );
2307         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2308         if( lstrcmp( buf, join_res_second[i].one ))
2309             data_correct = FALSE;
2310
2311         size = MAX_PATH;
2312         r = MsiRecordGetString( hrec, 2, buf, &size );
2313         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2314         if( lstrcmp( buf, join_res_second[i].two ))
2315             data_correct = FALSE;
2316
2317         i++;
2318         MsiCloseHandle(hrec);
2319     }
2320
2321     ok( data_correct, "data returned in the wrong order\n");
2322
2323     ok( i == 2, "Expected 2 rows, got %d\n", i );
2324     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2325
2326     MsiViewClose(hview);
2327     MsiCloseHandle(hview);
2328
2329     query = "SELECT `StdDlls`.`File`, `Binary`.`Data` "
2330             "FROM `StdDlls`, `Binary` "
2331             "WHERE `StdDlls`.`Binary_` = `Binary`.`Name` "
2332             "ORDER BY `File`";
2333     r = MsiDatabaseOpenView(hdb, query, &hview);
2334     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2335
2336     r = MsiViewExecute(hview, 0);
2337     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2338
2339     i = 0;
2340     data_correct = TRUE;
2341     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2342     {
2343         count = MsiRecordGetFieldCount( hrec );
2344         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2345
2346         size = MAX_PATH;
2347         r = MsiRecordGetString( hrec, 1, buf, &size );
2348         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2349         if( lstrcmp( buf, join_res_third[i].one ) )
2350             data_correct = FALSE;
2351
2352         size = MAX_PATH;
2353         r = MsiRecordGetString( hrec, 2, buf, &size );
2354         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2355         if( lstrcmp( buf, join_res_third[i].two ) )
2356             data_correct = FALSE;
2357
2358         i++;
2359         MsiCloseHandle(hrec);
2360     }
2361     ok( data_correct, "data returned in the wrong order\n");
2362
2363     ok( i == 2, "Expected 2 rows, got %d\n", i );
2364
2365     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2366
2367     MsiViewClose(hview);
2368     MsiCloseHandle(hview);
2369
2370     query = "SELECT `StdDlls`.`Binary_`, `Binary`.`Name` "
2371             "FROM `StdDlls`, `Binary` "
2372             "WHERE `StdDlls`.`File` = `Binary`.`Data` "
2373             "ORDER BY `Name`";
2374     r = MsiDatabaseOpenView(hdb, query, &hview);
2375     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2376
2377     r = MsiViewExecute(hview, 0);
2378     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2379
2380     i = 0;
2381     data_correct = TRUE;
2382     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2383     {
2384         count = MsiRecordGetFieldCount( hrec );
2385         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2386
2387         size = MAX_PATH;
2388         r = MsiRecordGetString( hrec, 1, buf, &size );
2389         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2390         if( lstrcmp( buf, join_res_fourth[i].one ))
2391             data_correct = FALSE;
2392
2393         size = MAX_PATH;
2394         r = MsiRecordGetString( hrec, 2, buf, &size );
2395         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2396         if( lstrcmp( buf, join_res_fourth[i].two ))
2397             data_correct = FALSE;
2398
2399         i++;
2400         MsiCloseHandle(hrec);
2401     }
2402     ok( data_correct, "data returned in the wrong order\n");
2403
2404     ok( i == 1, "Expected 1 rows, got %d\n", i );
2405     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2406
2407     MsiViewClose(hview);
2408     MsiCloseHandle(hview);
2409
2410     query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` "
2411             "FROM `Component`, `FeatureComponents` "
2412             "WHERE `Component`.`Component` = 'zygomatic' "
2413             "AND `FeatureComponents`.`Component_` = 'maxilla' "
2414             "ORDER BY `Feature_`";
2415     r = MsiDatabaseOpenView(hdb, query, &hview);
2416     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2417
2418     r = MsiViewExecute(hview, 0);
2419     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2420
2421     i = 0;
2422     data_correct = TRUE;
2423     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2424     {
2425         count = MsiRecordGetFieldCount( hrec );
2426         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2427
2428         size = MAX_PATH;
2429         r = MsiRecordGetString( hrec, 1, buf, &size );
2430         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2431         if( lstrcmp( buf, join_res_fifth[i].one ))
2432             data_correct = FALSE;
2433
2434         size = MAX_PATH;
2435         r = MsiRecordGetString( hrec, 2, buf, &size );
2436         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2437         if( lstrcmp( buf, join_res_fifth[i].two ))
2438             data_correct = FALSE;
2439
2440         i++;
2441         MsiCloseHandle(hrec);
2442     }
2443     ok( data_correct, "data returned in the wrong order\n");
2444
2445     ok( i == 1, "Expected 1 rows, got %d\n", i );
2446     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2447
2448     MsiViewClose(hview);
2449     MsiCloseHandle(hview);
2450
2451     query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` "
2452             "FROM `Component`, `FeatureComponents` "
2453             "WHERE `Component` = 'zygomatic' "
2454             "ORDER BY `Feature_`";
2455     r = MsiDatabaseOpenView(hdb, query, &hview);
2456     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2457
2458     r = MsiViewExecute(hview, 0);
2459     todo_wine
2460     {
2461         ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2462     }
2463
2464     i = 0;
2465     data_correct = TRUE;
2466     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2467     {
2468         count = MsiRecordGetFieldCount( hrec );
2469         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2470
2471         size = MAX_PATH;
2472         r = MsiRecordGetString( hrec, 1, buf, &size );
2473         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2474         if( lstrcmp( buf, join_res_sixth[i].one ))
2475             data_correct = FALSE;
2476
2477         size = MAX_PATH;
2478         r = MsiRecordGetString( hrec, 2, buf, &size );
2479         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2480         if( lstrcmp( buf, join_res_sixth[i].two ))
2481             data_correct = FALSE;
2482
2483         i++;
2484         MsiCloseHandle(hrec);
2485     }
2486     ok( data_correct, "data returned in the wrong order\n");
2487
2488     todo_wine
2489     {
2490         ok( i == 6, "Expected 6 rows, got %d\n", i );
2491     }
2492     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2493
2494     MsiViewClose(hview);
2495     MsiCloseHandle(hview);
2496
2497     query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` "
2498             "FROM `Component`, `FeatureComponents` "
2499             "WHERE `Component` = 'zygomatic' "
2500             "AND `Feature_` = 'nasalis' "
2501             "ORDER BY `Feature_`";
2502     r = MsiDatabaseOpenView(hdb, query, &hview);
2503     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2504
2505     r = MsiViewExecute(hview, 0);
2506     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2507
2508     i = 0;
2509     data_correct = TRUE;
2510     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2511     {
2512         count = MsiRecordGetFieldCount( hrec );
2513         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2514
2515         size = MAX_PATH;
2516         r = MsiRecordGetString( hrec, 1, buf, &size );
2517         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2518         if( lstrcmp( buf, join_res_seventh[i].one ))
2519             data_correct = FALSE;
2520
2521         size = MAX_PATH;
2522         r = MsiRecordGetString( hrec, 2, buf, &size );
2523         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2524         if( lstrcmp( buf, join_res_seventh[i].two ))
2525             data_correct = FALSE;
2526
2527         i++;
2528         MsiCloseHandle(hrec);
2529     }
2530
2531     ok( data_correct, "data returned in the wrong order\n");
2532     ok( i == 3, "Expected 3 rows, got %d\n", i );
2533     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2534
2535     MsiViewClose(hview);
2536     MsiCloseHandle(hview);
2537
2538     query = "SELECT `StdDlls`.`File`, `Binary`.`Data` "
2539             "FROM `StdDlls`, `Binary` ";
2540     r = MsiDatabaseOpenView(hdb, query, &hview);
2541     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2542
2543     r = MsiViewExecute(hview, 0);
2544     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2545
2546     i = 0;
2547     data_correct = TRUE;
2548     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2549     {
2550         count = MsiRecordGetFieldCount( hrec );
2551         ok( count == 2, "Expected 2 record fields, got %d\n", count );
2552
2553         size = MAX_PATH;
2554         r = MsiRecordGetString( hrec, 1, buf, &size );
2555         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2556         if( lstrcmp( buf, join_res_eighth[i].one ))
2557             data_correct = FALSE;
2558
2559         size = MAX_PATH;
2560         r = MsiRecordGetString( hrec, 2, buf, &size );
2561         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2562         if( lstrcmp( buf, join_res_eighth[i].four ))
2563             data_correct = FALSE;
2564
2565         i++;
2566         MsiCloseHandle(hrec);
2567     }
2568
2569     todo_wine ok( data_correct, "data returned in the wrong order\n");
2570     ok( i == 6, "Expected 6 rows, got %d\n", i );
2571     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2572
2573     MsiViewClose(hview);
2574     MsiCloseHandle(hview);
2575
2576     query = "SELECT * FROM `StdDlls`, `Binary` ";
2577     r = MsiDatabaseOpenView(hdb, query, &hview);
2578     ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
2579
2580     r = MsiViewExecute(hview, 0);
2581     ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
2582
2583     i = 0;
2584     data_correct = TRUE;
2585     while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
2586     {
2587         count = MsiRecordGetFieldCount( hrec );
2588         ok( count == 4, "Expected 4 record fields, got %d\n", count );
2589
2590         size = MAX_PATH;
2591         r = MsiRecordGetString( hrec, 1, buf, &size );
2592         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2593         if( lstrcmp( buf, join_res_eighth[i].one ))
2594             data_correct = FALSE;
2595
2596         size = MAX_PATH;
2597         r = MsiRecordGetString( hrec, 2, buf, &size );
2598         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2599         if( lstrcmp( buf, join_res_eighth[i].two ))
2600             data_correct = FALSE;
2601
2602         size = MAX_PATH;
2603         r = MsiRecordGetString( hrec, 3, buf, &size );
2604         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2605         if( lstrcmp( buf, join_res_eighth[i].three ))
2606             data_correct = FALSE;
2607
2608         size = MAX_PATH;
2609         r = MsiRecordGetString( hrec, 4, buf, &size );
2610         ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
2611         if( lstrcmp( buf, join_res_eighth[i].four ))
2612             data_correct = FALSE;
2613
2614         i++;
2615         MsiCloseHandle(hrec);
2616     }
2617     todo_wine ok( data_correct, "data returned in the wrong order\n");
2618
2619     ok( i == 6, "Expected 6 rows, got %d\n", i );
2620     ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
2621
2622     MsiViewClose(hview);
2623     MsiCloseHandle(hview);
2624
2625     MsiCloseHandle(hdb);
2626     DeleteFile(msifile);
2627 }
2628
2629 static void test_temporary_table(void)
2630 {
2631     MSICONDITION cond;
2632     MSIHANDLE hdb = 0, view = 0, rec;
2633     const char *query;
2634     UINT r;
2635     char buf[0x10];
2636     DWORD sz;
2637
2638     cond = MsiDatabaseIsTablePersistent(0, NULL);
2639     ok( cond == MSICONDITION_ERROR, "wrong return condition\n");
2640
2641     hdb = create_db();
2642     ok( hdb, "failed to create db\n");
2643
2644     cond = MsiDatabaseIsTablePersistent(hdb, NULL);
2645     ok( cond == MSICONDITION_ERROR, "wrong return condition\n");
2646
2647     todo_wine {
2648     cond = MsiDatabaseIsTablePersistent(hdb, "_Tables");
2649     ok( cond == MSICONDITION_NONE, "wrong return condition\n");
2650
2651     cond = MsiDatabaseIsTablePersistent(hdb, "_Columns");
2652     ok( cond == MSICONDITION_NONE, "wrong return condition\n");
2653
2654     cond = MsiDatabaseIsTablePersistent(hdb, "_Streams");
2655     ok( cond == MSICONDITION_NONE, "wrong return condition\n");
2656     }
2657
2658     query = "CREATE TABLE `P` ( `B` SHORT NOT NULL, `C` CHAR(255) PRIMARY KEY `C`)";
2659     r = run_query(hdb, 0, query);
2660     ok(r == ERROR_SUCCESS, "failed to add table\n");
2661
2662     cond = MsiDatabaseIsTablePersistent(hdb, "P");
2663     todo_wine ok( cond == MSICONDITION_TRUE, "wrong return condition\n");
2664
2665     query = "CREATE TABLE `P2` ( `B` SHORT NOT NULL, `C` CHAR(255) PRIMARY KEY `C`) HOLD";
2666     r = run_query(hdb, 0, query);
2667     ok(r == ERROR_SUCCESS, "failed to add table\n");
2668
2669     todo_wine {
2670     cond = MsiDatabaseIsTablePersistent(hdb, "P");
2671     ok( cond == MSICONDITION_TRUE, "wrong return condition\n");
2672     }
2673
2674     query = "CREATE TABLE `T` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`) HOLD";
2675     r = run_query(hdb, 0, query);
2676     ok(r == ERROR_SUCCESS, "failed to add table\n");
2677
2678     cond = MsiDatabaseIsTablePersistent(hdb, "T");
2679     ok( cond == MSICONDITION_FALSE, "wrong return condition\n");
2680
2681     query = "CREATE TABLE `T2` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`)";
2682     r = run_query(hdb, 0, query);
2683     ok(r == ERROR_SUCCESS, "failed to add table\n");
2684
2685     todo_wine {
2686     cond = MsiDatabaseIsTablePersistent(hdb, "T2");
2687     ok( cond == MSICONDITION_NONE, "wrong return condition\n");
2688     }
2689
2690     query = "CREATE TABLE `T3` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) PRIMARY KEY `C`)";
2691     r = run_query(hdb, 0, query);
2692     ok(r == ERROR_SUCCESS, "failed to add table\n");
2693
2694     todo_wine {
2695     cond = MsiDatabaseIsTablePersistent(hdb, "T3");
2696     ok( cond == MSICONDITION_TRUE, "wrong return condition\n");
2697
2698     query = "CREATE TABLE `T4` ( `B` SHORT NOT NULL, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`)";
2699     r = run_query(hdb, 0, query);
2700     ok(r == ERROR_FUNCTION_FAILED, "failed to add table\n");
2701
2702     cond = MsiDatabaseIsTablePersistent(hdb, "T4");
2703     ok( cond == MSICONDITION_NONE, "wrong return condition\n");
2704     }
2705
2706     query = "CREATE TABLE `T5` ( `B` SHORT NOT NULL TEMP, `C` CHAR(255) TEMP PRIMARY KEY `C`) HOLD";
2707     r = run_query(hdb, 0, query);
2708     ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to add table\n");
2709
2710     query = "select * from `T`";
2711     r = MsiDatabaseOpenView(hdb, query, &view);
2712     ok(r == ERROR_SUCCESS, "failed to query table\n");
2713     r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
2714     ok(r == ERROR_SUCCESS, "failed to get column info\n");
2715
2716     sz = sizeof buf;
2717     r = MsiRecordGetString(rec, 1, buf, &sz);
2718     ok(r == ERROR_SUCCESS, "failed to get string\n");
2719     todo_wine ok( 0 == strcmp("G255", buf), "wrong column type\n");
2720
2721     sz = sizeof buf;
2722     r = MsiRecordGetString(rec, 2, buf, &sz);
2723     ok(r == ERROR_SUCCESS, "failed to get string\n");
2724     todo_wine ok( 0 == strcmp("j2", buf), "wrong column type\n");
2725
2726     MsiCloseHandle( rec );
2727     MsiCloseHandle( view );
2728
2729     /* query the table data */
2730     rec = 0;
2731     r = do_query(hdb, "select * from `_Tables` where `Name` = 'T'", &rec);
2732     ok( r == ERROR_SUCCESS, "temporary table exists in _Tables\n");
2733     MsiCloseHandle( rec );
2734
2735     todo_wine {
2736     /* query the column data */
2737     rec = 0;
2738     r = do_query(hdb, "select * from `_Columns` where `Table` = 'T' AND `Name` = 'B'", &rec);
2739     ok( r == ERROR_NO_MORE_ITEMS, "temporary table exists in _Columns\n");
2740     if (rec) MsiCloseHandle( rec );
2741
2742     r = do_query(hdb, "select * from `_Columns` where `Table` = 'T' AND `Name` = 'C'", &rec);
2743     ok( r == ERROR_NO_MORE_ITEMS, "temporary table exists in _Columns\n");
2744     if (rec) MsiCloseHandle( rec );
2745     }
2746
2747     MsiCloseHandle( hdb );
2748
2749     DeleteFile(msifile);
2750 }
2751
2752 static void test_alter(void)
2753 {
2754     MSICONDITION cond;
2755     MSIHANDLE hdb = 0;
2756     const char *query;
2757     UINT r;
2758
2759     hdb = create_db();
2760     ok( hdb, "failed to create db\n");
2761
2762     query = "CREATE TABLE `T` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`) HOLD";
2763     r = run_query(hdb, 0, query);
2764     ok(r == ERROR_SUCCESS, "failed to add table\n");
2765
2766     cond = MsiDatabaseIsTablePersistent(hdb, "T");
2767     ok( cond == MSICONDITION_FALSE, "wrong return condition\n");
2768
2769     query = "ALTER TABLE `T` HOLD";
2770     r = run_query(hdb, 0, query);
2771     ok(r == ERROR_SUCCESS, "failed to hold table %d\n", r);
2772
2773     query = "ALTER TABLE `T` FREE";
2774     r = run_query(hdb, 0, query);
2775     ok(r == ERROR_SUCCESS, "failed to free table\n");
2776
2777     query = "ALTER TABLE `T` FREE";
2778     r = run_query(hdb, 0, query);
2779     ok(r == ERROR_SUCCESS, "failed to free table\n");
2780
2781     todo_wine {
2782     query = "ALTER TABLE `T` FREE";
2783     r = run_query(hdb, 0, query);
2784     ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to free table\n");
2785
2786     query = "ALTER TABLE `T` HOLD";
2787     r = run_query(hdb, 0, query);
2788     ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to hold table %d\n", r);
2789     }
2790
2791     MsiCloseHandle( hdb );
2792
2793     DeleteFile(msifile);
2794 }
2795
2796 static void test_integers(void)
2797 {
2798     MSIHANDLE hdb = 0, view = 0, rec = 0;
2799     DWORD count, i;
2800     const char *query;
2801     UINT r;
2802
2803     /* just MsiOpenDatabase should not create a file */
2804     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
2805     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
2806
2807     /* create a table */
2808     query = "CREATE TABLE `integers` ( "
2809             "`one` SHORT, `two` INT, `three` INTEGER, `four` LONG, "
2810             "`five` SHORT NOT NULL, `six` INT NOT NULL, "
2811             "`seven` INTEGER NOT NULL, `eight` LONG NOT NULL "
2812             "PRIMARY KEY `one`)";
2813     r = MsiDatabaseOpenView(hdb, query, &view);
2814     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
2815     r = MsiViewExecute(view, 0);
2816     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
2817     r = MsiViewClose(view);
2818     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
2819     r = MsiCloseHandle(view);
2820     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
2821
2822     query = "SELECT * FROM `integers`";
2823     r = MsiDatabaseOpenView(hdb, query, &view);
2824     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2825
2826     r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
2827     count = MsiRecordGetFieldCount(rec);
2828     ok(count == 8, "Expected 8, got %d\n", count);
2829     ok(check_record(rec, 1, "one"), "Expected one\n");
2830     ok(check_record(rec, 2, "two"), "Expected two\n");
2831     ok(check_record(rec, 3, "three"), "Expected three\n");
2832     ok(check_record(rec, 4, "four"), "Expected four\n");
2833     ok(check_record(rec, 5, "five"), "Expected five\n");
2834     ok(check_record(rec, 6, "six"), "Expected six\n");
2835     ok(check_record(rec, 7, "seven"), "Expected seven\n");
2836     ok(check_record(rec, 8, "eight"), "Expected eight\n");
2837     MsiCloseHandle(rec);
2838
2839     r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
2840     count = MsiRecordGetFieldCount(rec);
2841     ok(count == 8, "Expected 8, got %d\n", count);
2842     ok(check_record(rec, 1, "I2"), "Expected I2\n");
2843     ok(check_record(rec, 2, "I2"), "Expected I2\n");
2844     ok(check_record(rec, 3, "I2"), "Expected I2\n");
2845     ok(check_record(rec, 4, "I4"), "Expected I4\n");
2846     ok(check_record(rec, 5, "i2"), "Expected i2\n");
2847     ok(check_record(rec, 6, "i2"), "Expected i2\n");
2848     ok(check_record(rec, 7, "i2"), "Expected i2\n");
2849     ok(check_record(rec, 8, "i4"), "Expected i4\n");
2850     MsiCloseHandle(rec);
2851
2852     MsiViewClose(view);
2853     MsiCloseHandle(view);
2854
2855     /* insert values into it, NULL where NOT NULL is specified */
2856     query = "INSERT INTO `integers` ( `one`, `two`, `three`, `four`, `five`, `six`, `seven`, `eight` )"
2857         "VALUES('', '', '', '', '', '', '', '')";
2858     r = MsiDatabaseOpenView(hdb, query, &view);
2859     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2860     r = MsiViewExecute(view, 0);
2861     ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
2862
2863     MsiViewClose(view);
2864     MsiCloseHandle(view);
2865
2866     query = "SELECT * FROM `integers`";
2867     r = do_query(hdb, query, &rec);
2868     ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
2869
2870     r = MsiRecordGetFieldCount(rec);
2871     ok(r == -1, "record count wrong: %d\n", r);
2872
2873     MsiCloseHandle(rec);
2874
2875     /* insert legitimate values into it */
2876     query = "INSERT INTO `integers` ( `one`, `two`, `three`, `four`, `five`, `six`, `seven`, `eight` )"
2877         "VALUES('', '2', '', '4', '5', '6', '7', '8')";
2878     r = MsiDatabaseOpenView(hdb, query, &view);
2879     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2880     r = MsiViewExecute(view, 0);
2881     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2882
2883     query = "SELECT * FROM `integers`";
2884     r = do_query(hdb, query, &rec);
2885     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2886
2887     r = MsiRecordGetFieldCount(rec);
2888     ok(r == 8, "record count wrong: %d\n", r);
2889
2890     i = MsiRecordGetInteger(rec, 1);
2891     ok(i == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", i);
2892     i = MsiRecordGetInteger(rec, 3);
2893     ok(i == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", i);
2894     i = MsiRecordGetInteger(rec, 2);
2895     ok(i == 2, "Expected 2, got %d\n", i);
2896     i = MsiRecordGetInteger(rec, 4);
2897     ok(i == 4, "Expected 4, got %d\n", i);
2898     i = MsiRecordGetInteger(rec, 5);
2899     ok(i == 5, "Expected 5, got %d\n", i);
2900     i = MsiRecordGetInteger(rec, 6);
2901     ok(i == 6, "Expected 6, got %d\n", i);
2902     i = MsiRecordGetInteger(rec, 7);
2903     ok(i == 7, "Expected 7, got %d\n", i);
2904     i = MsiRecordGetInteger(rec, 8);
2905     ok(i == 8, "Expected 8, got %d\n", i);
2906
2907     MsiCloseHandle(rec);
2908     MsiViewClose(view);
2909     MsiCloseHandle(view);
2910
2911     r = MsiDatabaseCommit(hdb);
2912     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
2913
2914     r = MsiCloseHandle(hdb);
2915     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
2916
2917     r = DeleteFile(msifile);
2918     ok(r == TRUE, "file didn't exist after commit\n");
2919 }
2920
2921 static void test_update(void)
2922 {
2923     MSIHANDLE hdb = 0, view = 0, rec = 0;
2924     CHAR result[MAX_PATH];
2925     const char *query;
2926     DWORD size;
2927     UINT r;
2928
2929     /* just MsiOpenDatabase should not create a file */
2930     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
2931     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
2932
2933     /* create the Control table */
2934     query = "CREATE TABLE `Control` ( "
2935         "`Dialog_` CHAR(72) NOT NULL, `Control` CHAR(50) NOT NULL, `Type` SHORT NOT NULL, "
2936         "`X` SHORT NOT NULL, `Y` SHORT NOT NULL, `Width` SHORT NOT NULL, `Height` SHORT NOT NULL,"
2937         "`Attributes` LONG, `Property` CHAR(50), `Text` CHAR(0) LOCALIZABLE, "
2938         "`Control_Next` CHAR(50), `Help` CHAR(50) LOCALIZABLE PRIMARY KEY `Dialog_`, `Control`)";
2939     r = MsiDatabaseOpenView(hdb, query, &view);
2940     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
2941     r = MsiViewExecute(view, 0);
2942     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
2943     r = MsiViewClose(view);
2944     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
2945     r = MsiCloseHandle(view);
2946     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
2947
2948     /* add a control */
2949     query = "INSERT INTO `Control` ( "
2950         "`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, "
2951         "`Property`, `Text`, `Control_Next`, `Help` )"
2952         "VALUES('ErrorDialog', 'ErrorText', '1', '5', '5', '5', '5', '', '', '', '')";
2953     r = MsiDatabaseOpenView(hdb, query, &view);
2954     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2955     r = MsiViewExecute(view, 0);
2956     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2957     r = MsiViewClose(view);
2958     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
2959     r = MsiCloseHandle(view);
2960     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
2961
2962     /* add a second control */
2963     query = "INSERT INTO `Control` ( "
2964         "`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, "
2965         "`Property`, `Text`, `Control_Next`, `Help` )"
2966         "VALUES('ErrorDialog', 'Button', '1', '5', '5', '5', '5', '', '', '', '')";
2967     r = MsiDatabaseOpenView(hdb, query, &view);
2968     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2969     r = MsiViewExecute(view, 0);
2970     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2971     r = MsiViewClose(view);
2972     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
2973     r = MsiCloseHandle(view);
2974     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
2975
2976     /* add a third control */
2977     query = "INSERT INTO `Control` ( "
2978         "`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, "
2979         "`Property`, `Text`, `Control_Next`, `Help` )"
2980         "VALUES('AnotherDialog', 'ErrorText', '1', '5', '5', '5', '5', '', '', '', '')";
2981     r = MsiDatabaseOpenView(hdb, query, &view);
2982     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2983     r = MsiViewExecute(view, 0);
2984     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
2985     r = MsiViewClose(view);
2986     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
2987     r = MsiCloseHandle(view);
2988     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
2989
2990     /* bad table */
2991     query = "UPDATE `NotATable` SET `Text` = 'this is text' WHERE `Dialog_` = 'ErrorDialog'";
2992     r = MsiDatabaseOpenView(hdb, query, &view);
2993     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
2994
2995     /* bad set column */
2996     query = "UPDATE `Control` SET `NotAColumn` = 'this is text' WHERE `Dialog_` = 'ErrorDialog'";
2997     r = MsiDatabaseOpenView(hdb, query, &view);
2998     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
2999
3000     /* bad where condition */
3001     query = "UPDATE `Control` SET `Text` = 'this is text' WHERE `NotAColumn` = 'ErrorDialog'";
3002     r = MsiDatabaseOpenView(hdb, query, &view);
3003     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
3004
3005     /* just the dialog_ specified */
3006     query = "UPDATE `Control` SET `Text` = 'this is text' WHERE `Dialog_` = 'ErrorDialog'";
3007     r = MsiDatabaseOpenView(hdb, query, &view);
3008     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3009     r = MsiViewExecute(view, 0);
3010     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3011     r = MsiViewClose(view);
3012     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
3013     r = MsiCloseHandle(view);
3014     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3015
3016     /* check the modified text */
3017     query = "SELECT `Text` FROM `Control` WHERE `Control` = 'ErrorText'";
3018     r = MsiDatabaseOpenView(hdb, query, &view);
3019     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3020     r = MsiViewExecute(view, 0);
3021     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3022
3023     r = MsiViewFetch(view, &rec);
3024     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3025
3026     size = MAX_PATH;
3027     r = MsiRecordGetString(rec, 1, result, &size);
3028     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3029     ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result);
3030
3031     MsiCloseHandle(rec);
3032
3033     r = MsiViewFetch(view, &rec);
3034     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3035
3036     size = MAX_PATH;
3037     r = MsiRecordGetString(rec, 1, result, &size);
3038     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3039     ok(!lstrlen(result), "Expected an empty string, got %s\n", result);
3040
3041     MsiCloseHandle(rec);
3042
3043     r = MsiViewFetch(view, &rec);
3044     ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
3045
3046     r = MsiViewClose(view);
3047     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
3048     r = MsiCloseHandle(view);
3049     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3050
3051     /* dialog_ and control specified */
3052     query = "UPDATE `Control` SET `Text` = 'this is text' WHERE `Dialog_` = 'ErrorDialog' AND `Control` = 'ErrorText'";
3053     r = MsiDatabaseOpenView(hdb, query, &view);
3054     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3055     r = MsiViewExecute(view, 0);
3056     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3057     r = MsiViewClose(view);
3058     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
3059     r = MsiCloseHandle(view);
3060     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3061
3062     /* check the modified text */
3063     query = "SELECT `Text` FROM `Control` WHERE `Control` = 'ErrorText'";
3064     r = MsiDatabaseOpenView(hdb, query, &view);
3065     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3066     r = MsiViewExecute(view, 0);
3067     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3068
3069     r = MsiViewFetch(view, &rec);
3070     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3071
3072     size = MAX_PATH;
3073     r = MsiRecordGetString(rec, 1, result, &size);
3074     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3075     ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result);
3076
3077     MsiCloseHandle(rec);
3078
3079     r = MsiViewFetch(view, &rec);
3080     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3081
3082     size = MAX_PATH;
3083     r = MsiRecordGetString(rec, 1, result, &size);
3084     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3085     ok(!lstrlen(result), "Expected an empty string, got %s\n", result);
3086
3087     MsiCloseHandle(rec);
3088
3089     r = MsiViewFetch(view, &rec);
3090     ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
3091
3092     r = MsiViewClose(view);
3093     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
3094     r = MsiCloseHandle(view);
3095     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3096
3097     /* no where condition */
3098     query = "UPDATE `Control` SET `Text` = 'this is text'";
3099     r = MsiDatabaseOpenView(hdb, query, &view);
3100     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3101     r = MsiViewExecute(view, 0);
3102     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3103     r = MsiViewClose(view);
3104     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
3105     r = MsiCloseHandle(view);
3106     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3107
3108     /* check the modified text */
3109     query = "SELECT `Text` FROM `Control`";
3110     r = MsiDatabaseOpenView(hdb, query, &view);
3111     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3112     r = MsiViewExecute(view, 0);
3113     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3114
3115     r = MsiViewFetch(view, &rec);
3116     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3117
3118     size = MAX_PATH;
3119     r = MsiRecordGetString(rec, 1, result, &size);
3120     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3121     ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result);
3122
3123     MsiCloseHandle(rec);
3124
3125     r = MsiViewFetch(view, &rec);
3126     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3127
3128     size = MAX_PATH;
3129     r = MsiRecordGetString(rec, 1, result, &size);
3130     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3131     ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result);
3132
3133     MsiCloseHandle(rec);
3134
3135     r = MsiViewFetch(view, &rec);
3136     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3137
3138     size = MAX_PATH;
3139     r = MsiRecordGetString(rec, 1, result, &size);
3140     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3141     ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result);
3142
3143     MsiCloseHandle(rec);
3144
3145     r = MsiViewFetch(view, &rec);
3146     ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
3147
3148     r = MsiViewClose(view);
3149     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
3150     r = MsiCloseHandle(view);
3151     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3152     r = MsiDatabaseCommit(hdb);
3153     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
3154     r = MsiCloseHandle(hdb);
3155     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3156 }
3157
3158 static void test_special_tables(void)
3159 {
3160     const char *query;
3161     MSIHANDLE hdb = 0;
3162     UINT r;
3163
3164     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
3165     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
3166
3167     query = "CREATE TABLE `_Properties` ( "
3168         "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)";
3169     r = run_query(hdb, 0, query);
3170     ok(r == ERROR_SUCCESS, "failed to create table\n");
3171
3172     query = "CREATE TABLE `_Streams` ( "
3173         "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)";
3174     r = run_query(hdb, 0, query);
3175     todo_wine ok(r == ERROR_BAD_QUERY_SYNTAX, "created _Streams table\n");
3176
3177     query = "CREATE TABLE `_Tables` ( "
3178         "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)";
3179     r = run_query(hdb, 0, query);
3180     ok(r == ERROR_BAD_QUERY_SYNTAX, "created _Tables table\n");
3181
3182     query = "CREATE TABLE `_Columns` ( "
3183         "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)";
3184     r = run_query(hdb, 0, query);
3185     ok(r == ERROR_BAD_QUERY_SYNTAX, "created _Columns table\n");
3186
3187     r = MsiCloseHandle(hdb);
3188     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3189 }
3190
3191 static void test_select_markers(void)
3192 {
3193     MSIHANDLE hdb = 0, rec, view, res;
3194     LPCSTR query;
3195     UINT r;
3196     DWORD size;
3197     CHAR buf[MAX_PATH];
3198
3199     hdb = create_db();
3200     ok( hdb, "failed to create db\n");
3201
3202     r = run_query(hdb, 0,
3203             "CREATE TABLE `Table` (`One` CHAR(72), `Two` CHAR(72), `Three` SHORT PRIMARY KEY `One`, `Two`, `Three`)");
3204     ok(r == S_OK, "cannot create table: %d\n", r);
3205
3206     r = run_query(hdb, 0, "INSERT INTO `Table` "
3207             "( `One`, `Two`, `Three` ) VALUES ( 'apple', 'one', 1 )");
3208     ok(r == S_OK, "cannot add file to the Media table: %d\n", r);
3209
3210     r = run_query(hdb, 0, "INSERT INTO `Table` "
3211             "( `One`, `Two`, `Three` ) VALUES ( 'apple', 'two', 1 )");
3212     ok(r == S_OK, "cannot add file to the Media table: %d\n", r);
3213
3214     r = run_query(hdb, 0, "INSERT INTO `Table` "
3215             "( `One`, `Two`, `Three` ) VALUES ( 'apple', 'two', 2 )");
3216     ok(r == S_OK, "cannot add file to the Media table: %d\n", r);
3217
3218     r = run_query(hdb, 0, "INSERT INTO `Table` "
3219             "( `One`, `Two`, `Three` ) VALUES ( 'banana', 'three', 3 )");
3220     ok(r == S_OK, "cannot add file to the Media table: %d\n", r);
3221
3222     rec = MsiCreateRecord(2);
3223     MsiRecordSetString(rec, 1, "apple");
3224     MsiRecordSetString(rec, 2, "two");
3225
3226     query = "SELECT * FROM `Table` WHERE `One`=? AND `Two`=? ORDER BY `Three`";
3227     r = MsiDatabaseOpenView(hdb, query, &view);
3228     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3229
3230     r = MsiViewExecute(view, rec);
3231     todo_wine
3232     {
3233         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3234     }
3235
3236     r = MsiViewFetch(view, &res);
3237     todo_wine
3238     {
3239         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3240     }
3241
3242     size = MAX_PATH;
3243     r = MsiRecordGetString(res, 1, buf, &size);
3244     todo_wine
3245     {
3246         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3247         ok(!lstrcmp(buf, "apple"), "Expected apple, got %s\n", buf);
3248     }
3249
3250     size = MAX_PATH;
3251     r = MsiRecordGetString(res, 2, buf, &size);
3252     todo_wine
3253     {
3254         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3255         ok(!lstrcmp(buf, "two"), "Expected two, got %s\n", buf);
3256     }
3257
3258     r = MsiRecordGetInteger(res, 3);
3259     todo_wine
3260     {
3261         ok(r == 1, "Expected 1, got %d\n", r);
3262     }
3263
3264     MsiCloseHandle(res);
3265
3266     r = MsiViewFetch(view, &res);
3267     todo_wine
3268     {
3269         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3270     }
3271
3272     size = MAX_PATH;
3273     r = MsiRecordGetString(res, 1, buf, &size);
3274     todo_wine
3275     {
3276         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3277         ok(!lstrcmp(buf, "apple"), "Expected apple, got %s\n", buf);
3278     }
3279
3280     size = MAX_PATH;
3281     r = MsiRecordGetString(res, 2, buf, &size);
3282     todo_wine
3283     {
3284         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
3285         ok(!lstrcmp(buf, "two"), "Expected two, got %s\n", buf);
3286     }
3287
3288     r = MsiRecordGetInteger(res, 3);
3289     todo_wine
3290     {
3291         ok(r == 2, "Expected 2, got %d\n", r);
3292     }
3293
3294     MsiCloseHandle(res);
3295
3296     r = MsiViewFetch(view, &res);
3297     ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
3298
3299     r = MsiViewClose(view);
3300     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
3301     r = MsiCloseHandle(view);
3302     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3303     r = MsiCloseHandle(hdb);
3304     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
3305 }
3306
3307 START_TEST(db)
3308 {
3309     test_msidatabase();
3310     test_msiinsert();
3311     test_msidecomposedesc();
3312     test_msibadqueries();
3313     test_viewmodify();
3314     test_viewgetcolumninfo();
3315     test_getcolinfo();
3316     test_msiexport();
3317     test_longstrings();
3318     test_streamtable();
3319     test_where();
3320     test_msiimport();
3321     test_markers();
3322     test_handle_limit();
3323     test_try_transform();
3324     test_join();
3325     test_temporary_table();
3326     test_alter();
3327     test_integers();
3328     test_update();
3329     test_special_tables();
3330     test_select_markers();
3331 }