msi: Remove limit on number of handles.
[wine] / dlls / msi / tests / db.c
1 /*
2  * Copyright (C) 2005 Mike McCormack for CodeWeavers
3  *
4  * A test program for MSI database files.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #include <stdio.h>
21
22 #include <windows.h>
23 #include <msi.h>
24 #include <msiquery.h>
25
26 #include "wine/test.h"
27
28 static const char *msifile = "winetest.msi";
29
30 static void test_msidatabase(void)
31 {
32     MSIHANDLE hdb = 0;
33     UINT res;
34
35     DeleteFile(msifile);
36
37     /* create an empty database */
38     res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
39     ok( res == ERROR_SUCCESS , "Failed to create database\n" );
40
41     res = MsiDatabaseCommit( hdb );
42     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
43
44     res = MsiCloseHandle( hdb );
45     ok( res == ERROR_SUCCESS , "Failed to close database\n" );
46
47     res = DeleteFile( msifile );
48     ok( res == TRUE, "Failed to delete database\n" );
49 }
50
51 static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec)
52 {
53     MSIHANDLE hview = 0;
54     UINT r, ret;
55
56     /* open a select query */
57     r = MsiDatabaseOpenView(hdb, query, &hview);
58     if (r != ERROR_SUCCESS)
59         return r;
60     r = MsiViewExecute(hview, 0);
61     if (r != ERROR_SUCCESS)
62         return r;
63     ret = MsiViewFetch(hview, phrec);
64     r = MsiViewClose(hview);
65     if (r != ERROR_SUCCESS)
66         return r;
67     r = MsiCloseHandle(hview);
68     if (r != ERROR_SUCCESS)
69         return r;
70     return ret;
71 }
72
73 static void test_msiinsert(void)
74 {
75     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
76     UINT r;
77     const char *query;
78     char buf[80];
79     DWORD sz;
80
81     DeleteFile(msifile);
82
83     /* just MsiOpenDatabase should not create a file */
84     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
85     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
86
87     /* create a table */
88     query = "CREATE TABLE `phone` ( "
89             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
90             "PRIMARY KEY `id`)";
91     r = MsiDatabaseOpenView(hdb, query, &hview);
92     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
93     r = MsiViewExecute(hview, 0);
94     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
95     r = MsiViewClose(hview);
96     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
97     r = MsiCloseHandle(hview);
98     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
99
100     /* insert a value into it */
101     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
102         "VALUES('1', 'Abe', '8675309')";
103     r = MsiDatabaseOpenView(hdb, query, &hview);
104     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
105     r = MsiViewExecute(hview, 0);
106     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
107     r = MsiViewClose(hview);
108     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
109     r = MsiCloseHandle(hview);
110     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
111
112     query = "SELECT * FROM `phone` WHERE `id` = 1";
113     r = do_query(hdb, query, &hrec);
114     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
115
116     /* check the record contains what we put in it */
117     r = MsiRecordGetFieldCount(hrec);
118     ok(r == 3, "record count wrong\n");
119
120     todo_wine {
121     r = MsiRecordIsNull(hrec, 0);
122     ok(r == FALSE, "field 0 not null\n");
123     }
124
125     r = MsiRecordGetInteger(hrec, 1);
126     ok(r == 1, "field 1 contents wrong\n");
127     sz = sizeof buf;
128     r = MsiRecordGetString(hrec, 2, buf, &sz);
129     ok(r == ERROR_SUCCESS, "field 2 content fetch failed\n");
130     ok(!strcmp(buf,"Abe"), "field 2 content incorrect\n");
131     sz = sizeof buf;
132     r = MsiRecordGetString(hrec, 3, buf, &sz);
133     ok(r == ERROR_SUCCESS, "field 3 content fetch failed\n");
134     ok(!strcmp(buf,"8675309"), "field 3 content incorrect\n");
135
136     r = MsiCloseHandle(hrec);
137     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
138
139     /* open a select query */
140     hrec = 100;
141     query = "SELECT * FROM `phone` WHERE `id` >= 10";
142     r = do_query(hdb, query, &hrec);
143     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
144     ok(hrec == 0, "hrec should be null\n");
145
146     r = MsiCloseHandle(hrec);
147     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
148
149     query = "SELECT * FROM `phone` WHERE `id` < 0";
150     r = do_query(hdb, query, &hrec);
151     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
152
153     query = "SELECT * FROM `phone` WHERE `id` <= 0";
154     r = do_query(hdb, query, &hrec);
155     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
156
157     query = "SELECT * FROM `phone` WHERE `id` <> 1";
158     r = do_query(hdb, query, &hrec);
159     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
160
161     query = "SELECT * FROM `phone` WHERE `id` > 10";
162     r = do_query(hdb, query, &hrec);
163     ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
164
165     todo_wine {
166     /* now try a few bad INSERT xqueries */
167     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
168         "VALUES(?, ?)";
169     r = MsiDatabaseOpenView(hdb, query, &hview);
170     ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n");
171
172     if (r == ERROR_SUCCESS)
173         r = MsiCloseHandle(hview);
174     }
175
176     /* construct a record to insert */
177     hrec = MsiCreateRecord(4);
178     r = MsiRecordSetInteger(hrec, 1, 2);
179     ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n");
180     r = MsiRecordSetString(hrec, 2, "Adam");
181     ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
182     r = MsiRecordSetString(hrec, 3, "96905305");
183     ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
184
185     /* insert another value, using a record and wildcards */
186     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
187         "VALUES(?, ?, ?)";
188     r = MsiDatabaseOpenView(hdb, query, &hview);
189     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
190
191     if (r == ERROR_SUCCESS)
192     {
193         r = MsiViewExecute(hview, hrec);
194         ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
195         r = MsiViewClose(hview);
196         ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
197         r = MsiCloseHandle(hview);
198         ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
199     }
200     r = MsiCloseHandle(hrec);
201     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
202
203     r = MsiViewFetch(0, NULL);
204     ok(r == ERROR_INVALID_PARAMETER, "MsiViewFetch failed\n");
205
206     r = MsiDatabaseCommit(hdb);
207     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
208
209     r = MsiCloseHandle(hdb);
210     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
211
212     r = DeleteFile(msifile);
213     ok(r == TRUE, "file didn't exist after commit\n");
214 }
215
216 typedef UINT (WINAPI *fnMsiDecomposeDescriptorA)(LPCSTR, LPCSTR, LPSTR, LPSTR, DWORD *);
217 static fnMsiDecomposeDescriptorA pMsiDecomposeDescriptorA;
218
219 static void test_msidecomposedesc(void)
220 {
221     char prod[MAX_FEATURE_CHARS+1], comp[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1];
222     const char *desc;
223     UINT r;
224     DWORD len;
225     HMODULE hmod;
226
227     hmod = GetModuleHandle("msi.dll");
228     if (!hmod)
229         return;
230     pMsiDecomposeDescriptorA = (fnMsiDecomposeDescriptorA)
231         GetProcAddress(hmod, "MsiDecomposeDescriptorA");
232     if (!pMsiDecomposeDescriptorA)
233         return;
234
235     /* test a valid feature descriptor */
236     desc = "']gAVn-}f(ZXfeAR6.jiFollowTheWhiteRabbit>3w2x^IGfe?CxI5heAvk.";
237     len = 0;
238     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
239     ok(r == ERROR_SUCCESS, "returned an error\n");
240     ok(len == strlen(desc), "length was wrong\n");
241     ok(strcmp(prod,"{90110409-6000-11D3-8CFE-0150048383C9}")==0, "product wrong\n");
242     ok(strcmp(feature,"FollowTheWhiteRabbit")==0, "feature wrong\n");
243     ok(strcmp(comp,"{A7CD68DB-EF74-49C8-FBB2-A7C463B2AC24}")==0,"component wrong\n");
244
245     /* test an invalid feature descriptor with too many characters */
246     desc = "']gAVn-}f(ZXfeAR6.ji"
247            "ThisWillFailIfTheresMoreThanAGuidsChars>"
248            "3w2x^IGfe?CxI5heAvk.";
249     len = 0;
250     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
251     ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
252
253     /*
254      * Test a valid feature descriptor with the
255      * maximum number of characters and some trailing characters.
256      */
257     desc = "']gAVn-}f(ZXfeAR6.ji"
258            "ThisWillWorkIfTheresLTEThanAGuidsChars>"
259            "3w2x^IGfe?CxI5heAvk."
260            "extra";
261     len = 0;
262     r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
263     ok(r == ERROR_SUCCESS, "returned wrong error\n");
264     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
265
266     len = 0;
267     r = pMsiDecomposeDescriptorA(desc, prod, feature, NULL, &len);
268     ok(r == ERROR_SUCCESS, "returned wrong error\n");
269     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
270
271     len = 0;
272     r = pMsiDecomposeDescriptorA(desc, prod, NULL, NULL, &len);
273     ok(r == ERROR_SUCCESS, "returned wrong error\n");
274     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
275
276     len = 0;
277     r = pMsiDecomposeDescriptorA(desc, NULL, NULL, NULL, &len);
278     ok(r == ERROR_SUCCESS, "returned wrong error\n");
279     ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
280
281     len = 0;
282     r = pMsiDecomposeDescriptorA(NULL, NULL, NULL, NULL, &len);
283     ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
284     ok(len == 0, "length wrong\n");
285 }
286
287 static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
288 {
289     MSIHANDLE htab = 0;
290     UINT res;
291
292     res = MsiDatabaseOpenView( hdb, szQuery, &htab );
293     if(res == ERROR_SUCCESS )
294     {
295         UINT r;
296
297         r = MsiViewExecute( htab, hrec );
298         if(r != ERROR_SUCCESS )
299             res = r;
300
301         r = MsiViewClose( htab );
302         if(r != ERROR_SUCCESS )
303             res = r;
304
305         r = MsiCloseHandle( htab );
306         if(r != ERROR_SUCCESS )
307             res = r;
308     }
309     return res;
310 }
311
312 static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
313 {
314     return try_query_param( hdb, szQuery, 0 );
315 }
316
317 static UINT try_insert_query( MSIHANDLE hdb, LPCSTR szQuery )
318 {
319     MSIHANDLE hrec = 0;
320     UINT r;
321
322     hrec = MsiCreateRecord( 1 );
323     MsiRecordSetString( hrec, 1, "Hello");
324
325     r = try_query_param( hdb, szQuery, hrec );
326
327     MsiCloseHandle( hrec );
328     return r;
329 }
330
331 static void test_msibadqueries(void)
332 {
333     MSIHANDLE hdb = 0;
334     UINT r;
335
336     DeleteFile(msifile);
337
338     /* just MsiOpenDatabase should not create a file */
339     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
340     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
341
342     r = MsiDatabaseCommit( hdb );
343     ok(r == ERROR_SUCCESS , "Failed to commit database\n");
344
345     r = MsiCloseHandle( hdb );
346     ok(r == ERROR_SUCCESS , "Failed to close database\n");
347
348     /* open it readonly */
349     r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb );
350     ok(r == ERROR_SUCCESS , "Failed to open database r/o\n");
351
352     /* add a table to it */
353     r = try_query( hdb, "select * from _Tables");
354     ok(r == ERROR_SUCCESS , "query 1 failed\n");
355
356     r = MsiCloseHandle( hdb );
357     ok(r == ERROR_SUCCESS , "Failed to close database r/o\n");
358
359     /* open it read/write */
360     r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb );
361     ok(r == ERROR_SUCCESS , "Failed to open database r/w\n");
362
363     /* a bunch of test queries that fail with the native MSI */
364
365     r = try_query( hdb, "CREATE TABLE");
366     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2a return code\n");
367
368     r = try_query( hdb, "CREATE TABLE `a`");
369     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2b return code\n");
370
371     r = try_query( hdb, "CREATE TABLE `a` ()");
372     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2c return code\n");
373
374     r = try_query( hdb, "CREATE TABLE `a` (`b`)");
375     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2d return code\n");
376
377     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) )");
378     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2e return code\n");
379
380     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL)");
381     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2f return code\n");
382
383     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY)");
384     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2g return code\n");
385
386     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
387     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2h return code\n");
388
389     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
390     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2i return code\n");
391
392     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY 'b')");
393     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2j return code\n");
394
395     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
396     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2k return code\n");
397
398     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
399     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2l return code\n");
400
401     r = try_query( hdb, "CREATE TABLE `a` (`b` CHA(72) NOT NULL PRIMARY KEY `b`)");
402     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2m return code\n");
403
404     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(-1) NOT NULL PRIMARY KEY `b`)");
405     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2n return code\n");
406
407     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(720) NOT NULL PRIMARY KEY `b`)");
408     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2o return code\n");
409
410     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL KEY `b`)");
411     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
412
413     r = try_query( hdb, "CREATE TABLE `a` (`` CHAR(72) NOT NULL PRIMARY KEY `b`)");
414     ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
415
416     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
417     ok(r == ERROR_SUCCESS , "valid query 2z failed\n");
418
419     r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
420     ok(r == ERROR_BAD_QUERY_SYNTAX , "created same table again\n");
421
422     r = try_query( hdb, "CREATE TABLE `aa` (`b` CHAR(72) NOT NULL, `c` "
423           "CHAR(72), `d` CHAR(255) NOT NULL LOCALIZABLE PRIMARY KEY `b`)");
424     ok(r == ERROR_SUCCESS , "query 4 failed\n");
425
426     r = MsiDatabaseCommit( hdb );
427     ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
428
429     r = try_query( hdb, "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL "
430                           "PRIMARY KEY `foo`)");
431     ok(r == ERROR_SUCCESS , "query 4 failed\n");
432
433     r = try_insert_query( hdb, "insert into a  ( `b` ) VALUES ( ? )");
434     ok(r == ERROR_SUCCESS , "failed to insert record in db\n");
435
436     r = MsiDatabaseCommit( hdb );
437     ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
438
439     r = try_query( hdb, "CREATE TABLE `boo` (`foo` CHAR(72) NOT NULL "
440                           "PRIMARY KEY `ba`)");
441     ok(r != ERROR_SUCCESS , "query 5 succeeded\n");
442
443     r = try_query( hdb,"CREATE TABLE `bee` (`foo` CHAR(72) NOT NULL )");
444     ok(r != ERROR_SUCCESS , "query 6 succeeded\n");
445
446     r = try_query( hdb, "CREATE TABLE `temp` (`t` CHAR(72) NOT NULL "
447                           "PRIMARY KEY `t`)");
448     ok(r == ERROR_SUCCESS , "query 7 failed\n");
449
450     r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)");
451     ok(r == ERROR_SUCCESS , "query 8 failed\n");
452
453     r = MsiCloseHandle( hdb );
454     ok(r == ERROR_SUCCESS , "Failed to close database transact\n");
455
456     r = DeleteFile( msifile );
457     ok(r == TRUE, "file didn't exist after commit\n");
458 }
459
460 static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query )
461 {
462     MSIHANDLE hview = 0;
463     UINT r;
464
465     r = MsiDatabaseOpenView(hdb, query, &hview);
466     if( r != ERROR_SUCCESS )
467         return r;
468
469     r = MsiViewExecute(hview, hrec);
470     if( r == ERROR_SUCCESS )
471         r = MsiViewClose(hview);
472     MsiCloseHandle(hview);
473     return r;
474 }
475
476 static void test_viewmodify(void)
477 {
478     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
479     UINT r;
480     const char *query;
481     char buffer[0x100];
482     DWORD sz;
483
484     DeleteFile(msifile);
485
486     /* just MsiOpenDatabase should not create a file */
487     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
488     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
489
490     query = "CREATE TABLE `phone` ( "
491             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
492             "PRIMARY KEY `id`)";
493     r = run_query( hdb, 0, query );
494     ok(r == ERROR_SUCCESS, "query failed\n");
495
496     /* check what the error function reports without doing anything */
497     sz = 0;
498     /* passing NULL as the 3rd param make function to crash on older platforms */
499     r = MsiViewGetError( 0, NULL, &sz );
500     ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
501
502     /* open a view */
503     query = "SELECT * FROM `phone`";
504     r = MsiDatabaseOpenView(hdb, query, &hview);
505     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
506
507     /* see what happens with a good hview and bad args */
508     r = MsiViewGetError( hview, NULL, NULL );
509     ok(r == MSIDBERROR_INVALIDARG || r == MSIDBERROR_NOERROR,
510        "MsiViewGetError returns %u (expected -3)\n", r);
511     r = MsiViewGetError( hview, buffer, NULL );
512     ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
513
514     /* see what happens with a zero length buffer */
515     sz = 0;
516     buffer[0] = 'x';
517     r = MsiViewGetError( hview, buffer, &sz );
518     ok(r == MSIDBERROR_MOREDATA, "MsiViewGetError return\n");
519     ok(buffer[0] == 'x', "buffer cleared\n");
520     ok(sz == 0, "size not zero\n");
521
522     /* ok this one is strange */
523     sz = 0;
524     r = MsiViewGetError( hview, NULL, &sz );
525     ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
526     ok(sz == 0, "size not zero\n");
527
528     /* see if it really has an error */
529     sz = sizeof buffer;
530     buffer[0] = 'x';
531     r = MsiViewGetError( hview, buffer, &sz );
532     ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
533     ok(buffer[0] == 0, "buffer not cleared\n");
534     ok(sz == 0, "size not zero\n");
535
536     r = MsiViewExecute(hview, 0);
537     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
538
539     /* try some invalid records */
540     r = MsiViewModify(hview, MSIMODIFY_INSERT, 0 );
541     ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
542     r = MsiViewModify(hview, -1, 0 );
543     ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
544
545     /* try an small record */
546     hrec = MsiCreateRecord(1);
547     r = MsiViewModify(hview, -1, hrec );
548     ok(r == ERROR_INVALID_DATA, "MsiViewModify failed\n");
549
550     r = MsiCloseHandle(hrec);
551     ok(r == ERROR_SUCCESS, "failed to close record\n");
552
553     /* insert a valid record */
554     hrec = MsiCreateRecord(3);
555
556     r = MsiRecordSetInteger(hrec, 2, 1);
557     ok(r == ERROR_SUCCESS, "failed to set integer\n");
558     r = MsiRecordSetString(hrec, 2, "bob");
559     ok(r == ERROR_SUCCESS, "failed to set integer\n");
560     r = MsiRecordSetString(hrec, 3, "7654321");
561     ok(r == ERROR_SUCCESS, "failed to set integer\n");
562
563     r = MsiViewExecute(hview, 0);
564     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
565     r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
566     ok(r == ERROR_SUCCESS, "MsiViewModify failed\n");
567
568     /* insert the same thing again */
569     r = MsiViewExecute(hview, 0);
570     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
571
572     /* should fail ... */
573     todo_wine {
574     r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
575     ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n");
576     }
577
578     r = MsiCloseHandle(hrec);
579     ok(r == ERROR_SUCCESS, "failed to close record\n");
580
581     r = MsiViewClose(hview);
582     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
583     r = MsiCloseHandle(hview);
584     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
585
586     r = MsiCloseHandle( hdb );
587     ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
588 }
589
590 static MSIHANDLE create_db(void)
591 {
592     MSIHANDLE hdb = 0;
593     UINT res;
594
595     DeleteFile(msifile);
596
597     /* create an empty database */
598     res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
599     ok( res == ERROR_SUCCESS , "Failed to create database\n" );
600     if( res != ERROR_SUCCESS )
601         return hdb;
602
603     res = MsiDatabaseCommit( hdb );
604     ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
605
606     return hdb;
607 }
608
609 static void test_getcolinfo(void)
610 {
611     MSIHANDLE hdb, hview = 0, rec = 0;
612     UINT r;
613     DWORD sz;
614     char buffer[0x20];
615
616     /* create an empty db */
617     hdb = create_db();
618     ok( hdb, "failed to create db\n");
619
620     /* tables should be present */
621     r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview);
622     ok( r == ERROR_SUCCESS, "failed to open query\n");
623
624     r = MsiViewExecute(hview, 0);
625     ok( r == ERROR_SUCCESS, "failed to execute query\n");
626
627     /* check that NAMES works */
628     rec = 0;
629     r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec );
630     ok( r == ERROR_SUCCESS, "failed to get names\n");
631     sz = sizeof buffer;
632     r = MsiRecordGetString(rec, 1, buffer, &sz );
633     ok( r == ERROR_SUCCESS, "failed to get string\n");
634     ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n");
635     r = MsiCloseHandle( rec );
636     ok( r == ERROR_SUCCESS, "failed to close record handle\n");
637
638     /* check that TYPES works */
639     rec = 0;
640     r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec );
641     ok( r == ERROR_SUCCESS, "failed to get names\n");
642     sz = sizeof buffer;
643     r = MsiRecordGetString(rec, 1, buffer, &sz );
644     ok( r == ERROR_SUCCESS, "failed to get string\n");
645     ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n");
646     r = MsiCloseHandle( rec );
647     ok( r == ERROR_SUCCESS, "failed to close record handle\n");
648
649     /* check that invalid values fail */
650     rec = 0;
651     r = MsiViewGetColumnInfo( hview, 100, &rec );
652     ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
653     ok( rec == 0, "returned a record\n");
654
655     r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL );
656     ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
657
658     r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec );
659     ok( r == ERROR_INVALID_HANDLE, "wrong error code\n");
660
661     r = MsiViewClose(hview);
662     ok( r == ERROR_SUCCESS, "failed to close view\n");
663     r = MsiCloseHandle(hview);
664     ok( r == ERROR_SUCCESS, "failed to close view handle\n");
665     r = MsiCloseHandle(hdb);
666     ok( r == ERROR_SUCCESS, "failed to close database\n");
667 }
668
669 static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type)
670 {
671     MSIHANDLE hview = 0, rec = 0;
672     UINT r;
673
674     r = MsiDatabaseOpenView(hdb, query, &hview);
675     if( r != ERROR_SUCCESS )
676         return r;
677
678     r = MsiViewExecute(hview, 0);
679     if( r == ERROR_SUCCESS )
680     {
681         MsiViewGetColumnInfo( hview, type, &rec );
682         MsiViewClose(hview);
683     }
684     MsiCloseHandle(hview);
685     return rec;
686 }
687
688 static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field)
689 {
690     MSIHANDLE hview = 0, rec = 0;
691     UINT r, type = 0;
692     char query[0x100];
693
694     sprintf(query, "select * from `_Columns` where  `Table` = '%s'", table );
695
696     r = MsiDatabaseOpenView(hdb, query, &hview);
697     if( r != ERROR_SUCCESS )
698         return r;
699
700     r = MsiViewExecute(hview, 0);
701     if( r == ERROR_SUCCESS )
702     {
703         while (1)
704         {
705             r = MsiViewFetch( hview, &rec );
706             if( r != ERROR_SUCCESS)
707                 break;
708             r = MsiRecordGetInteger( rec, 2 );
709             if (r == field)
710                 type = MsiRecordGetInteger( rec, 4 );
711             MsiCloseHandle( rec );
712         }
713
714         MsiViewClose(hview);
715     }
716     MsiCloseHandle(hview);
717     return type;
718 }
719
720 static BOOL check_record( MSIHANDLE rec, UINT field, LPCSTR val )
721 {
722     CHAR buffer[0x20];
723     UINT r;
724     DWORD sz;
725
726     sz = sizeof buffer;
727     r = MsiRecordGetString( rec, field, buffer, &sz );
728     return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
729 }
730
731 static void test_viewgetcolumninfo(void)
732 {
733     MSIHANDLE hdb = 0, rec;
734     UINT r;
735
736     hdb = create_db();
737     ok( hdb, "failed to create db\n");
738
739     r = run_query( hdb, 0,
740             "CREATE TABLE `Properties` "
741             "( `Property` CHAR(255), `Value` CHAR(1)  PRIMARY KEY `Property`)" );
742     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
743
744     /* check the column types */
745     rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES );
746     ok( rec, "failed to get column info record\n" );
747
748     ok( check_record( rec, 1, "S255"), "wrong record type\n");
749     ok( check_record( rec, 2, "S1"), "wrong record type\n");
750
751     MsiCloseHandle( rec );
752
753     /* check the type in _Columns */
754     ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n");
755     ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n");
756
757     /* now try the names */
758     rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES );
759     ok( rec, "failed to get column info record\n" );
760
761     ok( check_record( rec, 1, "Property"), "wrong record type\n");
762     ok( check_record( rec, 2, "Value"), "wrong record type\n");
763
764     MsiCloseHandle( rec );
765
766     r = run_query( hdb, 0,
767             "CREATE TABLE `Binary` "
768             "( `Name` CHAR(255), `Data` OBJECT  PRIMARY KEY `Name`)" );
769     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
770
771     /* check the column types */
772     rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES );
773     ok( rec, "failed to get column info record\n" );
774
775     ok( check_record( rec, 1, "S255"), "wrong record type\n");
776     ok( check_record( rec, 2, "V0"), "wrong record type\n");
777
778     MsiCloseHandle( rec );
779
780     /* check the type in _Columns */
781     ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n");
782     ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n");
783
784     /* now try the names */
785     rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES );
786     ok( rec, "failed to get column info record\n" );
787
788     ok( check_record( rec, 1, "Name"), "wrong record type\n");
789     ok( check_record( rec, 2, "Data"), "wrong record type\n");
790     MsiCloseHandle( rec );
791
792     r = run_query( hdb, 0,
793             "CREATE TABLE `UIText` "
794             "( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" );
795     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
796
797     ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n");
798     ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n");
799
800     rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES );
801     ok( rec, "failed to get column info record\n" );
802     ok( check_record( rec, 1, "Key"), "wrong record type\n");
803     ok( check_record( rec, 2, "Text"), "wrong record type\n");
804     MsiCloseHandle( rec );
805
806     rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES );
807     ok( rec, "failed to get column info record\n" );
808     ok( check_record( rec, 1, "s72"), "wrong record type\n");
809     ok( check_record( rec, 2, "L255"), "wrong record type\n");
810     MsiCloseHandle( rec );
811
812     MsiCloseHandle( hdb );
813 }
814
815 static void test_msiexport(void)
816 {
817     MSIHANDLE hdb = 0, hview = 0;
818     UINT r;
819     const char *query;
820     char path[MAX_PATH];
821     const char file[] = "phone.txt";
822     HANDLE handle;
823     char buffer[0x100];
824     DWORD length;
825     const char expected[] =
826         "id\tname\tnumber\r\n"
827         "I2\tS32\tS32\r\n"
828         "phone\tid\r\n"
829         "1\tAbe\t8675309\r\n";
830
831     DeleteFile(msifile);
832
833     /* just MsiOpenDatabase should not create a file */
834     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
835     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
836
837     /* create a table */
838     query = "CREATE TABLE `phone` ( "
839             "`id` INT, `name` CHAR(32), `number` CHAR(32) "
840             "PRIMARY KEY `id`)";
841     r = MsiDatabaseOpenView(hdb, query, &hview);
842     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
843     r = MsiViewExecute(hview, 0);
844     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
845     r = MsiViewClose(hview);
846     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
847     r = MsiCloseHandle(hview);
848     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
849
850     /* insert a value into it */
851     query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
852         "VALUES('1', 'Abe', '8675309')";
853     r = MsiDatabaseOpenView(hdb, query, &hview);
854     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
855     r = MsiViewExecute(hview, 0);
856     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
857     r = MsiViewClose(hview);
858     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
859     r = MsiCloseHandle(hview);
860     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
861
862     GetCurrentDirectory(MAX_PATH, path);
863
864     todo_wine {
865     r = MsiDatabaseExport(hdb, "phone", path, file);
866     ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n");
867
868     MsiCloseHandle(hdb);
869
870     lstrcat(path, "\\");
871     lstrcat(path, file);
872
873     /* check the data that was written */
874     length = 0;
875     memset(buffer, 0, sizeof buffer);
876     handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
877     if (handle != INVALID_HANDLE_VALUE)
878     {
879         ReadFile(handle, buffer, sizeof buffer, &length, NULL);
880         CloseHandle(handle);
881         DeleteFile(path);
882     }
883     else
884         ok(0, "failed to open file %s\n", path);
885
886     ok( length == strlen(expected), "length of data wrong\n");
887     ok( !lstrcmp(buffer, expected), "data doesn't match\n");
888     }
889     DeleteFile(msifile);
890 }
891
892 static void test_longstrings(void)
893 {
894     const char insert_query[] = 
895         "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')";
896     char *str;
897     MSIHANDLE hdb = 0, hview = 0, hrec = 0;
898     DWORD len;
899     UINT r;
900     const DWORD STRING_LENGTH = 0x10005;
901
902     DeleteFile(msifile);
903     /* just MsiOpenDatabase should not create a file */
904     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
905     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
906
907     /* create a table */
908     r = try_query( hdb, 
909         "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)");
910     ok(r == ERROR_SUCCESS, "query failed\n");
911
912     /* try a insert a very long string */
913     str = HeapAlloc(GetProcessHeap(), 0, STRING_LENGTH+sizeof insert_query);
914     len = strchr(insert_query, 'Z') - insert_query;
915     strcpy(str, insert_query);
916     memset(str+len, 'Z', STRING_LENGTH);
917     strcpy(str+len+STRING_LENGTH, insert_query+len+1);
918     r = try_query( hdb, str );
919     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
920
921     HeapFree(GetProcessHeap(), 0, str);
922
923     MsiDatabaseCommit(hdb);
924     ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
925     MsiCloseHandle(hdb);
926
927     r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb);
928     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
929
930     r = MsiDatabaseOpenView(hdb, "select * from `strings` where `id` = 1", &hview);
931     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
932
933     r = MsiViewExecute(hview, 0);
934     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
935
936     r = MsiViewFetch(hview, &hrec);
937     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
938
939     MsiCloseHandle(hview);
940
941     r = MsiRecordGetString(hrec, 2, NULL, &len);
942     ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
943     ok(len == STRING_LENGTH, "string length wrong\n");
944
945     MsiCloseHandle(hrec);
946     MsiCloseHandle(hdb);
947     DeleteFile(msifile);
948 }
949  
950 static void test_streamtable(void)
951 {
952     MSIHANDLE hdb = 0, rec;
953     UINT r;
954
955     hdb = create_db();
956     ok( hdb, "failed to create db\n");
957
958     r = run_query( hdb, 0,
959             "CREATE TABLE `Properties` "
960             "( `Property` CHAR(255), `Value` CHAR(1)  PRIMARY KEY `Property`)" );
961     ok( r == ERROR_SUCCESS , "Failed to create table\n" );
962
963     /* check the column types */
964     rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES );
965     ok( rec, "failed to get column info record\n" );
966
967     todo_wine {
968     ok( check_record( rec, 1, "s62"), "wrong record type\n");
969     ok( check_record( rec, 2, "V0"), "wrong record type\n");
970     }
971
972     MsiCloseHandle( rec );
973
974     /* now try the names */
975     rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES );
976     ok( rec, "failed to get column info record\n" );
977
978     todo_wine {
979     ok( check_record( rec, 1, "Name"), "wrong record type\n");
980     ok( check_record( rec, 2, "Data"), "wrong record type\n");
981     }
982
983     MsiCloseHandle( rec );
984     MsiCloseHandle( hdb );
985     DeleteFile(msifile);
986 }
987
988 static void test_where(void)
989 {
990     MSIHANDLE hdb = 0, rec;
991     LPCSTR query;
992     UINT r;
993
994     hdb = create_db();
995     ok( hdb, "failed to create db\n");
996
997     r = run_query( hdb, 0,
998             "CREATE TABLE `Media` ("
999             "`DiskId` SHORT NOT NULL, "
1000             "`LastSequence` LONG, "
1001             "`DiskPrompt` CHAR(64) LOCALIZABLE, "
1002             "`Cabinet` CHAR(255), "
1003             "`VolumeLabel` CHAR(32), "
1004             "`Source` CHAR(72) "
1005             "PRIMARY KEY `DiskId`)" );
1006     ok( r == S_OK, "cannot create Media table: %d\n", r );
1007
1008     r = run_query( hdb, 0, "INSERT INTO `Media` "
1009             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1010             "VALUES ( 1, 0, '', 'zero.cab', '', '' )" );
1011     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1012
1013     r = run_query( hdb, 0, "INSERT INTO `Media` "
1014             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1015             "VALUES ( 2, 1, '', 'one.cab', '', '' )" );
1016     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1017
1018     r = run_query( hdb, 0, "INSERT INTO `Media` "
1019             "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1020             "VALUES ( 3, 2, '', 'two.cab', '', '' )" );
1021     ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1022
1023     query = "SELECT * FROM `Media`";
1024     r = do_query(hdb, query, &rec);
1025     ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1026     ok( check_record( rec, 4, "zero.cab"), "wrong cabinet\n");
1027     MsiCloseHandle( rec );
1028
1029     query = "SELECT * FROM `Media` WHERE `LastSequence` >= 1";
1030     r = do_query(hdb, query, &rec);
1031     ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1032     ok( check_record( rec, 4, "one.cab"), "wrong cabinet\n");
1033
1034     r = MsiRecordGetInteger(rec, 1);
1035     ok( 2 == r, "field wrong\n");
1036     r = MsiRecordGetInteger(rec, 2);
1037     ok( 1 == r, "field wrong\n");
1038
1039     MsiCloseHandle( rec );
1040
1041     MsiCloseHandle( hdb );
1042     DeleteFile(msifile);
1043 }
1044
1045 static CHAR CURR_DIR[MAX_PATH];
1046
1047 static const CHAR test_data[] = "FirstPrimaryColumn\tSecondPrimaryColumn\tShortInt\tShortIntNullable\tLongInt\tLongIntNullable\tString\tLocalizableString\tLocalizableStringNullable\n"
1048                                 "s255\ti2\ti2\tI2\ti4\tI4\tS255\tS0\ts0\n"
1049                                 "TestTable\tFirstPrimaryColumn\n"
1050                                 "stringage\t5\t2\t\t2147483640\t-2147483640\tanother string\tlocalizable\tduh\n";
1051
1052 static void write_file(const CHAR *filename, const char *data, int data_size)
1053 {
1054     DWORD size;
1055
1056     HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
1057                            CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1058
1059     WriteFile(hf, data, data_size, &size, NULL);
1060     CloseHandle(hf);
1061 }
1062
1063 static UINT add_table_to_db(MSIHANDLE hdb, LPCSTR table_data)
1064 {
1065     UINT r;
1066
1067     write_file("temp_file", table_data, (lstrlen(table_data) - 1) * sizeof(char));
1068     r = MsiDatabaseImportA(hdb, CURR_DIR, "temp_file");
1069     DeleteFileA("temp_file");
1070
1071     return r;
1072 }
1073
1074 static void test_msiimport(void)
1075 {
1076     MSIHANDLE hdb, view, rec;
1077     LPCSTR query;
1078     UINT r, count;
1079     signed int i;
1080
1081     GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
1082
1083     r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb);
1084     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1085
1086     r = add_table_to_db(hdb, test_data);
1087     todo_wine
1088     {
1089         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1090     }
1091
1092     query = "SELECT * FROM `TestTable`";
1093     r = MsiDatabaseOpenView(hdb, query, &view);
1094     todo_wine
1095     {
1096         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1097     }
1098
1099     r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1100     count = MsiRecordGetFieldCount(rec);
1101     todo_wine
1102     {
1103         ok(count == 9, "Expected 9, got %d\n", count);
1104         ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n");
1105         ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n");
1106         ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n");
1107         ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n");
1108         ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n");
1109         ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n");
1110         ok(check_record(rec, 7, "String"), "Expected String\n");
1111         ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n");
1112         ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n");
1113     }
1114
1115     r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1116     count = MsiRecordGetFieldCount(rec);
1117     todo_wine
1118     {
1119         ok(count == 9, "Expected 9, got %d\n", count);
1120         ok(check_record(rec, 1, "s255"), "Expected s255\n");
1121         ok(check_record(rec, 2, "i2"), "Expected i2\n");
1122         ok(check_record(rec, 3, "i2"), "Expected i2\n");
1123         ok(check_record(rec, 4, "I2"), "Expected I2\n");
1124         ok(check_record(rec, 5, "i4"), "Expected i4\n");
1125         ok(check_record(rec, 6, "I4"), "Expected I4\n");
1126         ok(check_record(rec, 7, "S255"), "Expected S255\n");
1127         ok(check_record(rec, 8, "S0"), "Expected S0\n");
1128         ok(check_record(rec, 9, "s0"), "Expected s0\n");
1129     }
1130
1131     query = "SELECT * FROM `TestTable`";
1132     r = do_query(hdb, query, &rec);
1133     todo_wine
1134     {
1135         ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1136     }
1137
1138     todo_wine
1139     {
1140         ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n");
1141         ok(check_record(rec, 7, "another string"), "Expected 'another string'\n");
1142         ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n");
1143         ok(check_record(rec, 9, "duh"), "Expected 'duh'\n");
1144     }
1145
1146     i = MsiRecordGetInteger(rec, 2);
1147     todo_wine
1148     {
1149         ok(i == 5, "Expected 5, got %d\n", i);
1150     }
1151
1152     i = MsiRecordGetInteger(rec, 3);
1153     todo_wine
1154     {
1155         ok(i == 2, "Expected 2, got %d\n", i);
1156     }
1157
1158     i = MsiRecordGetInteger(rec, 4);
1159     ok(i == 0x80000000, "Expected 0x80000000, got %d\n", i);
1160
1161     i = MsiRecordGetInteger(rec, 5);
1162     todo_wine
1163     {
1164         ok(i == 2147483640, "Expected 2147483640, got %d\n", i);
1165     }
1166
1167     i = MsiRecordGetInteger(rec, 6);
1168     todo_wine
1169     {
1170         ok(i == -2147483640, "Expected -2147483640, got %d\n", i);
1171     }
1172
1173     MsiCloseHandle(rec);
1174     MsiCloseHandle(view);
1175     MsiCloseHandle(hdb);
1176     DeleteFileA(msifile);
1177 }
1178
1179 static void test_markers(void)
1180 {
1181     MSIHANDLE hdb, rec;
1182     LPCSTR query;
1183     UINT r;
1184
1185     hdb = create_db();
1186     ok( hdb, "failed to create db\n");
1187
1188     rec = MsiCreateRecord(3);
1189     MsiRecordSetString(rec, 1, "Table");
1190     MsiRecordSetString(rec, 2, "Apples");
1191     MsiRecordSetString(rec, 3, "Oranges");
1192
1193     /* try a legit create */
1194     query = "CREATE TABLE `Table` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1195     r = run_query(hdb, 0, query);
1196     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1197     MsiCloseHandle(rec);
1198
1199     /* try table name as marker */
1200     rec = MsiCreateRecord(1);
1201     MsiRecordSetString(rec, 1, "Fable");
1202     query = "CREATE TABLE `?` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1203     r = run_query(hdb, rec, query);
1204     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1205
1206     /* try table name as marker without backticks */
1207     MsiRecordSetString(rec, 1, "Mable");
1208     query = "CREATE TABLE ? ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1209     r = run_query(hdb, rec, query);
1210     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1211
1212     /* try one column name as marker */
1213     MsiRecordSetString(rec, 1, "One");
1214     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1215     r = run_query(hdb, rec, query);
1216     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1217
1218     /* try column names as markers */
1219     MsiCloseHandle(rec);
1220     rec = MsiCreateRecord(2);
1221     MsiRecordSetString(rec, 1, "One");
1222     MsiRecordSetString(rec, 2, "Two");
1223     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `One`)";
1224     r = run_query(hdb, rec, query);
1225     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1226
1227     /* try names with backticks */
1228     MsiCloseHandle(rec);
1229     rec = MsiCreateRecord(3);
1230     MsiRecordSetString(rec, 1, "One");
1231     MsiRecordSetString(rec, 2, "Two");
1232     MsiRecordSetString(rec, 3, "One");
1233     query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1234     r = run_query(hdb, rec, query);
1235     todo_wine
1236     {
1237         ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1238     }
1239
1240     /* try names with backticks, minus definitions */
1241     query = "CREATE TABLE `Mable` ( `?`, `?` PRIMARY KEY `?`)";
1242     r = run_query(hdb, rec, query);
1243     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1244
1245     /* try names without backticks */
1246     query = "CREATE TABLE `Mable` ( ? SHORT NOT NULL, ? CHAR(255) PRIMARY KEY ?)";
1247     r = run_query(hdb, rec, query);
1248     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1249
1250     /* try one long marker */
1251     MsiCloseHandle(rec);
1252     rec = MsiCreateRecord(1);
1253     MsiRecordSetString(rec, 1, "`One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`");
1254     query = "CREATE TABLE `Mable` ( ? )";
1255     r = run_query(hdb, rec, query);
1256     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1257
1258     /* try all names as markers */
1259     MsiCloseHandle(rec);
1260     rec = MsiCreateRecord(4);
1261     MsiRecordSetString(rec, 1, "Mable");
1262     MsiRecordSetString(rec, 2, "One");
1263     MsiRecordSetString(rec, 3, "Two");
1264     MsiRecordSetString(rec, 4, "One");
1265     query = "CREATE TABLE `?` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1266     r = run_query(hdb, rec, query);
1267     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1268
1269     /* try a legit insert */
1270     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( 5, 'hello' )";
1271     r = run_query(hdb, 0, query);
1272     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1273
1274     /* try values as markers */
1275     MsiCloseHandle(rec);
1276     rec = MsiCreateRecord(2);
1277     MsiRecordSetInteger(rec, 1, 4);
1278     MsiRecordSetString(rec, 2, "hi");
1279     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1280     r = run_query(hdb, rec, query);
1281     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1282
1283     /* try column names and values as markers */
1284     MsiCloseHandle(rec);
1285     rec = MsiCreateRecord(4);
1286     MsiRecordSetString(rec, 1, "One");
1287     MsiRecordSetString(rec, 2, "Two");
1288     MsiRecordSetInteger(rec, 3, 5);
1289     MsiRecordSetString(rec, 4, "hi");
1290     query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( ?, '?' )";
1291     r = run_query(hdb, rec, query);
1292     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1293
1294     /* try column names as markers */
1295     MsiCloseHandle(rec);
1296     rec = MsiCreateRecord(2);
1297     MsiRecordSetString(rec, 1, "One");
1298     MsiRecordSetString(rec, 2, "Two");
1299     query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( 3, 'yellow' )";
1300     r = run_query(hdb, rec, query);
1301     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1302
1303     /* try table name as a marker */
1304     MsiCloseHandle(rec);
1305     rec = MsiCreateRecord(1);
1306     MsiRecordSetString(rec, 1, "Table");
1307     query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( 2, 'green' )";
1308     r = run_query(hdb, rec, query);
1309     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1310
1311     /* try table name and values as markers */
1312     MsiCloseHandle(rec);
1313     rec = MsiCreateRecord(3);
1314     MsiRecordSetString(rec, 1, "Table");
1315     MsiRecordSetInteger(rec, 2, 10);
1316     MsiRecordSetString(rec, 3, "haha");
1317     query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( ?, '?' )";
1318     r = run_query(hdb, rec, query);
1319     todo_wine
1320     {
1321         ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
1322     }
1323
1324     /* try all markers */
1325     MsiCloseHandle(rec);
1326     rec = MsiCreateRecord(5);
1327     MsiRecordSetString(rec, 1, "Table");
1328     MsiRecordSetString(rec, 1, "One");
1329     MsiRecordSetString(rec, 1, "Two");
1330     MsiRecordSetInteger(rec, 2, 10);
1331     MsiRecordSetString(rec, 3, "haha");
1332     query = "INSERT INTO `?` ( `?`, `?` ) VALUES ( ?, '?' )";
1333     r = run_query(hdb, rec, query);
1334     ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1335
1336     /* insert an integer as a string */
1337     MsiCloseHandle(rec);
1338     rec = MsiCreateRecord(2);
1339     MsiRecordSetString(rec, 1, "11");
1340     MsiRecordSetString(rec, 2, "hi");
1341     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1342     r = run_query(hdb, rec, query);
1343     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1344
1345     /* leave off the '' for the string */
1346     MsiCloseHandle(rec);
1347     rec = MsiCreateRecord(2);
1348     MsiRecordSetInteger(rec, 1, 12);
1349     MsiRecordSetString(rec, 2, "hi");
1350     query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, ? )";
1351     r = run_query(hdb, rec, query);
1352     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1353     MsiCloseHandle(rec);
1354
1355     MsiCloseHandle(hdb);
1356     DeleteFileA(msifile);
1357 }
1358
1359 #define MY_NVIEWS 4000    /* Largest installer I've seen uses < 2k */
1360 static void test_handle_limit(void)
1361 {
1362     int i;
1363     MSIHANDLE hdb;
1364     MSIHANDLE hviews[MY_NVIEWS];
1365     UINT r;
1366
1367     /* create an empty db */
1368     hdb = create_db();
1369     ok( hdb, "failed to create db\n");
1370
1371     memset(hviews, 0, sizeof(hviews));
1372
1373     for (i=0; i<MY_NVIEWS; i++) {
1374         static char szQueryBuf[256] = "SELECT * from `_Tables`";
1375         hviews[i] = 0xdeadbeeb;
1376         r = MsiDatabaseOpenView(hdb, szQueryBuf, &hviews[i]);
1377         ok( r == ERROR_SUCCESS, "failed to open query %d\n", i);
1378         ok( hviews[i] != 0xdeadbeeb, "no handle set\n");
1379         ok( hviews[i] != 0, "%d'th handle is NULL\n", i);
1380         if (!hviews[i])
1381             break;
1382         ok( (i == 0 || (hviews[i] != hviews[i-1])),
1383             "got handle %p twice\n", (void *) hviews[i] );
1384     }
1385
1386     for (i=0; i<MY_NVIEWS; i++) {
1387         if (hviews[i] != 0 && hviews[i] != 0xdeadbeeb) {
1388             r = MsiCloseHandle(hviews[i]);
1389             ok( r == ERROR_SUCCESS, "failed to close view handle %d\n", i);
1390         }
1391     }
1392
1393     r = MsiCloseHandle(hdb);
1394     ok( r == ERROR_SUCCESS, "failed to close database\n");
1395 }
1396
1397 START_TEST(db)
1398 {
1399     test_msidatabase();
1400     test_msiinsert();
1401     test_msidecomposedesc();
1402     test_msibadqueries();
1403     test_viewmodify();
1404     test_viewgetcolumninfo();
1405     test_getcolinfo();
1406     test_msiexport();
1407     test_longstrings();
1408     test_streamtable();
1409     test_where();
1410     test_msiimport();
1411     test_markers();
1412     test_handle_limit();
1413 }