2 * Copyright (C) 2005 Mike McCormack for CodeWeavers
4 * A test program for MSI database files.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/test.h"
28 static const char *msifile = "winetest.msi";
29 static const char *msifile2 = "winetst2.msi";
30 static const char *mstfile = "winetst.mst";
32 #ifndef ERROR_INSTALL_TRANSFORM_FAILURE
33 #define ERROR_INSTALL_TRANSFORM_FAILURE 1624
36 static void test_msidatabase(void)
43 /* create an empty database */
44 res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
45 ok( res == ERROR_SUCCESS , "Failed to create database\n" );
47 res = MsiDatabaseCommit( hdb );
48 ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
50 res = MsiCloseHandle( hdb );
51 ok( res == ERROR_SUCCESS , "Failed to close database\n" );
53 res = DeleteFile( msifile );
54 ok( res == TRUE, "Failed to delete database\n" );
57 static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec)
62 /* open a select query */
63 r = MsiDatabaseOpenView(hdb, query, &hview);
64 if (r != ERROR_SUCCESS)
66 r = MsiViewExecute(hview, 0);
67 if (r != ERROR_SUCCESS)
69 ret = MsiViewFetch(hview, phrec);
70 r = MsiViewClose(hview);
71 if (r != ERROR_SUCCESS)
73 r = MsiCloseHandle(hview);
74 if (r != ERROR_SUCCESS)
79 static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query )
84 r = MsiDatabaseOpenView(hdb, query, &hview);
85 if( r != ERROR_SUCCESS )
88 r = MsiViewExecute(hview, hrec);
89 if( r == ERROR_SUCCESS )
90 r = MsiViewClose(hview);
91 MsiCloseHandle(hview);
95 static UINT create_component_table( MSIHANDLE hdb )
97 return run_query( hdb, 0,
98 "CREATE TABLE `Component` ( "
99 "`Component` CHAR(72) NOT NULL, "
100 "`ComponentId` CHAR(38), "
101 "`Directory_` CHAR(72) NOT NULL, "
102 "`Attributes` SHORT NOT NULL, "
103 "`Condition` CHAR(255), "
104 "`KeyPath` CHAR(72) "
105 "PRIMARY KEY `Component`)" );
108 static UINT create_feature_components_table( MSIHANDLE hdb )
110 return run_query( hdb, 0,
111 "CREATE TABLE `FeatureComponents` ( "
112 "`Feature_` CHAR(38) NOT NULL, "
113 "`Component_` CHAR(72) NOT NULL "
114 "PRIMARY KEY `Feature_`, `Component_` )" );
117 static UINT create_std_dlls_table( MSIHANDLE hdb )
119 return run_query( hdb, 0,
120 "CREATE TABLE `StdDlls` ( "
121 "`File` CHAR(255) NOT NULL, "
122 "`Binary_` CHAR(72) NOT NULL "
123 "PRIMARY KEY `File` )" );
126 static UINT create_binary_table( MSIHANDLE hdb )
128 return run_query( hdb, 0,
129 "CREATE TABLE `Binary` ( "
130 "`Name` CHAR(72) NOT NULL, "
131 "`Data` CHAR(72) NOT NULL "
132 "PRIMARY KEY `Name` )" );
135 static UINT add_component_entry( MSIHANDLE hdb, const char *values )
137 char insert[] = "INSERT INTO `Component` "
138 "(`Component`, `ComponentId`, `Directory_`, `Attributes`, `Condition`, `KeyPath`) "
143 sz = strlen(values) + sizeof insert;
144 query = HeapAlloc(GetProcessHeap(),0,sz);
145 sprintf(query,insert,values);
146 r = run_query( hdb, 0, query );
147 HeapFree(GetProcessHeap(), 0, query);
151 static UINT add_feature_components_entry( MSIHANDLE hdb, const char *values )
153 char insert[] = "INSERT INTO `FeatureComponents` "
154 "(`Feature_`, `Component_`) "
159 sz = strlen(values) + sizeof insert;
160 query = HeapAlloc(GetProcessHeap(),0,sz);
161 sprintf(query,insert,values);
162 r = run_query( hdb, 0, query );
163 HeapFree(GetProcessHeap(), 0, query);
167 static UINT add_std_dlls_entry( MSIHANDLE hdb, const char *values )
169 char insert[] = "INSERT INTO `StdDlls` "
170 "(`File`, `Binary_`) "
175 sz = strlen(values) + sizeof insert;
176 query = HeapAlloc(GetProcessHeap(),0,sz);
177 sprintf(query,insert,values);
178 r = run_query( hdb, 0, query );
179 HeapFree(GetProcessHeap(), 0, query);
183 static UINT add_binary_entry( MSIHANDLE hdb, const char *values )
185 char insert[] = "INSERT INTO `Binary` "
191 sz = strlen(values) + sizeof insert;
192 query = HeapAlloc(GetProcessHeap(),0,sz);
193 sprintf(query,insert,values);
194 r = run_query( hdb, 0, query );
195 HeapFree(GetProcessHeap(), 0, query);
199 static void test_msiinsert(void)
201 MSIHANDLE hdb = 0, hview = 0, hrec = 0;
209 /* just MsiOpenDatabase should not create a file */
210 r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
211 ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
214 query = "CREATE TABLE `phone` ( "
215 "`id` INT, `name` CHAR(32), `number` CHAR(32) "
217 r = MsiDatabaseOpenView(hdb, query, &hview);
218 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
219 r = MsiViewExecute(hview, 0);
220 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
221 r = MsiViewClose(hview);
222 ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
223 r = MsiCloseHandle(hview);
224 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
226 /* insert a value into it */
227 query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
228 "VALUES('1', 'Abe', '8675309')";
229 r = MsiDatabaseOpenView(hdb, query, &hview);
230 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
231 r = MsiViewExecute(hview, 0);
232 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
233 r = MsiViewClose(hview);
234 ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
235 r = MsiCloseHandle(hview);
236 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
238 query = "SELECT * FROM `phone` WHERE `id` = 1";
239 r = do_query(hdb, query, &hrec);
240 ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
242 /* check the record contains what we put in it */
243 r = MsiRecordGetFieldCount(hrec);
244 ok(r == 3, "record count wrong\n");
247 r = MsiRecordIsNull(hrec, 0);
248 ok(r == FALSE, "field 0 not null\n");
251 r = MsiRecordGetInteger(hrec, 1);
252 ok(r == 1, "field 1 contents wrong\n");
254 r = MsiRecordGetString(hrec, 2, buf, &sz);
255 ok(r == ERROR_SUCCESS, "field 2 content fetch failed\n");
256 ok(!strcmp(buf,"Abe"), "field 2 content incorrect\n");
258 r = MsiRecordGetString(hrec, 3, buf, &sz);
259 ok(r == ERROR_SUCCESS, "field 3 content fetch failed\n");
260 ok(!strcmp(buf,"8675309"), "field 3 content incorrect\n");
262 r = MsiCloseHandle(hrec);
263 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
265 /* open a select query */
267 query = "SELECT * FROM `phone` WHERE `id` >= 10";
268 r = do_query(hdb, query, &hrec);
269 ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
270 ok(hrec == 0, "hrec should be null\n");
272 r = MsiCloseHandle(hrec);
273 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
275 query = "SELECT * FROM `phone` WHERE `id` < 0";
276 r = do_query(hdb, query, &hrec);
277 ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
279 query = "SELECT * FROM `phone` WHERE `id` <= 0";
280 r = do_query(hdb, query, &hrec);
281 ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
283 query = "SELECT * FROM `phone` WHERE `id` <> 1";
284 r = do_query(hdb, query, &hrec);
285 ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
287 query = "SELECT * FROM `phone` WHERE `id` > 10";
288 r = do_query(hdb, query, &hrec);
289 ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n");
291 /* now try a few bad INSERT xqueries */
292 query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
294 r = MsiDatabaseOpenView(hdb, query, &hview);
295 ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n");
297 /* construct a record to insert */
298 hrec = MsiCreateRecord(4);
299 r = MsiRecordSetInteger(hrec, 1, 2);
300 ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n");
301 r = MsiRecordSetString(hrec, 2, "Adam");
302 ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
303 r = MsiRecordSetString(hrec, 3, "96905305");
304 ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n");
306 /* insert another value, using a record and wildcards */
307 query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
309 r = MsiDatabaseOpenView(hdb, query, &hview);
310 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
312 if (r == ERROR_SUCCESS)
314 r = MsiViewExecute(hview, hrec);
315 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
316 r = MsiViewClose(hview);
317 ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
318 r = MsiCloseHandle(hview);
319 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
321 r = MsiCloseHandle(hrec);
322 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
324 r = MsiViewFetch(0, NULL);
325 ok(r == ERROR_INVALID_PARAMETER, "MsiViewFetch failed\n");
327 r = MsiDatabaseCommit(hdb);
328 ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
330 r = MsiCloseHandle(hdb);
331 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
333 r = DeleteFile(msifile);
334 ok(r == TRUE, "file didn't exist after commit\n");
337 typedef UINT (WINAPI *fnMsiDecomposeDescriptorA)(LPCSTR, LPCSTR, LPSTR, LPSTR, DWORD *);
338 static fnMsiDecomposeDescriptorA pMsiDecomposeDescriptorA;
340 static void test_msidecomposedesc(void)
342 char prod[MAX_FEATURE_CHARS+1], comp[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1];
348 hmod = GetModuleHandle("msi.dll");
351 pMsiDecomposeDescriptorA = (fnMsiDecomposeDescriptorA)
352 GetProcAddress(hmod, "MsiDecomposeDescriptorA");
353 if (!pMsiDecomposeDescriptorA)
356 /* test a valid feature descriptor */
357 desc = "']gAVn-}f(ZXfeAR6.jiFollowTheWhiteRabbit>3w2x^IGfe?CxI5heAvk.";
359 r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
360 ok(r == ERROR_SUCCESS, "returned an error\n");
361 ok(len == strlen(desc), "length was wrong\n");
362 ok(strcmp(prod,"{90110409-6000-11D3-8CFE-0150048383C9}")==0, "product wrong\n");
363 ok(strcmp(feature,"FollowTheWhiteRabbit")==0, "feature wrong\n");
364 ok(strcmp(comp,"{A7CD68DB-EF74-49C8-FBB2-A7C463B2AC24}")==0,"component wrong\n");
366 /* test an invalid feature descriptor with too many characters */
367 desc = "']gAVn-}f(ZXfeAR6.ji"
368 "ThisWillFailIfTheresMoreThanAGuidsChars>"
369 "3w2x^IGfe?CxI5heAvk.";
371 r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
372 ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
375 * Test a valid feature descriptor with the
376 * maximum number of characters and some trailing characters.
378 desc = "']gAVn-}f(ZXfeAR6.ji"
379 "ThisWillWorkIfTheresLTEThanAGuidsChars>"
380 "3w2x^IGfe?CxI5heAvk."
383 r = pMsiDecomposeDescriptorA(desc, prod, feature, comp, &len);
384 ok(r == ERROR_SUCCESS, "returned wrong error\n");
385 ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
388 r = pMsiDecomposeDescriptorA(desc, prod, feature, NULL, &len);
389 ok(r == ERROR_SUCCESS, "returned wrong error\n");
390 ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
393 r = pMsiDecomposeDescriptorA(desc, prod, NULL, NULL, &len);
394 ok(r == ERROR_SUCCESS, "returned wrong error\n");
395 ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
398 r = pMsiDecomposeDescriptorA(desc, NULL, NULL, NULL, &len);
399 ok(r == ERROR_SUCCESS, "returned wrong error\n");
400 ok(len == (strlen(desc) - strlen("extra")), "length wrong\n");
403 r = pMsiDecomposeDescriptorA(NULL, NULL, NULL, NULL, &len);
404 ok(r == ERROR_INVALID_PARAMETER, "returned wrong error\n");
405 ok(len == 0, "length wrong\n");
408 static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec )
413 res = MsiDatabaseOpenView( hdb, szQuery, &htab );
414 if(res == ERROR_SUCCESS )
418 r = MsiViewExecute( htab, hrec );
419 if(r != ERROR_SUCCESS )
422 r = MsiViewClose( htab );
423 if(r != ERROR_SUCCESS )
426 r = MsiCloseHandle( htab );
427 if(r != ERROR_SUCCESS )
433 static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery )
435 return try_query_param( hdb, szQuery, 0 );
438 static UINT try_insert_query( MSIHANDLE hdb, LPCSTR szQuery )
443 hrec = MsiCreateRecord( 1 );
444 MsiRecordSetString( hrec, 1, "Hello");
446 r = try_query_param( hdb, szQuery, hrec );
448 MsiCloseHandle( hrec );
452 static void test_msibadqueries(void)
459 /* just MsiOpenDatabase should not create a file */
460 r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
461 ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
463 r = MsiDatabaseCommit( hdb );
464 ok(r == ERROR_SUCCESS , "Failed to commit database\n");
466 r = MsiCloseHandle( hdb );
467 ok(r == ERROR_SUCCESS , "Failed to close database\n");
469 /* open it readonly */
470 r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb );
471 ok(r == ERROR_SUCCESS , "Failed to open database r/o\n");
473 /* add a table to it */
474 r = try_query( hdb, "select * from _Tables");
475 ok(r == ERROR_SUCCESS , "query 1 failed\n");
477 r = MsiCloseHandle( hdb );
478 ok(r == ERROR_SUCCESS , "Failed to close database r/o\n");
480 /* open it read/write */
481 r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb );
482 ok(r == ERROR_SUCCESS , "Failed to open database r/w\n");
484 /* a bunch of test queries that fail with the native MSI */
486 r = try_query( hdb, "CREATE TABLE");
487 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2a return code\n");
489 r = try_query( hdb, "CREATE TABLE `a`");
490 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2b return code\n");
492 r = try_query( hdb, "CREATE TABLE `a` ()");
493 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2c return code\n");
495 r = try_query( hdb, "CREATE TABLE `a` (`b`)");
496 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2d return code\n");
498 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) )");
499 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2e return code\n");
501 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL)");
502 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2f return code\n");
504 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY)");
505 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2g return code\n");
507 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
508 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2h return code\n");
510 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)");
511 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2i return code\n");
513 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY 'b')");
514 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2j return code\n");
516 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
517 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2k return code\n");
519 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')");
520 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2l return code\n");
522 r = try_query( hdb, "CREATE TABLE `a` (`b` CHA(72) NOT NULL PRIMARY KEY `b`)");
523 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2m return code\n");
525 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(-1) NOT NULL PRIMARY KEY `b`)");
526 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2n return code\n");
528 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(720) NOT NULL PRIMARY KEY `b`)");
529 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2o return code\n");
531 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL KEY `b`)");
532 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
534 r = try_query( hdb, "CREATE TABLE `a` (`` CHAR(72) NOT NULL PRIMARY KEY `b`)");
535 ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n");
537 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
538 ok(r == ERROR_SUCCESS , "valid query 2z failed\n");
540 r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)");
541 ok(r == ERROR_BAD_QUERY_SYNTAX , "created same table again\n");
543 r = try_query( hdb, "CREATE TABLE `aa` (`b` CHAR(72) NOT NULL, `c` "
544 "CHAR(72), `d` CHAR(255) NOT NULL LOCALIZABLE PRIMARY KEY `b`)");
545 ok(r == ERROR_SUCCESS , "query 4 failed\n");
547 r = MsiDatabaseCommit( hdb );
548 ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
550 r = try_query( hdb, "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL "
551 "PRIMARY KEY `foo`)");
552 ok(r == ERROR_SUCCESS , "query 4 failed\n");
554 r = try_insert_query( hdb, "insert into a ( `b` ) VALUES ( ? )");
555 ok(r == ERROR_SUCCESS , "failed to insert record in db\n");
557 r = MsiDatabaseCommit( hdb );
558 ok(r == ERROR_SUCCESS , "Failed to commit database after write\n");
560 r = try_query( hdb, "CREATE TABLE `boo` (`foo` CHAR(72) NOT NULL "
561 "PRIMARY KEY `ba`)");
562 ok(r != ERROR_SUCCESS , "query 5 succeeded\n");
564 r = try_query( hdb,"CREATE TABLE `bee` (`foo` CHAR(72) NOT NULL )");
565 ok(r != ERROR_SUCCESS , "query 6 succeeded\n");
567 r = try_query( hdb, "CREATE TABLE `temp` (`t` CHAR(72) NOT NULL "
569 ok(r == ERROR_SUCCESS , "query 7 failed\n");
571 r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)");
572 ok(r == ERROR_SUCCESS , "query 8 failed\n");
574 r = MsiCloseHandle( hdb );
575 ok(r == ERROR_SUCCESS , "Failed to close database transact\n");
577 r = DeleteFile( msifile );
578 ok(r == TRUE, "file didn't exist after commit\n");
581 static void test_viewmodify(void)
583 MSIHANDLE hdb = 0, hview = 0, hrec = 0;
591 /* just MsiOpenDatabase should not create a file */
592 r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
593 ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
595 query = "CREATE TABLE `phone` ( "
596 "`id` INT, `name` CHAR(32), `number` CHAR(32) "
598 r = run_query( hdb, 0, query );
599 ok(r == ERROR_SUCCESS, "query failed\n");
601 /* check what the error function reports without doing anything */
603 /* passing NULL as the 3rd param make function to crash on older platforms */
604 r = MsiViewGetError( 0, NULL, &sz );
605 ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
608 query = "SELECT * FROM `phone`";
609 r = MsiDatabaseOpenView(hdb, query, &hview);
610 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
612 /* see what happens with a good hview and bad args */
613 r = MsiViewGetError( hview, NULL, NULL );
614 ok(r == MSIDBERROR_INVALIDARG || r == MSIDBERROR_NOERROR,
615 "MsiViewGetError returns %u (expected -3)\n", r);
616 r = MsiViewGetError( hview, buffer, NULL );
617 ok(r == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n");
619 /* see what happens with a zero length buffer */
622 r = MsiViewGetError( hview, buffer, &sz );
623 ok(r == MSIDBERROR_MOREDATA, "MsiViewGetError return\n");
624 ok(buffer[0] == 'x', "buffer cleared\n");
625 ok(sz == 0, "size not zero\n");
627 /* ok this one is strange */
629 r = MsiViewGetError( hview, NULL, &sz );
630 ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
631 ok(sz == 0, "size not zero\n");
633 /* see if it really has an error */
636 r = MsiViewGetError( hview, buffer, &sz );
637 ok(r == MSIDBERROR_NOERROR, "MsiViewGetError return\n");
638 ok(buffer[0] == 0, "buffer not cleared\n");
639 ok(sz == 0, "size not zero\n");
641 r = MsiViewExecute(hview, 0);
642 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
644 /* try some invalid records */
645 r = MsiViewModify(hview, MSIMODIFY_INSERT, 0 );
646 ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
647 r = MsiViewModify(hview, -1, 0 );
648 ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n");
650 /* try an small record */
651 hrec = MsiCreateRecord(1);
652 r = MsiViewModify(hview, -1, hrec );
653 ok(r == ERROR_INVALID_DATA, "MsiViewModify failed\n");
655 r = MsiCloseHandle(hrec);
656 ok(r == ERROR_SUCCESS, "failed to close record\n");
658 /* insert a valid record */
659 hrec = MsiCreateRecord(3);
661 r = MsiRecordSetInteger(hrec, 2, 1);
662 ok(r == ERROR_SUCCESS, "failed to set integer\n");
663 r = MsiRecordSetString(hrec, 2, "bob");
664 ok(r == ERROR_SUCCESS, "failed to set integer\n");
665 r = MsiRecordSetString(hrec, 3, "7654321");
666 ok(r == ERROR_SUCCESS, "failed to set integer\n");
668 r = MsiViewExecute(hview, 0);
669 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
670 r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
671 ok(r == ERROR_SUCCESS, "MsiViewModify failed\n");
673 /* insert the same thing again */
674 r = MsiViewExecute(hview, 0);
675 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
677 /* should fail ... */
679 r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec );
680 ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n");
683 r = MsiCloseHandle(hrec);
684 ok(r == ERROR_SUCCESS, "failed to close record\n");
686 r = MsiViewClose(hview);
687 ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
688 r = MsiCloseHandle(hview);
689 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
691 r = MsiCloseHandle( hdb );
692 ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n");
695 static MSIHANDLE create_db(void)
702 /* create an empty database */
703 res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb );
704 ok( res == ERROR_SUCCESS , "Failed to create database\n" );
705 if( res != ERROR_SUCCESS )
708 res = MsiDatabaseCommit( hdb );
709 ok( res == ERROR_SUCCESS , "Failed to commit database\n" );
714 static void test_getcolinfo(void)
716 MSIHANDLE hdb, hview = 0, rec = 0;
721 /* create an empty db */
723 ok( hdb, "failed to create db\n");
725 /* tables should be present */
726 r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview);
727 ok( r == ERROR_SUCCESS, "failed to open query\n");
729 r = MsiViewExecute(hview, 0);
730 ok( r == ERROR_SUCCESS, "failed to execute query\n");
732 /* check that NAMES works */
734 r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec );
735 ok( r == ERROR_SUCCESS, "failed to get names\n");
737 r = MsiRecordGetString(rec, 1, buffer, &sz );
738 ok( r == ERROR_SUCCESS, "failed to get string\n");
739 ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n");
740 r = MsiCloseHandle( rec );
741 ok( r == ERROR_SUCCESS, "failed to close record handle\n");
743 /* check that TYPES works */
745 r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec );
746 ok( r == ERROR_SUCCESS, "failed to get names\n");
748 r = MsiRecordGetString(rec, 1, buffer, &sz );
749 ok( r == ERROR_SUCCESS, "failed to get string\n");
750 ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n");
751 r = MsiCloseHandle( rec );
752 ok( r == ERROR_SUCCESS, "failed to close record handle\n");
754 /* check that invalid values fail */
756 r = MsiViewGetColumnInfo( hview, 100, &rec );
757 ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
758 ok( rec == 0, "returned a record\n");
760 r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL );
761 ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n");
763 r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec );
764 ok( r == ERROR_INVALID_HANDLE, "wrong error code\n");
766 r = MsiViewClose(hview);
767 ok( r == ERROR_SUCCESS, "failed to close view\n");
768 r = MsiCloseHandle(hview);
769 ok( r == ERROR_SUCCESS, "failed to close view handle\n");
770 r = MsiCloseHandle(hdb);
771 ok( r == ERROR_SUCCESS, "failed to close database\n");
774 static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type)
776 MSIHANDLE hview = 0, rec = 0;
779 r = MsiDatabaseOpenView(hdb, query, &hview);
780 if( r != ERROR_SUCCESS )
783 r = MsiViewExecute(hview, 0);
784 if( r == ERROR_SUCCESS )
786 MsiViewGetColumnInfo( hview, type, &rec );
789 MsiCloseHandle(hview);
793 static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field)
795 MSIHANDLE hview = 0, rec = 0;
799 sprintf(query, "select * from `_Columns` where `Table` = '%s'", table );
801 r = MsiDatabaseOpenView(hdb, query, &hview);
802 if( r != ERROR_SUCCESS )
805 r = MsiViewExecute(hview, 0);
806 if( r == ERROR_SUCCESS )
810 r = MsiViewFetch( hview, &rec );
811 if( r != ERROR_SUCCESS)
813 r = MsiRecordGetInteger( rec, 2 );
815 type = MsiRecordGetInteger( rec, 4 );
816 MsiCloseHandle( rec );
821 MsiCloseHandle(hview);
825 static BOOL check_record( MSIHANDLE rec, UINT field, LPCSTR val )
832 r = MsiRecordGetString( rec, field, buffer, &sz );
833 return (r == ERROR_SUCCESS ) && !strcmp(val, buffer);
836 static void test_viewgetcolumninfo(void)
838 MSIHANDLE hdb = 0, rec;
842 ok( hdb, "failed to create db\n");
844 r = run_query( hdb, 0,
845 "CREATE TABLE `Properties` "
846 "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" );
847 ok( r == ERROR_SUCCESS , "Failed to create table\n" );
849 /* check the column types */
850 rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES );
851 ok( rec, "failed to get column info record\n" );
853 ok( check_record( rec, 1, "S255"), "wrong record type\n");
854 ok( check_record( rec, 2, "S1"), "wrong record type\n");
856 MsiCloseHandle( rec );
858 /* check the type in _Columns */
859 ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n");
860 ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n");
862 /* now try the names */
863 rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES );
864 ok( rec, "failed to get column info record\n" );
866 ok( check_record( rec, 1, "Property"), "wrong record type\n");
867 ok( check_record( rec, 2, "Value"), "wrong record type\n");
869 MsiCloseHandle( rec );
871 r = run_query( hdb, 0,
872 "CREATE TABLE `Binary` "
873 "( `Name` CHAR(255), `Data` OBJECT PRIMARY KEY `Name`)" );
874 ok( r == ERROR_SUCCESS , "Failed to create table\n" );
876 /* check the column types */
877 rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES );
878 ok( rec, "failed to get column info record\n" );
880 ok( check_record( rec, 1, "S255"), "wrong record type\n");
881 ok( check_record( rec, 2, "V0"), "wrong record type\n");
883 MsiCloseHandle( rec );
885 /* check the type in _Columns */
886 ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n");
887 ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n");
889 /* now try the names */
890 rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES );
891 ok( rec, "failed to get column info record\n" );
893 ok( check_record( rec, 1, "Name"), "wrong record type\n");
894 ok( check_record( rec, 2, "Data"), "wrong record type\n");
895 MsiCloseHandle( rec );
897 r = run_query( hdb, 0,
898 "CREATE TABLE `UIText` "
899 "( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" );
900 ok( r == ERROR_SUCCESS , "Failed to create table\n" );
902 ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n");
903 ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n");
905 rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES );
906 ok( rec, "failed to get column info record\n" );
907 ok( check_record( rec, 1, "Key"), "wrong record type\n");
908 ok( check_record( rec, 2, "Text"), "wrong record type\n");
909 MsiCloseHandle( rec );
911 rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES );
912 ok( rec, "failed to get column info record\n" );
913 ok( check_record( rec, 1, "s72"), "wrong record type\n");
914 ok( check_record( rec, 2, "L255"), "wrong record type\n");
915 MsiCloseHandle( rec );
917 MsiCloseHandle( hdb );
920 static void test_msiexport(void)
922 MSIHANDLE hdb = 0, hview = 0;
926 const char file[] = "phone.txt";
930 const char expected[] =
931 "id\tname\tnumber\r\n"
934 "1\tAbe\t8675309\r\n";
938 /* just MsiOpenDatabase should not create a file */
939 r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
940 ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
943 query = "CREATE TABLE `phone` ( "
944 "`id` INT, `name` CHAR(32), `number` CHAR(32) "
946 r = MsiDatabaseOpenView(hdb, query, &hview);
947 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
948 r = MsiViewExecute(hview, 0);
949 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
950 r = MsiViewClose(hview);
951 ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
952 r = MsiCloseHandle(hview);
953 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
955 /* insert a value into it */
956 query = "INSERT INTO `phone` ( `id`, `name`, `number` )"
957 "VALUES('1', 'Abe', '8675309')";
958 r = MsiDatabaseOpenView(hdb, query, &hview);
959 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
960 r = MsiViewExecute(hview, 0);
961 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
962 r = MsiViewClose(hview);
963 ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
964 r = MsiCloseHandle(hview);
965 ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
967 GetCurrentDirectory(MAX_PATH, path);
969 r = MsiDatabaseExport(hdb, "phone", path, file);
970 ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n");
977 /* check the data that was written */
979 memset(buffer, 0, sizeof buffer);
980 handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
981 if (handle != INVALID_HANDLE_VALUE)
983 ReadFile(handle, buffer, sizeof buffer, &length, NULL);
988 ok(0, "failed to open file %s\n", path);
990 ok( length == strlen(expected), "length of data wrong\n");
991 ok( !lstrcmp(buffer, expected), "data doesn't match\n");
995 static void test_longstrings(void)
997 const char insert_query[] =
998 "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')";
1000 MSIHANDLE hdb = 0, hview = 0, hrec = 0;
1003 const DWORD STRING_LENGTH = 0x10005;
1005 DeleteFile(msifile);
1006 /* just MsiOpenDatabase should not create a file */
1007 r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
1008 ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
1010 /* create a table */
1012 "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)");
1013 ok(r == ERROR_SUCCESS, "query failed\n");
1015 /* try a insert a very long string */
1016 str = HeapAlloc(GetProcessHeap(), 0, STRING_LENGTH+sizeof insert_query);
1017 len = strchr(insert_query, 'Z') - insert_query;
1018 strcpy(str, insert_query);
1019 memset(str+len, 'Z', STRING_LENGTH);
1020 strcpy(str+len+STRING_LENGTH, insert_query+len+1);
1021 r = try_query( hdb, str );
1022 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1024 HeapFree(GetProcessHeap(), 0, str);
1026 MsiDatabaseCommit(hdb);
1027 ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n");
1028 MsiCloseHandle(hdb);
1030 r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb);
1031 ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
1033 r = MsiDatabaseOpenView(hdb, "select * from `strings` where `id` = 1", &hview);
1034 ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
1036 r = MsiViewExecute(hview, 0);
1037 ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
1039 r = MsiViewFetch(hview, &hrec);
1040 ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
1042 MsiCloseHandle(hview);
1044 r = MsiRecordGetString(hrec, 2, NULL, &len);
1045 ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n");
1046 ok(len == STRING_LENGTH, "string length wrong\n");
1048 MsiCloseHandle(hrec);
1049 MsiCloseHandle(hdb);
1050 DeleteFile(msifile);
1053 static void create_file(const CHAR *name)
1058 file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
1059 ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
1060 WriteFile(file, name, strlen(name), &written, NULL);
1061 WriteFile(file, "\n", strlen("\n"), &written, NULL);
1065 static void test_streamtable(void)
1067 MSIHANDLE hdb = 0, rec, view;
1068 char file[MAX_PATH];
1074 ok( hdb, "failed to create db\n");
1076 r = run_query( hdb, 0,
1077 "CREATE TABLE `Properties` "
1078 "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" );
1079 ok( r == ERROR_SUCCESS , "Failed to create table\n" );
1081 /* check the column types */
1082 rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES );
1083 ok( rec, "failed to get column info record\n" );
1086 ok( check_record( rec, 1, "s62"), "wrong record type\n");
1087 ok( check_record( rec, 2, "V0"), "wrong record type\n");
1090 MsiCloseHandle( rec );
1092 /* now try the names */
1093 rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES );
1094 ok( rec, "failed to get column info record\n" );
1097 ok( check_record( rec, 1, "Name"), "wrong record type\n");
1098 ok( check_record( rec, 2, "Data"), "wrong record type\n");
1101 MsiCloseHandle( rec );
1103 /* insert a file into the _Streams table */
1104 create_file( "test.txt" );
1106 rec = MsiCreateRecord( 2 );
1107 MsiRecordSetString( rec, 1, "data" );
1109 r = MsiRecordSetStream( rec, 2, "test.txt" );
1110 ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r);
1112 DeleteFile("test.txt");
1114 r = MsiDatabaseOpenView( hdb,
1115 "INSERT INTO `_Streams` ( `Name`, `Data` ) VALUES ( ?, ? )", &view );
1118 ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r);
1121 r = MsiViewExecute( view, rec );
1124 ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r);
1127 MsiCloseHandle( rec );
1128 MsiCloseHandle( view );
1130 r = MsiDatabaseOpenView( hdb,
1131 "SELECT `Name`, `Data` FROM `_Streams`", &view );
1134 ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r);
1137 r = MsiViewExecute( view, 0 );
1140 ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r);
1143 r = MsiViewFetch( view, &rec );
1146 ok( r == ERROR_SUCCESS, "Failed to fetch record: %d\n", r);
1150 r = MsiRecordGetString( rec, 1, file, &size );
1153 ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r);
1154 ok( !lstrcmp(file, "data"), "Expected 'data', got %s\n", file);
1158 memset(buf, 0, MAX_PATH);
1159 r = MsiRecordReadStream( rec, 2, buf, &size );
1162 ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r);
1163 ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf);
1166 MsiCloseHandle( rec );
1168 r = MsiViewFetch( view, &rec );
1171 ok( r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
1174 MsiCloseHandle( view );
1175 MsiCloseHandle( hdb );
1176 DeleteFile(msifile);
1179 static void test_where(void)
1181 MSIHANDLE hdb = 0, rec;
1186 ok( hdb, "failed to create db\n");
1188 r = run_query( hdb, 0,
1189 "CREATE TABLE `Media` ("
1190 "`DiskId` SHORT NOT NULL, "
1191 "`LastSequence` LONG, "
1192 "`DiskPrompt` CHAR(64) LOCALIZABLE, "
1193 "`Cabinet` CHAR(255), "
1194 "`VolumeLabel` CHAR(32), "
1195 "`Source` CHAR(72) "
1196 "PRIMARY KEY `DiskId`)" );
1197 ok( r == S_OK, "cannot create Media table: %d\n", r );
1199 r = run_query( hdb, 0, "INSERT INTO `Media` "
1200 "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1201 "VALUES ( 1, 0, '', 'zero.cab', '', '' )" );
1202 ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1204 r = run_query( hdb, 0, "INSERT INTO `Media` "
1205 "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1206 "VALUES ( 2, 1, '', 'one.cab', '', '' )" );
1207 ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1209 r = run_query( hdb, 0, "INSERT INTO `Media` "
1210 "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) "
1211 "VALUES ( 3, 2, '', 'two.cab', '', '' )" );
1212 ok( r == S_OK, "cannot add file to the Media table: %d\n", r );
1214 query = "SELECT * FROM `Media`";
1215 r = do_query(hdb, query, &rec);
1216 ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1217 ok( check_record( rec, 4, "zero.cab"), "wrong cabinet\n");
1218 MsiCloseHandle( rec );
1220 query = "SELECT * FROM `Media` WHERE `LastSequence` >= 1";
1221 r = do_query(hdb, query, &rec);
1222 ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r);
1223 ok( check_record( rec, 4, "one.cab"), "wrong cabinet\n");
1225 r = MsiRecordGetInteger(rec, 1);
1226 ok( 2 == r, "field wrong\n");
1227 r = MsiRecordGetInteger(rec, 2);
1228 ok( 1 == r, "field wrong\n");
1230 MsiCloseHandle( rec );
1232 MsiCloseHandle( hdb );
1233 DeleteFile(msifile);
1236 static CHAR CURR_DIR[MAX_PATH];
1238 static const CHAR test_data[] = "FirstPrimaryColumn\tSecondPrimaryColumn\tShortInt\tShortIntNullable\tLongInt\tLongIntNullable\tString\tLocalizableString\tLocalizableStringNullable\n"
1239 "s255\ti2\ti2\tI2\ti4\tI4\tS255\tS0\ts0\n"
1240 "TestTable\tFirstPrimaryColumn\n"
1241 "stringage\t5\t2\t\t2147483640\t-2147483640\tanother string\tlocalizable\tduh\n";
1243 static void write_file(const CHAR *filename, const char *data, int data_size)
1247 HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL,
1248 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1250 WriteFile(hf, data, data_size, &size, NULL);
1254 static UINT add_table_to_db(MSIHANDLE hdb, LPCSTR table_data)
1258 write_file("temp_file", table_data, (lstrlen(table_data) - 1) * sizeof(char));
1259 r = MsiDatabaseImportA(hdb, CURR_DIR, "temp_file");
1260 DeleteFileA("temp_file");
1265 static void test_msiimport(void)
1267 MSIHANDLE hdb, view, rec;
1272 GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
1274 r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb);
1275 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1277 r = add_table_to_db(hdb, test_data);
1280 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1283 query = "SELECT * FROM `TestTable`";
1284 r = MsiDatabaseOpenView(hdb, query, &view);
1287 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1290 r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1291 count = MsiRecordGetFieldCount(rec);
1294 ok(count == 9, "Expected 9, got %d\n", count);
1295 ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n");
1296 ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n");
1297 ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n");
1298 ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n");
1299 ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n");
1300 ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n");
1301 ok(check_record(rec, 7, "String"), "Expected String\n");
1302 ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n");
1303 ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n");
1306 r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1307 count = MsiRecordGetFieldCount(rec);
1310 ok(count == 9, "Expected 9, got %d\n", count);
1311 ok(check_record(rec, 1, "s255"), "Expected s255\n");
1312 ok(check_record(rec, 2, "i2"), "Expected i2\n");
1313 ok(check_record(rec, 3, "i2"), "Expected i2\n");
1314 ok(check_record(rec, 4, "I2"), "Expected I2\n");
1315 ok(check_record(rec, 5, "i4"), "Expected i4\n");
1316 ok(check_record(rec, 6, "I4"), "Expected I4\n");
1317 ok(check_record(rec, 7, "S255"), "Expected S255\n");
1318 ok(check_record(rec, 8, "S0"), "Expected S0\n");
1319 ok(check_record(rec, 9, "s0"), "Expected s0\n");
1322 query = "SELECT * FROM `TestTable`";
1323 r = do_query(hdb, query, &rec);
1326 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1331 ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n");
1332 ok(check_record(rec, 7, "another string"), "Expected 'another string'\n");
1333 ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n");
1334 ok(check_record(rec, 9, "duh"), "Expected 'duh'\n");
1337 i = MsiRecordGetInteger(rec, 2);
1340 ok(i == 5, "Expected 5, got %d\n", i);
1343 i = MsiRecordGetInteger(rec, 3);
1346 ok(i == 2, "Expected 2, got %d\n", i);
1349 i = MsiRecordGetInteger(rec, 4);
1350 ok(i == 0x80000000, "Expected 0x80000000, got %d\n", i);
1352 i = MsiRecordGetInteger(rec, 5);
1355 ok(i == 2147483640, "Expected 2147483640, got %d\n", i);
1358 i = MsiRecordGetInteger(rec, 6);
1361 ok(i == -2147483640, "Expected -2147483640, got %d\n", i);
1364 MsiCloseHandle(rec);
1365 MsiCloseHandle(view);
1366 MsiCloseHandle(hdb);
1367 DeleteFileA(msifile);
1370 static void test_markers(void)
1377 ok( hdb, "failed to create db\n");
1379 rec = MsiCreateRecord(3);
1380 MsiRecordSetString(rec, 1, "Table");
1381 MsiRecordSetString(rec, 2, "Apples");
1382 MsiRecordSetString(rec, 3, "Oranges");
1384 /* try a legit create */
1385 query = "CREATE TABLE `Table` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1386 r = run_query(hdb, 0, query);
1387 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1388 MsiCloseHandle(rec);
1390 /* try table name as marker */
1391 rec = MsiCreateRecord(1);
1392 MsiRecordSetString(rec, 1, "Fable");
1393 query = "CREATE TABLE `?` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1394 r = run_query(hdb, rec, query);
1395 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1397 /* verify that we just created a table called '?', not 'Fable' */
1398 r = try_query(hdb, "SELECT * from `Fable`");
1399 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1401 r = try_query(hdb, "SELECT * from `?`");
1402 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1404 /* try table name as marker without backticks */
1405 MsiRecordSetString(rec, 1, "Mable");
1406 query = "CREATE TABLE ? ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1407 r = run_query(hdb, rec, query);
1408 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1410 /* try one column name as marker */
1411 MsiRecordSetString(rec, 1, "One");
1412 query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)";
1413 r = run_query(hdb, rec, query);
1414 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1416 /* try column names as markers */
1417 MsiCloseHandle(rec);
1418 rec = MsiCreateRecord(2);
1419 MsiRecordSetString(rec, 1, "One");
1420 MsiRecordSetString(rec, 2, "Two");
1421 query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `One`)";
1422 r = run_query(hdb, rec, query);
1423 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1425 /* try names with backticks */
1426 MsiCloseHandle(rec);
1427 rec = MsiCreateRecord(3);
1428 MsiRecordSetString(rec, 1, "One");
1429 MsiRecordSetString(rec, 2, "Two");
1430 MsiRecordSetString(rec, 3, "One");
1431 query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1432 r = run_query(hdb, rec, query);
1433 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1435 /* try names with backticks, minus definitions */
1436 query = "CREATE TABLE `Mable` ( `?`, `?` PRIMARY KEY `?`)";
1437 r = run_query(hdb, rec, query);
1438 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1440 /* try names without backticks */
1441 query = "CREATE TABLE `Mable` ( ? SHORT NOT NULL, ? CHAR(255) PRIMARY KEY ?)";
1442 r = run_query(hdb, rec, query);
1443 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1445 /* try one long marker */
1446 MsiCloseHandle(rec);
1447 rec = MsiCreateRecord(1);
1448 MsiRecordSetString(rec, 1, "`One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`");
1449 query = "CREATE TABLE `Mable` ( ? )";
1450 r = run_query(hdb, rec, query);
1451 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1453 /* try all names as markers */
1454 MsiCloseHandle(rec);
1455 rec = MsiCreateRecord(4);
1456 MsiRecordSetString(rec, 1, "Mable");
1457 MsiRecordSetString(rec, 2, "One");
1458 MsiRecordSetString(rec, 3, "Two");
1459 MsiRecordSetString(rec, 4, "One");
1460 query = "CREATE TABLE `?` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)";
1461 r = run_query(hdb, rec, query);
1462 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1464 /* try a legit insert */
1465 query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( 5, 'hello' )";
1466 r = run_query(hdb, 0, query);
1467 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1469 r = try_query(hdb, "SELECT * from `Table`");
1470 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1472 /* try values as markers */
1473 MsiCloseHandle(rec);
1474 rec = MsiCreateRecord(2);
1475 MsiRecordSetInteger(rec, 1, 4);
1476 MsiRecordSetString(rec, 2, "hi");
1477 query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1478 r = run_query(hdb, rec, query);
1479 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1481 /* try column names and values as markers */
1482 MsiCloseHandle(rec);
1483 rec = MsiCreateRecord(4);
1484 MsiRecordSetString(rec, 1, "One");
1485 MsiRecordSetString(rec, 2, "Two");
1486 MsiRecordSetInteger(rec, 3, 5);
1487 MsiRecordSetString(rec, 4, "hi");
1488 query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( ?, '?' )";
1489 r = run_query(hdb, rec, query);
1490 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1492 /* try column names as markers */
1493 MsiCloseHandle(rec);
1494 rec = MsiCreateRecord(2);
1495 MsiRecordSetString(rec, 1, "One");
1496 MsiRecordSetString(rec, 2, "Two");
1497 query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( 3, 'yellow' )";
1498 r = run_query(hdb, rec, query);
1499 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1501 /* try table name as a marker */
1502 MsiCloseHandle(rec);
1503 rec = MsiCreateRecord(1);
1504 MsiRecordSetString(rec, 1, "Table");
1505 query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( 2, 'green' )";
1506 r = run_query(hdb, rec, query);
1507 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1509 /* try table name and values as markers */
1510 MsiCloseHandle(rec);
1511 rec = MsiCreateRecord(3);
1512 MsiRecordSetString(rec, 1, "Table");
1513 MsiRecordSetInteger(rec, 2, 10);
1514 MsiRecordSetString(rec, 3, "haha");
1515 query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( ?, '?' )";
1516 r = run_query(hdb, rec, query);
1517 ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r);
1519 /* try all markers */
1520 MsiCloseHandle(rec);
1521 rec = MsiCreateRecord(5);
1522 MsiRecordSetString(rec, 1, "Table");
1523 MsiRecordSetString(rec, 1, "One");
1524 MsiRecordSetString(rec, 1, "Two");
1525 MsiRecordSetInteger(rec, 2, 10);
1526 MsiRecordSetString(rec, 3, "haha");
1527 query = "INSERT INTO `?` ( `?`, `?` ) VALUES ( ?, '?' )";
1528 r = run_query(hdb, rec, query);
1529 ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r);
1531 /* insert an integer as a string */
1532 MsiCloseHandle(rec);
1533 rec = MsiCreateRecord(2);
1534 MsiRecordSetString(rec, 1, "11");
1535 MsiRecordSetString(rec, 2, "hi");
1536 query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )";
1537 r = run_query(hdb, rec, query);
1538 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1540 /* leave off the '' for the string */
1541 MsiCloseHandle(rec);
1542 rec = MsiCreateRecord(2);
1543 MsiRecordSetInteger(rec, 1, 12);
1544 MsiRecordSetString(rec, 2, "hi");
1545 query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, ? )";
1546 r = run_query(hdb, rec, query);
1547 ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
1548 MsiCloseHandle(rec);
1550 MsiCloseHandle(hdb);
1551 DeleteFileA(msifile);
1554 #define MY_NVIEWS 4000 /* Largest installer I've seen uses < 2k */
1555 static void test_handle_limit(void)
1559 MSIHANDLE hviews[MY_NVIEWS];
1562 /* create an empty db */
1564 ok( hdb, "failed to create db\n");
1566 memset(hviews, 0, sizeof(hviews));
1568 for (i=0; i<MY_NVIEWS; i++) {
1569 static char szQueryBuf[256] = "SELECT * from `_Tables`";
1570 hviews[i] = 0xdeadbeeb;
1571 r = MsiDatabaseOpenView(hdb, szQueryBuf, &hviews[i]);
1572 if( r != ERROR_SUCCESS || hviews[i] == 0xdeadbeeb ||
1573 hviews[i] == 0 || (i && (hviews[i] == hviews[i-1])))
1577 ok( i == MY_NVIEWS, "problem opening views\n");
1579 for (i=0; i<MY_NVIEWS; i++) {
1580 if (hviews[i] != 0 && hviews[i] != 0xdeadbeeb) {
1581 r = MsiCloseHandle(hviews[i]);
1582 if (r != ERROR_SUCCESS)
1587 ok( i == MY_NVIEWS, "problem closing views\n");
1589 r = MsiCloseHandle(hdb);
1590 ok( r == ERROR_SUCCESS, "failed to close database\n");
1593 static void test_generate_transform(void)
1595 MSIHANDLE hdb1, hdb2;
1599 DeleteFile(msifile);
1600 DeleteFile(msifile2);
1601 DeleteFile(mstfile);
1603 /* create an empty database */
1604 r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb1 );
1605 ok( r == ERROR_SUCCESS , "Failed to create database\n" );
1607 r = MsiDatabaseCommit( hdb1 );
1608 ok( r == ERROR_SUCCESS , "Failed to commit database\n" );
1610 /* create another empty database */
1611 r = MsiOpenDatabase(msifile2, MSIDBOPEN_CREATE, &hdb2 );
1612 ok( r == ERROR_SUCCESS , "Failed to create database\n" );
1614 r = MsiDatabaseCommit( hdb2 );
1615 ok( r == ERROR_SUCCESS , "Failed to commit database\n" );
1617 /* the transform between two empty database should be empty */
1618 r = MsiDatabaseGenerateTransform(hdb1, hdb2, NULL, 0, 0);
1620 ok( r == ERROR_NO_DATA, "return code %d, should be ERROR_NO_DATA\n", r );
1623 query = "CREATE TABLE `AAR` ( `BAR` SHORT NOT NULL, `CAR` CHAR(255) PRIMARY KEY `CAR`)";
1624 r = run_query(hdb1, 0, query);
1625 ok(r == ERROR_SUCCESS, "failed to add table\n");
1628 r = MsiDatabaseGenerateTransform(hdb1, hdb2, NULL, 0, 0);
1629 ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
1631 r = MsiDatabaseGenerateTransform(hdb1, hdb2, mstfile, 0, 0);
1632 ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
1634 MsiCloseHandle( hdb1 );
1636 r = MsiDatabaseApplyTransform( hdb2, mstfile, 0 );
1637 ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r );
1639 /* apply the same transform again? */
1640 r = MsiDatabaseApplyTransform( hdb2, mstfile, 0 );
1641 ok( r == ERROR_INSTALL_TRANSFORM_FAILURE,
1642 "return code %d, should be ERROR_INSTALL_TRANSFORM_FAILURE\n", r );
1645 MsiCloseHandle( hdb2 );
1647 DeleteFile(msifile);
1648 DeleteFile(msifile2);
1649 DeleteFile(mstfile);
1654 const CHAR one[MAX_PATH];
1655 const CHAR two[MAX_PATH];
1658 static const struct join_res join_res_first[] =
1660 { "alveolar", "procerus" },
1661 { "septum", "procerus" },
1662 { "septum", "nasalis" },
1663 { "ramus", "nasalis" },
1664 { "malar", "mentalis" },
1667 static const struct join_res join_res_second[] =
1669 { "nasal", "septum" },
1670 { "mandible", "ramus" },
1673 static const struct join_res join_res_third[] =
1675 { "msvcp.dll", "abcdefgh" },
1676 { "msvcr.dll", "ijklmnop" },
1679 static void test_join(void)
1681 MSIHANDLE hdb, hview, hrec;
1688 ok( hdb, "failed to create db\n");
1690 r = create_component_table( hdb );
1691 ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r );
1693 r = add_component_entry( hdb, "'zygomatic', 'malar', 'INSTALLDIR', 0, '', ''" );
1694 ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1696 r = add_component_entry( hdb, "'maxilla', 'alveolar', 'INSTALLDIR', 0, '', ''" );
1697 ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1699 r = add_component_entry( hdb, "'nasal', 'septum', 'INSTALLDIR', 0, '', ''" );
1700 ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1702 r = add_component_entry( hdb, "'mandible', 'ramus', 'INSTALLDIR', 0, '', ''" );
1703 ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r );
1705 r = create_feature_components_table( hdb );
1706 ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r );
1708 r = add_feature_components_entry( hdb, "'procerus', 'maxilla'" );
1709 ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1711 r = add_feature_components_entry( hdb, "'procerus', 'nasal'" );
1712 ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1714 r = add_feature_components_entry( hdb, "'nasalis', 'nasal'" );
1715 ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1717 r = add_feature_components_entry( hdb, "'nasalis', 'mandible'" );
1718 ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1720 r = add_feature_components_entry( hdb, "'nasalis', 'notacomponent'" );
1721 ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1723 r = add_feature_components_entry( hdb, "'mentalis', 'zygomatic'" );
1724 ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r );
1726 r = create_std_dlls_table( hdb );
1727 ok( r == ERROR_SUCCESS, "cannot create StdDlls table: %d\n", r );
1729 r = add_std_dlls_entry( hdb, "'msvcp.dll', 'msvcp.dll.01234'" );
1730 ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r );
1732 r = add_std_dlls_entry( hdb, "'msvcr.dll', 'msvcr.dll.56789'" );
1733 ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r );
1735 r = create_binary_table( hdb );
1736 ok( r == ERROR_SUCCESS, "cannot create Binary table: %d\n", r );
1738 r = add_binary_entry( hdb, "'msvcp.dll.01234', 'abcdefgh'" );
1739 ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r );
1741 r = add_binary_entry( hdb, "'msvcr.dll.56789', 'ijklmnop'" );
1742 ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r );
1744 query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` "
1745 "FROM `Component`, `FeatureComponents` "
1746 "WHERE `Component`.`Component` = `FeatureComponents`.`Component_` "
1747 "ORDER BY `Feature_`";
1748 r = MsiDatabaseOpenView(hdb, query, &hview);
1749 ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
1751 r = MsiViewExecute(hview, 0);
1752 ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
1755 while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
1757 count = MsiRecordGetFieldCount( hrec );
1758 ok( count == 2, "Expected 2 record fields, got %d\n", count );
1761 r = MsiRecordGetString( hrec, 1, buf, &size );
1762 ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1763 if (i == 2 || i == 3) todo_wine
1765 ok( !lstrcmp( buf, join_res_first[i].one ),
1766 "Expected '%s', got %s\n", join_res_first[i].one, buf );
1770 r = MsiRecordGetString( hrec, 2, buf, &size );
1771 ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1772 if (i == 3) todo_wine
1774 ok( !lstrcmp( buf, join_res_first[i].two ),
1775 "Expected '%s', got %s\n", join_res_first[i].two, buf );
1779 MsiCloseHandle(hrec);
1782 ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
1784 MsiViewClose(hview);
1785 MsiCloseHandle(hview);
1787 query = "SELECT DISTINCT Component, ComponentId FROM FeatureComponents, Component "
1788 "WHERE FeatureComponents.Component_=Component.Component "
1789 "AND (Feature_='nasalis') ORDER BY Feature_";
1790 r = MsiDatabaseOpenView(hdb, query, &hview);
1793 ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
1796 r = MsiViewExecute(hview, 0);
1799 ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
1803 while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
1805 count = MsiRecordGetFieldCount( hrec );
1806 ok( count == 2, "Expected 2 record fields, got %d\n", count );
1809 r = MsiRecordGetString( hrec, 1, buf, &size );
1810 ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1811 ok( !lstrcmp( buf, join_res_second[i].one ),
1812 "Expected '%s', got %s\n", join_res_second[i].one, buf );
1815 r = MsiRecordGetString( hrec, 2, buf, &size );
1816 ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1817 ok( !lstrcmp( buf, join_res_second[i].two ),
1818 "Expected '%s', got %s\n", join_res_second[i].two, buf );
1821 MsiCloseHandle(hrec);
1826 ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
1829 MsiViewClose(hview);
1830 MsiCloseHandle(hview);
1832 query = "SELECT `StdDlls`.`File`, `Binary`.`Data` "
1833 "FROM `StdDlls`, `Binary` "
1834 "WHERE `StdDlls`.`Binary_` = `Binary`.`Name` "
1836 r = MsiDatabaseOpenView(hdb, query, &hview);
1839 ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r );
1842 r = MsiViewExecute(hview, 0);
1845 ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r );
1849 while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS)
1851 count = MsiRecordGetFieldCount( hrec );
1852 ok( count == 2, "Expected 2 record fields, got %d\n", count );
1855 r = MsiRecordGetString( hrec, 1, buf, &size );
1856 ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1857 ok( !lstrcmp( buf, join_res_third[i].one ),
1858 "Expected '%s', got %s\n", join_res_third[i].one, buf );
1861 r = MsiRecordGetString( hrec, 2, buf, &size );
1862 ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r );
1863 ok( !lstrcmp( buf, join_res_third[i].two ),
1864 "Expected '%s', got %s\n", join_res_third[i].two, buf );
1867 MsiCloseHandle(hrec);
1872 ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r );
1875 MsiViewClose(hview);
1876 MsiCloseHandle(hview);
1877 MsiCloseHandle(hdb);
1878 DeleteFile(msifile);
1885 test_msidecomposedesc();
1886 test_msibadqueries();
1888 test_viewgetcolumninfo();
1896 test_handle_limit();
1897 test_generate_transform();