1 /*
2  * Copyright (C) 2005 Mike McCormack for CodeWeavers
3  *
4  * A test program for MSI records
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 #include <windows.h>
22 #include <msi.h>
23 #include <msiquery.h>
24 
25 #include "wine/test.h"
26 
27 static const char *msifile = "winetest-record.msi";
28 static const WCHAR msifileW[] =
29     {'w','i','n','e','t','e','s','t','-','r','e','c','o','r','d','.','m','s','i',0};
30 
31 static BOOL create_temp_file(char *name)
32 {
33     UINT r;
34     unsigned char buffer[26], i;
35     DWORD sz;
36     HANDLE handle;
37 
38     r = GetTempFileNameA(".", "msitest",0,name);
39     if(!r)
40         return r;
41     handle = CreateFileA(name, GENERIC_READ|GENERIC_WRITE,
42         0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
43     if(handle==INVALID_HANDLE_VALUE)
44         return FALSE;
45     for(i=0; i<26; i++)
46         buffer[i]=i+'a';
47     r = WriteFile(handle,buffer,sizeof buffer,&sz,NULL);
48     CloseHandle(handle);
49     return r;
50 }
51 
52 static void test_msirecord(void)
53 {
54     DWORD r, sz;
55     INT i;
56     MSIHANDLE h;
57     char buf[10];
58     WCHAR bufW[10];
59     const char str[] = "hello";
60     const WCHAR strW[] = { 'h','e','l','l','o',0};
61     char filename[MAX_PATH];
62 
63     /* check behaviour with an invalid record */
64     r = MsiRecordGetFieldCount(0);
65     ok(r==-1, "field count for invalid record not -1\n");
66     SetLastError(0);
67     r = MsiRecordIsNull(0, 0);
68     ok(r==0, "invalid handle not considered to be non-null...\n");
69     ok(GetLastError()==0, "MsiRecordIsNull set LastError\n");
70     r = MsiRecordGetInteger(0,0);
71     ok(r == MSI_NULL_INTEGER, "got integer from invalid record\n");
72     r = MsiRecordSetInteger(0,0,0);
73     ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
74     r = MsiRecordSetInteger(0,-1,0);
75     ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n");
76     SetLastError(0);
77     h = MsiCreateRecord(-1);
78     ok(h==0, "created record with -1 elements\n");
79     h = MsiCreateRecord(0x10000);
80     ok(h==0, "created record with 0x10000 elements\n");
81     /* doesn't set LastError */
82     ok(GetLastError()==0, "MsiCreateRecord set last error\n");
83     r = MsiRecordClearData(0);
84     ok(r == ERROR_INVALID_HANDLE, "MsiRecordClearData returned wrong error\n");
85     r = MsiRecordDataSize(0,0);
86     ok(r == 0, "MsiRecordDataSize returned wrong error\n");
87 
88 
89     /* check behaviour of a record with 0 elements */
90     h = MsiCreateRecord(0);
91     ok(h!=0, "couldn't create record with zero elements\n");
92     r = MsiRecordGetFieldCount(h);
93     ok(r==0, "field count should be zero\n");
94     r = MsiRecordIsNull(h,0);
95     ok(r, "new record wasn't null\n");
96     r = MsiRecordIsNull(h,1);
97     ok(r, "out of range record wasn't null\n");
98     r = MsiRecordIsNull(h,-1);
99     ok(r, "out of range record wasn't null\n");
100     r = MsiRecordDataSize(h,0);
101     ok(r==0, "size of null record is 0\n");
102     sz = sizeof buf;
103     strcpy(buf,"x");
104     r = MsiRecordGetStringA(h, 0, buf, &sz);
105     ok(r==ERROR_SUCCESS, "failed to get null string\n");
106     ok(sz==0, "null string too long\n");
107     ok(buf[0]==0, "null string not set\n");
108 
109     /* same record, but add an integer to it */
110     r = MsiRecordSetInteger(h, 0, 0);
111     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 0\n");
112     r = MsiRecordIsNull(h,0);
113     ok(r==0, "new record is null after setting an integer\n");
114     r = MsiRecordDataSize(h,0);
115     ok(r==sizeof(DWORD), "size of integer record is 4\n");
116     r = MsiRecordSetInteger(h, 0, 1);
117     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 1\n");
118     r = MsiRecordSetInteger(h, 1, 1);
119     ok(r == ERROR_INVALID_PARAMETER, "set integer at 1\n");
120     r = MsiRecordSetInteger(h, -1, 0);
121     ok(r == ERROR_INVALID_PARAMETER, "set integer at -1\n");
122     r = MsiRecordIsNull(h,0);
123     ok(r==0, "new record is null after setting an integer\n");
124     r = MsiRecordGetInteger(h, 0);
125     ok(r == 1, "failed to get integer\n");
126 
127     /* same record, but add a null or empty string to it */
128     r = MsiRecordSetStringA(h, 0, NULL);
129     ok(r == ERROR_SUCCESS, "Failed to set null string at 0\n");
130     r = MsiRecordIsNull(h, 0);
131     ok(r == TRUE, "null string not null field\n");
132     r = MsiRecordDataSize(h, 0);
133     ok(r == 0, "size of string record is strlen\n");
134     buf[0] = 0;
135     sz = sizeof buf;
136     r = MsiRecordGetStringA(h, 0, buf, &sz);
137     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
138     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
139     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
140     bufW[0] = 0;
141     sz = sizeof bufW / sizeof bufW[0];
142     r = MsiRecordGetStringW(h, 0, bufW, &sz);
143     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
144     ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n");
145     ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n");
146     r = MsiRecordSetStringA(h, 0, "");
147     ok(r == ERROR_SUCCESS, "Failed to set empty string at 0\n");
148     r = MsiRecordIsNull(h, 0);
149     ok(r == TRUE, "null string not null field\n");
150     r = MsiRecordDataSize(h, 0);
151     ok(r == 0, "size of string record is strlen\n");
152     buf[0] = 0;
153     sz = sizeof buf;
154     r = MsiRecordGetStringA(h, 0, buf, &sz);
155     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
156     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
157     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
158     bufW[0] = 0;
159     sz = sizeof bufW / sizeof bufW[0];
160     r = MsiRecordGetStringW(h, 0, bufW, &sz);
161     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
162     ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n");
163     ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n");
164 
165     /* same record, but add a null integer to it */
166     r = MsiRecordSetInteger(h, 0, 1);
167     ok(r == ERROR_SUCCESS, "Failed to set integer at 0\n");
168     r = MsiRecordIsNull(h, 0);
169     ok(r == FALSE, "expected field to be non-null\n");
170     r = MsiRecordSetInteger(h, 0, MSI_NULL_INTEGER);
171     ok(r == ERROR_SUCCESS, "Failed to set integer at 0\n");
172     r = MsiRecordIsNull(h, 0);
173     ok(r == TRUE, "expected field to be null\n");
174     sz = sizeof buf;
175     r = MsiRecordGetStringA(h, 0, buf, &sz);
176     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
177     ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n");
178     ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n");
179 
180     /* same record, but add a string to it */
181     r = MsiRecordSetStringA(h,0,str);
182     ok(r == ERROR_SUCCESS, "Failed to set string at 0\n");
183     r = MsiRecordGetInteger(h, 0);
184     ok(r == MSI_NULL_INTEGER, "should get invalid integer\n");
185     r = MsiRecordDataSize(h,0);
186     ok(r==sizeof str-1, "size of string record is strlen\n");
187     buf[0]=0;
188     sz = sizeof buf;
189     r = MsiRecordGetStringA(h,0,buf,&sz);
190     ok(r == ERROR_SUCCESS, "Failed to get string at 0\n");
191     ok(0==strcmp(buf,str), "MsiRecordGetStringA returned the wrong string\n");
192     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
193     buf[0]=0;
194     sz = sizeof str - 2;
195     r = MsiRecordGetStringA(h,0,buf,&sz);
196     ok(r == ERROR_MORE_DATA, "small buffer should yield ERROR_MORE_DATA\n");
197     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
198     ok(0==strncmp(buf,str,sizeof str-3), "MsiRecordGetStringA returned the wrong string\n");
199     ok(buf[sizeof str - 3]==0, "string wasn't nul terminated\n");
200 
201     buf[0]=0;
202     sz = sizeof str;
203     r = MsiRecordGetStringA(h,0,buf,&sz);
204     ok(r == ERROR_SUCCESS, "wrong error\n");
205     ok(sz == sizeof str-1, "MsiRecordGetStringA returned the wrong length\n");
206     ok(0==strcmp(buf,str), "MsiRecordGetStringA returned the wrong string\n");
207 
208 
209     memset(bufW, 0, sizeof bufW);
210     sz = 5;
211     r = MsiRecordGetStringW(h,0,bufW,&sz);
212     ok(r == ERROR_MORE_DATA, "wrong error\n");
213     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
214     ok(0==memcmp(bufW,strW,8), "MsiRecordGetStringA returned the wrong string\n");
215 
216     sz = 0;
217     bufW[0] = 'x';
218     r = MsiRecordGetStringW(h,0,bufW,&sz);
219     ok(r == ERROR_MORE_DATA, "wrong error\n");
220     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
221     ok('x'==bufW[0], "MsiRecordGetStringA returned the wrong string\n");
222 
223     memset(buf, 0, sizeof buf);
224     sz = 5;
225     r = MsiRecordGetStringA(h,0,buf,&sz);
226     ok(r == ERROR_MORE_DATA, "wrong error\n");
227     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
228     ok(0==memcmp(buf,str,4), "MsiRecordGetStringA returned the wrong string\n");
229 
230     sz = 0;
231     buf[0] = 'x';
232     r = MsiRecordGetStringA(h,0,buf,&sz);
233     ok(r == ERROR_MORE_DATA, "wrong error\n");
234     ok(sz == 5, "MsiRecordGetStringA returned the wrong length\n");
235     ok('x'==buf[0], "MsiRecordGetStringA returned the wrong string\n");
236 
237     /* same record, check we can wipe all the data */
238     r = MsiRecordClearData(h);
239     ok(r == ERROR_SUCCESS, "Failed to clear record\n");
240     r = MsiRecordClearData(h);
241     ok(r == ERROR_SUCCESS, "Failed to clear record again\n");
242     r = MsiRecordIsNull(h,0);
243     ok(r, "cleared record wasn't null\n");
244 
245     /* same record, try converting strings to integers */
246     i = MsiRecordSetStringA(h,0,"42");
247     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
248     i = MsiRecordGetInteger(h, 0);
249     ok(i == 42, "should get invalid integer\n");
250     i = MsiRecordSetStringA(h,0,"-42");
251     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
252     i = MsiRecordGetInteger(h, 0);
253     ok(i == -42, "should get invalid integer\n");
254     i = MsiRecordSetStringA(h,0," 42");
255     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
256     i = MsiRecordGetInteger(h, 0);
257     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
258     i = MsiRecordSetStringA(h,0,"42 ");
259     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
260     i = MsiRecordGetInteger(h, 0);
261     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
262     i = MsiRecordSetStringA(h,0,"42.0");
263     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
264     i = MsiRecordGetInteger(h, 0);
265     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
266     i = MsiRecordSetStringA(h,0,"0x42");
267     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
268     i = MsiRecordGetInteger(h, 0);
269     ok(i == MSI_NULL_INTEGER, "should get invalid integer\n");
270     i = MsiRecordSetStringA(h,0,"1000000000000000");
271     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
272     i = MsiRecordGetInteger(h, 0);
273     ok(i == -1530494976, "should get truncated integer\n");
274     i = MsiRecordSetStringA(h,0,"2147483647");
275     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
276     i = MsiRecordGetInteger(h, 0);
277     ok(i == 2147483647, "should get maxint\n");
278     i = MsiRecordSetStringA(h,0,"-2147483647");
279     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
280     i = MsiRecordGetInteger(h, 0);
281     ok(i == -2147483647, "should get -maxint-1\n");
282     i = MsiRecordSetStringA(h,0,"4294967297");
283     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
284     i = MsiRecordGetInteger(h, 0);
285     ok(i == 1, "should get one\n");
286     i = MsiRecordSetStringA(h,0,"foo");
287     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
288     i = MsiRecordGetInteger(h, 0);
289     ok(i == MSI_NULL_INTEGER, "should get zero\n");
290     i = MsiRecordSetStringA(h,0,"");
291     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
292     i = MsiRecordGetInteger(h, 0);
293     ok(i == MSI_NULL_INTEGER, "should get zero\n");
294     i = MsiRecordSetStringA(h,0,"+1");
295     ok(i == ERROR_SUCCESS, "Failed to set string at 0\n");
296     i = MsiRecordGetInteger(h, 0);
297     ok(i == MSI_NULL_INTEGER, "should get zero\n");
298 
299     /* same record, try converting integers to strings */
300     r = MsiRecordSetInteger(h, 0, 32);
301     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
302     sz = 1;
303     r = MsiRecordGetStringA(h, 0, NULL, &sz);
304     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
305     ok(sz == 2, "length wrong\n");
306     buf[0]=0;
307     sz = sizeof buf;
308     r = MsiRecordGetStringA(h, 0, buf, &sz);
309     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
310     ok(0==strcmp(buf,"32"), "failed to get string from integer\n");
311     r = MsiRecordSetInteger(h, 0, -32);
312     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n");
313     buf[0]=0;
314     sz = 1;
315     r = MsiRecordGetStringA(h, 0, NULL, &sz);
316     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
317     ok(sz == 3, "length wrong\n");
318     sz = sizeof buf;
319     r = MsiRecordGetStringA(h, 0, buf, &sz);
320     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
321     ok(0==strcmp(buf,"-32"), "failed to get string from integer\n");
322     buf[0]=0;
323 
324     /* same record, now try streams */
325     r = MsiRecordSetStreamA(h, 0, NULL);
326     ok(r == ERROR_INVALID_PARAMETER, "set NULL stream\n");
327     sz = sizeof buf;
328     r = MsiRecordReadStream(h, 0, buf, &sz);
329     ok(r == ERROR_INVALID_DATATYPE, "read non-stream type\n");
330     ok(sz == sizeof buf, "set sz\n");
331     r = MsiRecordDataSize( h, -1);
332     ok(r == 0,"MsiRecordDataSize returned wrong size\n");
333     r = MsiRecordDataSize( h, 0);
334     ok(r == 4,"MsiRecordDataSize returned wrong size\n");
335 
336     /* same record, now close it */
337     r = MsiCloseHandle(h);
338     ok(r == ERROR_SUCCESS, "Failed to close handle\n");
339 
340     /* now try streams in a new record - need to create a file to play with */
341     r = create_temp_file(filename);
342     if(!r)
343         return;
344 
345     /* streams can't be inserted in field 0 for some reason */
346     h = MsiCreateRecord(2);
347     ok(h, "couldn't create a two field record\n");
348     r = MsiRecordSetStreamA(h, 0, filename);
349     ok(r == ERROR_INVALID_PARAMETER, "added stream to field 0\n");
350     r = MsiRecordSetStreamA(h, 1, filename);
351     ok(r == ERROR_SUCCESS, "failed to add stream to record\n");
352     r = MsiRecordReadStream(h, 1, buf, NULL);
353     ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
354     DeleteFileA(filename); /* Windows 98 doesn't like this at all, so don't check return. */
355     r = MsiRecordReadStream(h, 1, NULL, NULL);
356     ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
357     sz = sizeof buf;
358     r = MsiRecordReadStream(h, 1, NULL, &sz);
359     ok(r == ERROR_SUCCESS, "failed to read stream\n");
360     ok(sz==26,"couldn't get size of stream\n");
361     sz = 0;
362     r = MsiRecordReadStream(h, 1, buf, &sz);
363     ok(r == ERROR_SUCCESS, "failed to read stream\n");
364     ok(sz==0,"short read\n");
365     sz = sizeof buf;
366     r = MsiRecordReadStream(h, 1, buf, &sz);
367     ok(r == ERROR_SUCCESS, "failed to read stream\n");
368     ok(sz==sizeof buf,"short read\n");
369     ok(!strncmp(buf,"abcdefghij",10), "read the wrong thing\n");
370     sz = sizeof buf;
371     r = MsiRecordReadStream(h, 1, buf, &sz);
372     ok(r == ERROR_SUCCESS, "failed to read stream\n");
373     ok(sz==sizeof buf,"short read\n");
374     ok(!strncmp(buf,"klmnopqrst",10), "read the wrong thing\n");
375     memset(buf,0,sizeof buf);
376     sz = sizeof buf;
377     r = MsiRecordReadStream(h, 1, buf, &sz);
378     ok(r == ERROR_SUCCESS, "failed to read stream\n");
379     ok(sz==6,"short read\n");
380     ok(!strcmp(buf,"uvwxyz"), "read the wrong thing\n");
381     memset(buf,0,sizeof buf);
382     sz = sizeof buf;
383     r = MsiRecordReadStream(h, 1, buf, &sz);
384     ok(r == ERROR_SUCCESS, "failed to read stream\n");
385     ok(sz==0,"size non-zero at end of stream\n");
386     ok(buf[0]==0, "read something at end of the stream\n");
387     r = MsiRecordSetStreamA(h, 1, NULL);
388     ok(r == ERROR_SUCCESS, "failed to reset stream\n");
389     sz = 0;
390     r = MsiRecordReadStream(h, 1, NULL, &sz);
391     ok(r == ERROR_SUCCESS, "bytes left wrong after reset\n");
392     ok(sz==26,"couldn't get size of stream\n");
393     r = MsiRecordDataSize(h,1);
394     ok(r == 26,"MsiRecordDataSize returned wrong size\n");
395 
396     /* now close the stream record */
397     r = MsiCloseHandle(h);
398     ok(r == ERROR_SUCCESS, "Failed to close handle\n");
399     DeleteFileA(filename); /* Delete it for sure, when everything else is closed. */
400 }
401 
402 static void test_MsiRecordGetString(void)
403 {
404     MSIHANDLE rec;
405     CHAR buf[MAX_PATH];
406     DWORD sz;
407     UINT r;
408 
409     rec = MsiCreateRecord(2);
410     ok(rec != 0, "Expected a valid handle\n");
411 
412     sz = MAX_PATH;
413     r = MsiRecordGetStringA(rec, 1, NULL, &sz);
414     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r);
415     ok(sz == 0, "Expected 0, got %d\n",sz);
416 
417     sz = MAX_PATH;
418     lstrcpyA(buf, "apple");
419     r = MsiRecordGetStringA(rec, 1, buf, &sz);
420     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
421     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
422     ok(sz == 0, "Expected 0, got %d\n", sz);
423 
424     sz = MAX_PATH;
425     lstrcpyA(buf, "apple");
426     r = MsiRecordGetStringA(rec, 10, buf, &sz);
427     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
428     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
429     ok(sz == 0, "Expected 0, got %d\n", sz);
430 
431     MsiCloseHandle(rec);
432 
433     rec = MsiCreateRecord(1);
434     ok(rec != 0, "Expected a valid handle\n");
435 
436     r = MsiRecordSetInteger(rec, 1, 5);
437     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
438 
439     sz = MAX_PATH;
440     r = MsiRecordGetStringA(rec, 1, NULL, &sz);
441     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r);
442     ok(sz == 1, "Expected 1, got %d\n",sz);
443 
444     sz = MAX_PATH;
445     lstrcpyA(buf, "apple");
446     r = MsiRecordGetStringA(rec, 1, buf, &sz);
447     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
448     ok(!lstrcmpA(buf, "5"), "Expected \"5\", got \"%s\"\n", buf);
449     ok(sz == 1, "Expected 1, got %d\n", sz);
450 
451     r = MsiRecordSetInteger(rec, 1, -5);
452     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
453 
454     sz = MAX_PATH;
455     lstrcpyA(buf, "apple");
456     r = MsiRecordGetStringA(rec, 1, buf, &sz);
457     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
458     ok(!lstrcmpA(buf, "-5"), "Expected \"-5\", got \"%s\"\n", buf);
459     ok(sz == 2, "Expected 2, got %d\n", sz);
460 
461     MsiCloseHandle(rec);
462 }
463 
464 static void test_MsiRecordGetInteger(void)
465 {
466     MSIHANDLE rec;
467     INT val;
468     UINT r;
469 
470     rec = MsiCreateRecord(1);
471     ok(rec != 0, "Expected a valid handle\n");
472 
473     r = MsiRecordSetStringA(rec, 1, "5");
474     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
475 
476     val = MsiRecordGetInteger(rec, 1);
477     ok(val == 5, "Expected 5, got %d\n", val);
478 
479     r = MsiRecordSetStringA(rec, 1, "-5");
480     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
481 
482     val = MsiRecordGetInteger(rec, 1);
483     ok(val == -5, "Expected -5, got %d\n", val);
484 
485     r = MsiRecordSetStringA(rec, 1, "5apple");
486     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
487 
488     val = MsiRecordGetInteger(rec, 1);
489     ok(val == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", val);
490 
491     MsiCloseHandle(rec);
492 }
493 
494 static void test_fieldzero(void)
495 {
496     MSIHANDLE hdb, hview, rec;
497     CHAR buf[MAX_PATH];
498     LPCSTR query;
499     DWORD sz;
500     UINT r;
501 
502     rec = MsiCreateRecord(1);
503     ok(rec != 0, "Expected a valid handle\n");
504 
505     r = MsiRecordGetInteger(rec, 0);
506     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
507 
508     sz = MAX_PATH;
509     lstrcpyA(buf, "apple");
510     r = MsiRecordGetStringA(rec, 0, buf, &sz);
511     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
512     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
513     ok(sz == 0, "Expected 0, got %d\n", sz);
514 
515     r = MsiRecordIsNull(rec, 0);
516     ok(r == TRUE, "Expected TRUE, got %d\n", r);
517 
518     r = MsiRecordGetInteger(rec, 1);
519     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
520 
521     r = MsiRecordSetInteger(rec, 1, 42);
522     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
523 
524     r = MsiRecordGetInteger(rec, 0);
525     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
526 
527     sz = MAX_PATH;
528     lstrcpyA(buf, "apple");
529     r = MsiRecordGetStringA(rec, 0, buf, &sz);
530     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
531     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
532     ok(sz == 0, "Expected 0, got %d\n", sz);
533 
534     r = MsiRecordIsNull(rec, 0);
535     ok(r == TRUE, "Expected TRUE, got %d\n", r);
536 
537     r = MsiRecordGetInteger(rec, 1);
538     ok(r == 42, "Expected 42, got %d\n", r);
539 
540     r = MsiRecordSetStringA(rec, 1, "bologna");
541     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
542 
543     r = MsiRecordGetInteger(rec, 0);
544     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
545 
546     sz = MAX_PATH;
547     lstrcpyA(buf, "apple");
548     r = MsiRecordGetStringA(rec, 0, buf, &sz);
549     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
550     ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf);
551     ok(sz == 0, "Expected 0, got %d\n", sz);
552 
553     r = MsiRecordIsNull(rec, 0);
554     ok(r == TRUE, "Expected TRUE, got %d\n", r);
555 
556     sz = MAX_PATH;
557     lstrcpyA(buf, "apple");
558     r = MsiRecordGetStringA(rec, 1, buf, &sz);
559     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
560     ok(!lstrcmpA(buf, "bologna"), "Expected \"bologna\", got \"%s\"\n", buf);
561     ok(sz == 7, "Expected 7, got %d\n", sz);
562 
563     MsiCloseHandle(rec);
564 
565     r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_CREATE, &hdb);
566     ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
567 
568     query = "CREATE TABLE `drone` ( "
569            "`id` INT, `name` CHAR(32), `number` CHAR(32) "
570            "PRIMARY KEY `id`)";
571     r = MsiDatabaseOpenViewA(hdb, query, &hview);
572     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
573     r = MsiViewExecute(hview, 0);
574     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
575     r = MsiViewClose(hview);
576     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
577     r = MsiCloseHandle(hview);
578     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
579 
580     query = "INSERT INTO `drone` ( `id`, `name`, `number` )"
581            "VALUES('1', 'Abe', '8675309')";
582     r = MsiDatabaseOpenViewA(hdb, query, &hview);
583     ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n");
584     r = MsiViewExecute(hview, 0);
585     ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n");
586     r = MsiViewClose(hview);
587     ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
588     r = MsiCloseHandle(hview);
589     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
590 
591     r = MsiDatabaseGetPrimaryKeysA(hdb, "drone", &rec);
592     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
593 
594     r = MsiRecordGetInteger(rec, 0);
595     ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r);
596 
597     sz = MAX_PATH;
598     lstrcpyA(buf, "apple");
599     r = MsiRecordGetStringA(rec, 0, buf, &sz);
600     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
601     ok(!lstrcmpA(buf, "drone"), "Expected \"drone\", got \"%s\"\n", buf);
602     ok(sz == 5, "Expected 5, got %d\n", sz);
603 
604     r = MsiRecordIsNull(rec, 0);
605     ok(r == FALSE, "Expected FALSE, got %d\n", r);
606 
607     MsiCloseHandle(rec);
608 
609     r = MsiDatabaseGetPrimaryKeysA(hdb, "nosuchtable", &rec);
610     ok(r == ERROR_INVALID_TABLE, "Expected ERROR_INVALID_TABLE, got %d\n", r);
611 
612     query = "SELECT * FROM `drone` WHERE `id` = 1";
613     r = MsiDatabaseOpenViewA(hdb, query, &hview);
614     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
615     r = MsiViewExecute(hview, 0);
616     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
617     r = MsiViewFetch(hview, &rec);
618     ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
619 
620     r = MsiRecordGetInteger(rec, 0);
621     ok(r != MSI_NULL_INTEGER && r != 0, "Expected non-NULL value, got %d\n", r);
622 
623     r = MsiRecordIsNull(rec, 0);
624     ok(r == FALSE, "Expected FALSE, got %d\n", r);
625 
626     r = MsiCloseHandle(hview);
627     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
628     MsiCloseHandle(rec);
629     MsiCloseHandle(hdb);
630     DeleteFileA(msifile);
631 }
632 
633 START_TEST(record)
634 {
635     test_msirecord();
636     test_MsiRecordGetString();
637     test_MsiRecordGetInteger();
638     test_fieldzero();
639 }
640