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