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