diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2012-10-23 09:39:12 +0200 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2012-12-06 20:26:27 +0100 |
commit | fb131be992277bd4e36e8cbcb2650588f1ee831e (patch) | |
tree | 97047d51febe7c9004328f6b3c0822b20324d206 | |
parent | ef7498980164130e9aea170a84eaa184a07d0e99 (diff) | |
download | msitools-fb131be992277bd4e36e8cbcb2650588f1ee831e.tar.gz msitools-fb131be992277bd4e36e8cbcb2650588f1ee831e.tar.xz msitools-fb131be992277bd4e36e8cbcb2650588f1ee831e.zip |
add tests
-rw-r--r-- | Makefile.am | 2 | ||||
-rw-r--r-- | configure.ac | 3 | ||||
-rw-r--r-- | tests/Makefile.am | 29 | ||||
-rw-r--r-- | tests/runtest.in | 12 | ||||
-rw-r--r-- | tests/test.h | 20 | ||||
-rw-r--r-- | tests/testdatabase.c | 9380 | ||||
-rw-r--r-- | tests/testdatabase.ok | 2955 | ||||
-rw-r--r-- | tests/testrecord.c | 621 | ||||
-rw-r--r-- | tests/testrecord.ok | 214 | ||||
-rw-r--r-- | tests/testsuminfo.c | 429 | ||||
-rw-r--r-- | tests/testsuminfo.ok | 87 |
11 files changed, 13750 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am index 123937a..9c995cd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = libmsi +SUBDIRS = libmsi tests noinst_HEADERS = include/debug.h include/libmsi.h diff --git a/configure.ac b/configure.ac index c2ecd8b..921128d 100644 --- a/configure.ac +++ b/configure.ac @@ -10,5 +10,6 @@ AC_PROG_YACC LT_INIT([win32-dll disable-fast-install]) -AC_CONFIG_FILES([Makefile libmsi/Makefile]) +AC_CONFIG_FILES([Makefile libmsi/Makefile tests/Makefile]) +AC_CONFIG_FILES([tests/runtest], [chmod +x tests/runtest]) AC_OUTPUT diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..e9d4b94 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,29 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(srcdir) -I. + +check_PROGRAMS = testdatabase testrecord testsuminfo + +testdatabase_SOURCES = testdatabase.c +testdatabase_LDADD = ../libmsi/libmsi.la -lole32 +testrecord_SOURCES = testrecord.c +testrecord_LDADD = ../libmsi/libmsi.la -lole32 +testsuminfo_SOURCES = testsuminfo.c +testsuminfo_LDADD = ../libmsi/libmsi.la -lole32 + +TESTS = \ + testdatabase$(EXEEXT) \ + testrecord$(EXEEXT) \ + testsuminfo$(EXEEXT) + +TESTS_ENVIRONMENT = srcdir=$(srcdir) ./runtest + +CLEANFILES = \ + testdatabase.log testdatabase.diff \ + testrecord.log testrecord.diff \ + testsuminfo.log testsuminfo.diff + +EXTRA_DIST = \ + testdatabase.ok \ + testrecord.ok \ + testsuminfo.ok + +.NOTPARALLEL: diff --git a/tests/runtest.in b/tests/runtest.in new file mode 100644 index 0000000..f8a3f4e --- /dev/null +++ b/tests/runtest.in @@ -0,0 +1,12 @@ +#! /bin/bash + +: ${srcdir=$(basename $0)} +: ${EXEEXT=@EXEEXT@} + +base=${1%${EXEEXT}} + +rm -f winetest-db.msi +$1 > $base.log +diff -u $srcdir/$base.ok $base.log | tee $base.diff +test -s $base.diff && exit 1 +exit 0 diff --git a/tests/test.h b/tests/test.h new file mode 100644 index 0000000..72234df --- /dev/null +++ b/tests/test.h @@ -0,0 +1,20 @@ +#include <stdarg.h> +#include <stdbool.h> +#include "debug.h" + +#define todo_wine if(0) + +#define ok(cond, desc, ...) _ok(cond, #cond, desc, ## __VA_ARGS__ ) + +static inline void _ok(bool cond, const char *cond_str, const char *str, ...) +{ + va_list ap; + va_start(ap, str); + if (!cond) { + printf("FAIL: %s\n ", cond_str); + vprintf(str, ap); + } else { + printf("ok: %s\n", cond_str); + } + va_end(ap); +} diff --git a/tests/testdatabase.c b/tests/testdatabase.c new file mode 100644 index 0000000..3b2d316 --- /dev/null +++ b/tests/testdatabase.c @@ -0,0 +1,9380 @@ +/* + * Copyright (C) 2005 Mike McCormack for CodeWeavers + * + * A test program for MSI database files. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include <stdio.h> + +#include <windows.h> +#include <libmsi.h> + +#include <objidl.h> + +#include "test.h" + +static const char *msifile = "winetest-db.msi"; +static const char *msifile2 = "winetst2-db.msi"; +static const char *mstfile = "winetst-db.mst"; +static const WCHAR msifileW[] = {'w','i','n','e','t','e','s','t','-','d','b','.','m','s','i',0}; + +static void test_msidatabase(void) +{ + MSIHANDLE hdb = 0, hdb2 = 0; + UINT res; + + DeleteFile(msifile); + + res = MsiOpenDatabase( msifile, msifile2, &hdb ); + ok( res == ERROR_OPEN_FAILED, "expected failure\n"); + + res = MsiOpenDatabase( msifile, (LPSTR) 0xff, &hdb ); + ok( res == ERROR_INVALID_PARAMETER, "expected failure\n"); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + /* create an empty database */ + res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to create database\n" ); + + res = MsiDatabaseCommit( hdb ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + res = MsiOpenDatabase( msifile, msifile2, &hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 ), "database should exist\n"); + + res = MsiDatabaseCommit( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + res = MsiCloseHandle( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiOpenDatabase( msifile, msifile2, &hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiCloseHandle( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + ok( INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile2 ), "uncommitted database should not exist\n"); + + res = MsiOpenDatabase( msifile, msifile2, &hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiDatabaseCommit( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + res = MsiCloseHandle( hdb2 ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 ), "committed database should exist\n"); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiDatabaseCommit( hdb ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_DIRECT, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_TRANSACT, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + + /* MSIDBOPEN_CREATE deletes the database if MsiCommitDatabase isn't called */ + res = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + ok( INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile ), "database should exist\n"); + + res = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to open database\n" ); + + res = MsiDatabaseCommit( hdb ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile ), "database should exist\n"); + + res = MsiCloseHandle( hdb ); + ok( res == ERROR_SUCCESS , "Failed to close database\n" ); + + res = DeleteFile( msifile2 ); + ok( res == TRUE, "Failed to delete database\n" ); + + res = DeleteFile( msifile ); + ok( res == TRUE, "Failed to delete database\n" ); +} + +static UINT do_query(MSIHANDLE hdb, const char *query, MSIHANDLE *phrec) +{ + MSIHANDLE hview = 0; + UINT r, ret; + + if (phrec) + *phrec = 0; + + /* open a select query */ + r = MsiDatabaseOpenView(hdb, query, &hview); + if (r != ERROR_SUCCESS) + return r; + r = MsiViewExecute(hview, 0); + if (r != ERROR_SUCCESS) + return r; + ret = MsiViewFetch(hview, phrec); + r = MsiViewClose(hview); + if (r != ERROR_SUCCESS) + return r; + r = MsiCloseHandle(hview); + if (r != ERROR_SUCCESS) + return r; + return ret; +} + +static UINT run_query( MSIHANDLE hdb, MSIHANDLE hrec, const char *query ) +{ + MSIHANDLE hview = 0; + UINT r; + + r = MsiDatabaseOpenView(hdb, query, &hview); + if( r != ERROR_SUCCESS ) + return r; + + r = MsiViewExecute(hview, hrec); + if( r == ERROR_SUCCESS ) + r = MsiViewClose(hview); + MsiCloseHandle(hview); + return r; +} + +static UINT run_queryW( MSIHANDLE hdb, MSIHANDLE hrec, const WCHAR *query ) +{ + MSIHANDLE hview = 0; + UINT r; + + r = MsiDatabaseOpenViewW(hdb, query, &hview); + if( r != ERROR_SUCCESS ) + return r; + + r = MsiViewExecute(hview, hrec); + if( r == ERROR_SUCCESS ) + r = MsiViewClose(hview); + MsiCloseHandle(hview); + return r; +} + +static UINT create_component_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `Component` ( " + "`Component` CHAR(72) NOT NULL, " + "`ComponentId` CHAR(38), " + "`Directory_` CHAR(72) NOT NULL, " + "`Attributes` SHORT NOT NULL, " + "`Condition` CHAR(255), " + "`KeyPath` CHAR(72) " + "PRIMARY KEY `Component`)" ); +} + +static UINT create_custom_action_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `CustomAction` ( " + "`Action` CHAR(72) NOT NULL, " + "`Type` SHORT NOT NULL, " + "`Source` CHAR(72), " + "`Target` CHAR(255) " + "PRIMARY KEY `Action`)" ); +} + +static UINT create_directory_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `Directory` ( " + "`Directory` CHAR(255) NOT NULL, " + "`Directory_Parent` CHAR(255), " + "`DefaultDir` CHAR(255) NOT NULL " + "PRIMARY KEY `Directory`)" ); +} + +static UINT create_feature_components_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `FeatureComponents` ( " + "`Feature_` CHAR(38) NOT NULL, " + "`Component_` CHAR(72) NOT NULL " + "PRIMARY KEY `Feature_`, `Component_` )" ); +} + +static UINT create_std_dlls_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `StdDlls` ( " + "`File` CHAR(255) NOT NULL, " + "`Binary_` CHAR(72) NOT NULL " + "PRIMARY KEY `File` )" ); +} + +static UINT create_binary_table( MSIHANDLE hdb ) +{ + return run_query( hdb, 0, + "CREATE TABLE `Binary` ( " + "`Name` CHAR(72) NOT NULL, " + "`Data` CHAR(72) NOT NULL " + "PRIMARY KEY `Name` )" ); +} + +#define make_add_entry(type, qtext) \ + static UINT add##_##type##_##entry( MSIHANDLE hdb, const char *values ) \ + { \ + char insert[] = qtext; \ + char *query; \ + UINT sz, r; \ + sz = strlen(values) + sizeof insert; \ + query = HeapAlloc(GetProcessHeap(),0,sz); \ + sprintf(query,insert,values); \ + r = run_query( hdb, 0, query ); \ + HeapFree(GetProcessHeap(), 0, query); \ + return r; \ + } + +make_add_entry(component, + "INSERT INTO `Component` " + "(`Component`, `ComponentId`, `Directory_`, " + "`Attributes`, `Condition`, `KeyPath`) VALUES( %s )") + +make_add_entry(custom_action, + "INSERT INTO `CustomAction` " + "(`Action`, `Type`, `Source`, `Target`) VALUES( %s )") + +make_add_entry(feature_components, + "INSERT INTO `FeatureComponents` " + "(`Feature_`, `Component_`) VALUES( %s )") + +make_add_entry(std_dlls, + "INSERT INTO `StdDlls` (`File`, `Binary_`) VALUES( %s )") + +make_add_entry(binary, + "INSERT INTO `Binary` (`Name`, `Data`) VALUES( %s )") + +static void test_msiinsert(void) +{ + MSIHANDLE hdb = 0, hview = 0, hview2 = 0, hrec = 0; + UINT r; + const char *query; + char buf[80]; + DWORD sz; + + DeleteFile(msifile); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + /* create a table */ + query = "CREATE TABLE `phone` ( " + "`id` INT, `name` CHAR(32), `number` CHAR(32) " + "PRIMARY KEY `id`)"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM phone WHERE number = '8675309'"; + r = MsiDatabaseOpenView(hdb, query, &hview2); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview2, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview2, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch produced items\n"); + + /* insert a value into it */ + query = "INSERT INTO `phone` ( `id`, `name`, `number` )" + "VALUES('1', 'Abe', '8675309')"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiViewFetch(hview2, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch produced items\n"); + r = MsiViewExecute(hview2, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview2, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %u\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + r = MsiViewClose(hview2); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview2); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone` WHERE `id` = 1"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + /* check the record contains what we put in it */ + r = MsiRecordGetFieldCount(hrec); + ok(r == 3, "record count wrong\n"); + + r = MsiRecordIsNull(hrec, 0); + ok(r == FALSE, "field 0 not null\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "field 1 contents wrong\n"); + sz = sizeof buf; + r = MsiRecordGetString(hrec, 2, buf, &sz); + ok(r == ERROR_SUCCESS, "field 2 content fetch failed\n"); + ok(!strcmp(buf,"Abe"), "field 2 content incorrect\n"); + sz = sizeof buf; + r = MsiRecordGetString(hrec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "field 3 content fetch failed\n"); + ok(!strcmp(buf,"8675309"), "field 3 content incorrect\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* open a select query */ + hrec = 100; + query = "SELECT * FROM `phone` WHERE `id` >= 10"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n"); + ok(hrec == 0, "hrec should be null\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone` WHERE `id` < 0"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n"); + + query = "SELECT * FROM `phone` WHERE `id` <= 0"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n"); + + query = "SELECT * FROM `phone` WHERE `id` <> 1"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n"); + + query = "SELECT * FROM `phone` WHERE `id` > 10"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n"); + + /* now try a few bad INSERT xqueries */ + query = "INSERT INTO `phone` ( `id`, `name`, `number` )" + "VALUES(?, ?)"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_BAD_QUERY_SYNTAX, "MsiDatabaseOpenView failed\n"); + + /* construct a record to insert */ + hrec = MsiCreateRecord(4); + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n"); + r = MsiRecordSetString(hrec, 2, "Adam"); + ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n"); + r = MsiRecordSetString(hrec, 3, "96905305"); + ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n"); + + /* insert another value, using a record and wildcards */ + query = "INSERT INTO `phone` ( `id`, `name`, `number` )" + "VALUES(?, ?, ?)"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + if (r == ERROR_SUCCESS) + { + r = MsiViewExecute(hview, hrec); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + } + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiViewFetch(0, NULL); + ok(r == ERROR_INVALID_PARAMETER, "MsiViewFetch failed\n"); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = DeleteFile(msifile); + ok(r == TRUE, "file didn't exist after commit\n"); +} + +static UINT try_query_param( MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE hrec ) +{ + MSIHANDLE htab = 0; + UINT res; + + res = MsiDatabaseOpenView( hdb, szQuery, &htab ); + if(res == ERROR_SUCCESS ) + { + UINT r; + + r = MsiViewExecute( htab, hrec ); + if(r != ERROR_SUCCESS ) + res = r; + + r = MsiViewClose( htab ); + if(r != ERROR_SUCCESS ) + res = r; + + r = MsiCloseHandle( htab ); + if(r != ERROR_SUCCESS ) + res = r; + } + return res; +} + +static UINT try_query( MSIHANDLE hdb, LPCSTR szQuery ) +{ + return try_query_param( hdb, szQuery, 0 ); +} + +static UINT try_insert_query( MSIHANDLE hdb, LPCSTR szQuery ) +{ + MSIHANDLE hrec = 0; + UINT r; + + hrec = MsiCreateRecord( 1 ); + MsiRecordSetString( hrec, 1, "Hello"); + + r = try_query_param( hdb, szQuery, hrec ); + + MsiCloseHandle( hrec ); + return r; +} + +static void test_msibadqueries(void) +{ + MSIHANDLE hdb = 0; + UINT r; + + DeleteFile(msifile); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + r = MsiDatabaseCommit( hdb ); + ok(r == ERROR_SUCCESS , "Failed to commit database\n"); + + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS , "Failed to close database\n"); + + /* open it readonly */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb ); + ok(r == ERROR_SUCCESS , "Failed to open database r/o\n"); + + /* add a table to it */ + r = try_query( hdb, "select * from _Tables"); + ok(r == ERROR_SUCCESS , "query 1 failed\n"); + + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS , "Failed to close database r/o\n"); + + /* open it read/write */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb ); + ok(r == ERROR_SUCCESS , "Failed to open database r/w\n"); + + /* a bunch of test queries that fail with the native MSI */ + + r = try_query( hdb, "CREATE TABLE"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2a return code\n"); + + r = try_query( hdb, "CREATE TABLE `a`"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2b return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` ()"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2c return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b`)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2d return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) )"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2e return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2f return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2g return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2h return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2i return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY 'b')"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2j return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2k return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b')"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2l return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHA(72) NOT NULL PRIMARY KEY `b`)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2m return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(-1) NOT NULL PRIMARY KEY `b`)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2n return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(720) NOT NULL PRIMARY KEY `b`)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2o return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL KEY `b`)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`` CHAR(72) NOT NULL PRIMARY KEY `b`)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "invalid query 2p return code\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)"); + ok(r == ERROR_SUCCESS , "valid query 2z failed\n"); + + r = try_query( hdb, "CREATE TABLE `a` (`b` CHAR(72) NOT NULL PRIMARY KEY `b`)"); + ok(r == ERROR_BAD_QUERY_SYNTAX , "created same table again\n"); + + r = try_query( hdb, "CREATE TABLE `aa` (`b` CHAR(72) NOT NULL, `c` " + "CHAR(72), `d` CHAR(255) NOT NULL LOCALIZABLE PRIMARY KEY `b`)"); + ok(r == ERROR_SUCCESS , "query 4 failed\n"); + + r = MsiDatabaseCommit( hdb ); + ok(r == ERROR_SUCCESS , "Failed to commit database after write\n"); + + r = try_query( hdb, "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL " + "PRIMARY KEY `foo`)"); + ok(r == ERROR_SUCCESS , "query 4 failed\n"); + + r = try_insert_query( hdb, "insert into a ( `b` ) VALUES ( ? )"); + ok(r == ERROR_SUCCESS , "failed to insert record in db\n"); + + r = MsiDatabaseCommit( hdb ); + ok(r == ERROR_SUCCESS , "Failed to commit database after write\n"); + + r = try_query( hdb, "CREATE TABLE `boo` (`foo` CHAR(72) NOT NULL " + "PRIMARY KEY `ba`)"); + ok(r != ERROR_SUCCESS , "query 5 succeeded\n"); + + r = try_query( hdb,"CREATE TABLE `bee` (`foo` CHAR(72) NOT NULL )"); + ok(r != ERROR_SUCCESS , "query 6 succeeded\n"); + + r = try_query( hdb, "CREATE TABLE `temp` (`t` CHAR(72) NOT NULL " + "PRIMARY KEY `t`)"); + ok(r == ERROR_SUCCESS , "query 7 failed\n"); + + r = try_query( hdb, "CREATE TABLE `c` (`b` CHAR NOT NULL PRIMARY KEY `b`)"); + ok(r == ERROR_SUCCESS , "query 8 failed\n"); + + r = try_query( hdb, "select * from c"); + ok(r == ERROR_SUCCESS , "query failed\n"); + + r = try_query( hdb, "select * from c where b = 'x"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from c where b = 'x'"); + ok(r == ERROR_SUCCESS, "query failed\n"); + + r = try_query( hdb, "select * from 'c'"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from ''"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from c where b = x"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from c where b = \"x\""); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select * from c where b = 'x'"); + ok(r == ERROR_SUCCESS, "query failed\n"); + + r = try_query( hdb, "select * from c where b = '\"x'"); + ok(r == ERROR_SUCCESS, "query failed\n"); + + if (0) /* FIXME: this query causes trouble with other tests */ + { + r = try_query( hdb, "select * from c where b = '\\\'x'"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + } + + r = try_query( hdb, "select * from 'c'"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n"); + + r = try_query( hdb, "select `c`.`b` from `c` order by `c`.`order`"); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "select `c`.b` from `c`"); + ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "select `c`.`b from `c`"); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "select `c`.b from `c`"); + ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "select `c.`b` from `c`"); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "select c`.`b` from `c`"); + ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "select c.`b` from `c`"); + ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "select `c`.`b` from c`"); + ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "select `c`.`b` from `c"); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "select `c`.`b` from c"); + ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "CREATE TABLE `\5a` (`b` CHAR NOT NULL PRIMARY KEY `b`)" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT * FROM \5a" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "CREATE TABLE `a\5` (`b` CHAR NOT NULL PRIMARY KEY `b`)" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT * FROM a\5" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "CREATE TABLE `-a` (`b` CHAR NOT NULL PRIMARY KEY `b`)" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT * FROM -a" ); + todo_wine ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "CREATE TABLE `a-` (`b` CHAR NOT NULL PRIMARY KEY `b`)" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT * FROM a-" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS , "Failed to close database transact\n"); + + r = DeleteFile( msifile ); + ok(r == TRUE, "file didn't exist after commit\n"); +} + +static void test_viewmodify(void) +{ + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + UINT r; + MSIDBERROR err; + const char *query; + char buffer[0x100]; + DWORD sz; + + DeleteFile(msifile); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `phone` ( " + "`id` INT, `name` CHAR(32), `number` CHAR(32) " + "PRIMARY KEY `id`)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + query = "CREATE TABLE `_Validation` ( " + "`Table` CHAR(32) NOT NULL, `Column` CHAR(32) NOT NULL, " + "`Nullable` CHAR(4) NOT NULL, `MinValue` INT, `MaxValue` INT, " + "`KeyTable` CHAR(255), `KeyColumn` SHORT, `Category` CHAR(32), " + "`Set` CHAR(255), `Description` CHAR(255) PRIMARY KEY `Table`, `Column`)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + query = "INSERT INTO `_Validation` ( `Table`, `Column`, `Nullable` ) " + "VALUES('phone', 'id', 'N')"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + /* check what the error function reports without doing anything */ + sz = 0; + /* passing NULL as the 3rd param make function to crash on older platforms */ + err = MsiViewGetError( 0, NULL, &sz ); + ok(err == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n"); + + /* open a view */ + query = "SELECT * FROM `phone`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + /* see what happens with a good hview and bad args */ + err = MsiViewGetError( hview, NULL, NULL ); + ok(err == MSIDBERROR_INVALIDARG || err == MSIDBERROR_NOERROR, + "MsiViewGetError returns %u (expected -3)\n", err); + err = MsiViewGetError( hview, buffer, NULL ); + ok(err == MSIDBERROR_INVALIDARG, "MsiViewGetError return\n"); + + /* see what happens with a zero length buffer */ + sz = 0; + buffer[0] = 'x'; + err = MsiViewGetError( hview, buffer, &sz ); + ok(err == MSIDBERROR_MOREDATA, "MsiViewGetError return\n"); + ok(buffer[0] == 'x', "buffer cleared\n"); + ok(sz == 0, "size not zero\n"); + + /* ok this one is strange */ + sz = 0; + err = MsiViewGetError( hview, NULL, &sz ); + ok(err == MSIDBERROR_NOERROR, "MsiViewGetError return\n"); + ok(sz == 0, "size not zero\n"); + + /* see if it really has an error */ + sz = sizeof buffer; + buffer[0] = 'x'; + err = MsiViewGetError( hview, buffer, &sz ); + ok(err == MSIDBERROR_NOERROR, "MsiViewGetError return\n"); + ok(buffer[0] == 0, "buffer not cleared\n"); + ok(sz == 0, "size not zero\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + /* try some invalid records */ + r = MsiViewModify(hview, MSIMODIFY_INSERT, 0 ); + ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n"); + r = MsiViewModify(hview, -1, 0 ); + ok(r == ERROR_INVALID_HANDLE, "MsiViewModify failed\n"); + + /* try an small record */ + hrec = MsiCreateRecord(1); + r = MsiViewModify(hview, -1, hrec ); + ok(r == ERROR_INVALID_DATA, "MsiViewModify failed\n"); + + sz = sizeof buffer; + buffer[0] = 'x'; + err = MsiViewGetError( hview, buffer, &sz ); + ok(err == MSIDBERROR_NOERROR, "MsiViewGetError return\n"); + ok(buffer[0] == 0, "buffer not cleared\n"); + ok(sz == 0, "size not zero\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + /* insert a valid record */ + hrec = MsiCreateRecord(3); + + r = MsiRecordSetInteger(hrec, 1, 1); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "bob"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "7654321"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec ); + ok(r == ERROR_SUCCESS, "MsiViewModify failed\n"); + + /* validate it */ + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewModify(hview, MSIMODIFY_VALIDATE_NEW, hrec ); + ok(r == ERROR_INVALID_DATA, "MsiViewModify failed %u\n", r); + + sz = sizeof buffer; + buffer[0] = 'x'; + err = MsiViewGetError( hview, buffer, &sz ); + ok(err == MSIDBERROR_DUPLICATEKEY, "MsiViewGetError returned %u\n", err); + ok(!strcmp(buffer, "id"), "expected \"id\" c, got \"%s\"\n", buffer); + ok(sz == 2, "size not 2\n"); + + /* insert the same thing again */ + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + /* should fail ... */ + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec ); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + /* try to merge the same record */ + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewModify(hview, MSIMODIFY_MERGE, hrec ); + ok(r == ERROR_SUCCESS, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + /* try merging a new record */ + hrec = MsiCreateRecord(3); + + r = MsiRecordSetInteger(hrec, 1, 10); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "pepe"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "7654321"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_MERGE, hrec ); + ok(r == ERROR_SUCCESS, "MsiViewModify failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "bob"), "Expected bob, got %s\n", buffer); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "7654321"), "Expected 7654321, got %s\n", buffer); + + /* update the view, non-primary key */ + r = MsiRecordSetString(hrec, 3, "3141592"); + ok(r == ERROR_SUCCESS, "MsiRecordSetString failed\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed\n"); + + /* do it again */ + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed: %d\n", r); + + /* update the view, primary key */ + r = MsiRecordSetInteger(hrec, 1, 5); + ok(r == ERROR_SUCCESS, "MsiRecordSetInteger failed\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "bob"), "Expected bob, got %s\n", buffer); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "MsiRecordGetString failed\n"); + ok(!lstrcmp(buffer, "3141592"), "Expected 3141592, got %s\n", buffer); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + /* use a record that doesn't come from a view fetch */ + hrec = MsiCreateRecord(3); + ok(hrec != 0, "MsiCreateRecord failed\n"); + + r = MsiRecordSetInteger(hrec, 1, 3); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jane"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "112358"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + /* use a record that doesn't come from a view fetch, primary key matches */ + hrec = MsiCreateRecord(3); + ok(hrec != 0, "MsiCreateRecord failed\n"); + + r = MsiRecordSetInteger(hrec, 1, 1); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jane"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "112358"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + hrec = MsiCreateRecord(3); + + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "nick"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + r = MsiRecordSetString(hrec, 3, "141421"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec ); + ok(r == ERROR_SUCCESS, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `phone` WHERE `id` = 1"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + /* change the id to match the second row */ + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jerry"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* broader search */ + query = "SELECT * FROM `phone` ORDER BY `id`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + /* change the id to match the second row */ + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetString(hrec, 2, "jerry"); + ok(r == ERROR_SUCCESS, "failed to set string\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_FUNCTION_FAILED, "MsiViewModify failed\n"); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n"); +} + +static MSIHANDLE create_db(void) +{ + MSIHANDLE hdb = 0; + UINT res; + + DeleteFile(msifile); + + /* create an empty database */ + res = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to create database\n" ); + if( res != ERROR_SUCCESS ) + return hdb; + + res = MsiDatabaseCommit( hdb ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + return hdb; +} + +static void test_getcolinfo(void) +{ + MSIHANDLE hdb, hview = 0, rec = 0; + UINT r; + DWORD sz; + char buffer[0x20]; + + /* create an empty db */ + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + /* tables should be present */ + r = MsiDatabaseOpenView(hdb, "select * from _Tables", &hview); + ok( r == ERROR_SUCCESS, "failed to open query\n"); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute query\n"); + + /* check that NAMES works */ + rec = 0; + r = MsiViewGetColumnInfo( hview, MSICOLINFO_NAMES, &rec ); + ok( r == ERROR_SUCCESS, "failed to get names\n"); + sz = sizeof buffer; + r = MsiRecordGetString(rec, 1, buffer, &sz ); + ok( r == ERROR_SUCCESS, "failed to get string\n"); + ok( !strcmp(buffer,"Name"), "_Tables has wrong column name\n"); + r = MsiCloseHandle( rec ); + ok( r == ERROR_SUCCESS, "failed to close record handle\n"); + + /* check that TYPES works */ + rec = 0; + r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, &rec ); + ok( r == ERROR_SUCCESS, "failed to get names\n"); + sz = sizeof buffer; + r = MsiRecordGetString(rec, 1, buffer, &sz ); + ok( r == ERROR_SUCCESS, "failed to get string\n"); + ok( !strcmp(buffer,"s64"), "_Tables has wrong column type\n"); + r = MsiCloseHandle( rec ); + ok( r == ERROR_SUCCESS, "failed to close record handle\n"); + + /* check that invalid values fail */ + rec = 0; + r = MsiViewGetColumnInfo( hview, 100, &rec ); + ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n"); + ok( rec == 0, "returned a record\n"); + + r = MsiViewGetColumnInfo( hview, MSICOLINFO_TYPES, NULL ); + ok( r == ERROR_INVALID_PARAMETER, "wrong error code\n"); + + r = MsiViewGetColumnInfo( 0, MSICOLINFO_TYPES, &rec ); + ok( r == ERROR_INVALID_HANDLE, "wrong error code\n"); + + r = MsiViewClose(hview); + ok( r == ERROR_SUCCESS, "failed to close view\n"); + r = MsiCloseHandle(hview); + ok( r == ERROR_SUCCESS, "failed to close view handle\n"); + r = MsiCloseHandle(hdb); + ok( r == ERROR_SUCCESS, "failed to close database\n"); +} + +static MSIHANDLE get_column_info(MSIHANDLE hdb, const char *query, MSICOLINFO type) +{ + MSIHANDLE hview = 0, rec = 0; + UINT r; + + r = MsiDatabaseOpenView(hdb, query, &hview); + if( r != ERROR_SUCCESS ) + return r; + + r = MsiViewExecute(hview, 0); + if( r == ERROR_SUCCESS ) + { + MsiViewGetColumnInfo( hview, type, &rec ); + } + MsiViewClose(hview); + MsiCloseHandle(hview); + return rec; +} + +static UINT get_columns_table_type(MSIHANDLE hdb, const char *table, UINT field) +{ + MSIHANDLE hview = 0, rec = 0; + UINT r, type = 0; + char query[0x100]; + + sprintf(query, "select * from `_Columns` where `Table` = '%s'", table ); + + r = MsiDatabaseOpenView(hdb, query, &hview); + if( r != ERROR_SUCCESS ) + return r; + + r = MsiViewExecute(hview, 0); + if( r == ERROR_SUCCESS ) + { + while (1) + { + r = MsiViewFetch( hview, &rec ); + if( r != ERROR_SUCCESS) + break; + r = MsiRecordGetInteger( rec, 2 ); + if (r == field) + type = MsiRecordGetInteger( rec, 4 ); + MsiCloseHandle( rec ); + } + } + MsiViewClose(hview); + MsiCloseHandle(hview); + return type; +} + +static BOOL check_record( MSIHANDLE rec, UINT field, LPCSTR val ) +{ + CHAR buffer[0x20]; + UINT r; + DWORD sz; + + sz = sizeof buffer; + r = MsiRecordGetString( rec, field, buffer, &sz ); + return (r == ERROR_SUCCESS ) && !strcmp(val, buffer); +} + +static void test_viewgetcolumninfo(void) +{ + MSIHANDLE hdb = 0, rec; + UINT r; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + r = run_query( hdb, 0, + "CREATE TABLE `Properties` " + "( `Property` CHAR(255), " + " `Value` CHAR(1), " + " `Intvalue` INT, " + " `Integervalue` INTEGER, " + " `Shortvalue` SHORT, " + " `Longvalue` LONG, " + " `Longcharvalue` LONGCHAR " + " PRIMARY KEY `Property`)" ); + ok( r == ERROR_SUCCESS , "Failed to create table\n" ); + + /* check the column types */ + rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_TYPES ); + ok( rec, "failed to get column info record\n" ); + + ok( check_record( rec, 1, "S255"), "wrong record type\n"); + ok( check_record( rec, 2, "S1"), "wrong record type\n"); + ok( check_record( rec, 3, "I2"), "wrong record type\n"); + ok( check_record( rec, 4, "I2"), "wrong record type\n"); + ok( check_record( rec, 5, "I2"), "wrong record type\n"); + ok( check_record( rec, 6, "I4"), "wrong record type\n"); + ok( check_record( rec, 7, "S0"), "wrong record type\n"); + + MsiCloseHandle( rec ); + + /* check the type in _Columns */ + ok( 0x3dff == get_columns_table_type(hdb, "Properties", 1 ), "_columns table wrong\n"); + ok( 0x1d01 == get_columns_table_type(hdb, "Properties", 2 ), "_columns table wrong\n"); + ok( 0x1502 == get_columns_table_type(hdb, "Properties", 3 ), "_columns table wrong\n"); + ok( 0x1502 == get_columns_table_type(hdb, "Properties", 4 ), "_columns table wrong\n"); + ok( 0x1502 == get_columns_table_type(hdb, "Properties", 5 ), "_columns table wrong\n"); + ok( 0x1104 == get_columns_table_type(hdb, "Properties", 6 ), "_columns table wrong\n"); + ok( 0x1d00 == get_columns_table_type(hdb, "Properties", 7 ), "_columns table wrong\n"); + + /* now try the names */ + rec = get_column_info( hdb, "select * from `Properties`", MSICOLINFO_NAMES ); + ok( rec, "failed to get column info record\n" ); + + ok( check_record( rec, 1, "Property"), "wrong record type\n"); + ok( check_record( rec, 2, "Value"), "wrong record type\n"); + ok( check_record( rec, 3, "Intvalue"), "wrong record type\n"); + ok( check_record( rec, 4, "Integervalue"), "wrong record type\n"); + ok( check_record( rec, 5, "Shortvalue"), "wrong record type\n"); + ok( check_record( rec, 6, "Longvalue"), "wrong record type\n"); + ok( check_record( rec, 7, "Longcharvalue"), "wrong record type\n"); + + MsiCloseHandle( rec ); + + r = run_query( hdb, 0, + "CREATE TABLE `Binary` " + "( `Name` CHAR(255), `Data` OBJECT PRIMARY KEY `Name`)" ); + ok( r == ERROR_SUCCESS , "Failed to create table\n" ); + + /* check the column types */ + rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_TYPES ); + ok( rec, "failed to get column info record\n" ); + + ok( check_record( rec, 1, "S255"), "wrong record type\n"); + ok( check_record( rec, 2, "V0"), "wrong record type\n"); + + MsiCloseHandle( rec ); + + /* check the type in _Columns */ + ok( 0x3dff == get_columns_table_type(hdb, "Binary", 1 ), "_columns table wrong\n"); + ok( 0x1900 == get_columns_table_type(hdb, "Binary", 2 ), "_columns table wrong\n"); + + /* now try the names */ + rec = get_column_info( hdb, "select * from `Binary`", MSICOLINFO_NAMES ); + ok( rec, "failed to get column info record\n" ); + + ok( check_record( rec, 1, "Name"), "wrong record type\n"); + ok( check_record( rec, 2, "Data"), "wrong record type\n"); + MsiCloseHandle( rec ); + + r = run_query( hdb, 0, + "CREATE TABLE `UIText` " + "( `Key` CHAR(72) NOT NULL, `Text` CHAR(255) LOCALIZABLE PRIMARY KEY `Key`)" ); + ok( r == ERROR_SUCCESS , "Failed to create table\n" ); + + ok( 0x2d48 == get_columns_table_type(hdb, "UIText", 1 ), "_columns table wrong\n"); + ok( 0x1fff == get_columns_table_type(hdb, "UIText", 2 ), "_columns table wrong\n"); + + rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_NAMES ); + ok( rec, "failed to get column info record\n" ); + ok( check_record( rec, 1, "Key"), "wrong record type\n"); + ok( check_record( rec, 2, "Text"), "wrong record type\n"); + MsiCloseHandle( rec ); + + rec = get_column_info( hdb, "select * from `UIText`", MSICOLINFO_TYPES ); + ok( rec, "failed to get column info record\n" ); + ok( check_record( rec, 1, "s72"), "wrong record type\n"); + ok( check_record( rec, 2, "L255"), "wrong record type\n"); + MsiCloseHandle( rec ); + + MsiCloseHandle( hdb ); +} + +static void test_msiexport(void) +{ + MSIHANDLE hdb = 0, hview = 0; + UINT r; + const char *query; + char path[MAX_PATH]; + const char file[] = "phone.txt"; + HANDLE handle; + char buffer[0x100]; + DWORD length; + const char expected[] = + "id\tname\tnumber\r\n" + "I2\tS32\tS32\r\n" + "phone\tid\r\n" + "1\tAbe\t8675309\r\n"; + + DeleteFile(msifile); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + /* create a table */ + query = "CREATE TABLE `phone` ( " + "`id` INT, `name` CHAR(32), `number` CHAR(32) " + "PRIMARY KEY `id`)"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* insert a value into it */ + query = "INSERT INTO `phone` ( `id`, `name`, `number` )" + "VALUES('1', 'Abe', '8675309')"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + GetCurrentDirectory(MAX_PATH, path); + + r = MsiDatabaseExport(hdb, "phone", path, file); + ok(r == ERROR_SUCCESS, "MsiDatabaseExport failed\n"); + + MsiCloseHandle(hdb); + + lstrcat(path, "\\"); + lstrcat(path, file); + + /* check the data that was written */ + length = 0; + memset(buffer, 0, sizeof buffer); + handle = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) + { + ReadFile(handle, buffer, sizeof buffer, &length, NULL); + CloseHandle(handle); + DeleteFile(path); + } + else + ok(0, "failed to open file %s\n", path); + + ok( length == strlen(expected), "length of data wrong\n"); + ok( !lstrcmp(buffer, expected), "data doesn't match\n"); + DeleteFile(msifile); +} + +static void test_longstrings(void) +{ + const char insert_query[] = + "INSERT INTO `strings` ( `id`, `val` ) VALUES('1', 'Z')"; + char *str; + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + DWORD len; + UINT r; + const DWORD STRING_LENGTH = 0x10005; + + DeleteFile(msifile); + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + /* create a table */ + r = try_query( hdb, + "CREATE TABLE `strings` ( `id` INT, `val` CHAR(0) PRIMARY KEY `id`)"); + ok(r == ERROR_SUCCESS, "query failed\n"); + + /* try a insert a very long string */ + str = HeapAlloc(GetProcessHeap(), 0, STRING_LENGTH+sizeof insert_query); + len = strchr(insert_query, 'Z') - insert_query; + strcpy(str, insert_query); + memset(str+len, 'Z', STRING_LENGTH); + strcpy(str+len+STRING_LENGTH, insert_query+len+1); + r = try_query( hdb, str ); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + HeapFree(GetProcessHeap(), 0, str); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n"); + MsiCloseHandle(hdb); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + r = MsiDatabaseOpenView(hdb, "select * from `strings` where `id` = 1", &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + r = MsiRecordGetString(hrec, 2, NULL, &len); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + ok(len == STRING_LENGTH, "string length wrong\n"); + + MsiCloseHandle(hrec); + MsiCloseHandle(hdb); + DeleteFile(msifile); +} + +static void create_file_data(LPCSTR name, LPCSTR data, DWORD size) +{ + HANDLE file; + DWORD written; + + file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL); + if (file == INVALID_HANDLE_VALUE) + return; + + WriteFile(file, data, strlen(data), &written, NULL); + WriteFile(file, "\n", strlen("\n"), &written, NULL); + + if (size) + { + SetFilePointer(file, size, NULL, FILE_BEGIN); + SetEndOfFile(file); + } + + CloseHandle(file); +} + +#define create_file(name) create_file_data(name, name, 0) + +static void test_streamtable(void) +{ + MSIHANDLE hdb = 0, rec, view, hsi; + char file[MAX_PATH]; + char buf[MAX_PATH]; + DWORD size; + UINT r; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + r = run_query( hdb, 0, + "CREATE TABLE `Properties` " + "( `Property` CHAR(255), `Value` CHAR(1) PRIMARY KEY `Property`)" ); + ok( r == ERROR_SUCCESS , "Failed to create table\n" ); + + r = run_query( hdb, 0, + "INSERT INTO `Properties` " + "( `Value`, `Property` ) VALUES ( 'Prop', 'value' )" ); + ok( r == ERROR_SUCCESS, "Failed to add to table\n" ); + + r = MsiDatabaseCommit( hdb ); + ok( r == ERROR_SUCCESS , "Failed to commit database\n" ); + + MsiCloseHandle( hdb ); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb ); + ok( r == ERROR_SUCCESS , "Failed to open database\n" ); + + /* check the column types */ + rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_TYPES ); + ok( rec, "failed to get column info record\n" ); + + ok( check_record( rec, 1, "s62"), "wrong record type\n"); + ok( check_record( rec, 2, "V0"), "wrong record type\n"); + + MsiCloseHandle( rec ); + + /* now try the names */ + rec = get_column_info( hdb, "select * from `_Streams`", MSICOLINFO_NAMES ); + ok( rec, "failed to get column info record\n" ); + + ok( check_record( rec, 1, "Name"), "wrong record type\n"); + ok( check_record( rec, 2, "Data"), "wrong record type\n"); + + MsiCloseHandle( rec ); + + r = MsiDatabaseOpenView( hdb, + "SELECT * FROM `_Streams` WHERE `Name` = '\5SummaryInformation'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "Unexpected result: %u\n", r ); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + /* create a summary information stream */ + r = MsiGetSummaryInformationA( hdb, NULL, 1, &hsi ); + ok( r == ERROR_SUCCESS, "Failed to get summary information handle: %u\n", r ); + + r = MsiSummaryInfoSetPropertyA( hsi, MSI_PID_SECURITY, VT_I4, 2, NULL, NULL ); + ok( r == ERROR_SUCCESS, "Failed to set property: %u\n", r ); + + r = MsiSummaryInfoPersist( hsi ); + ok( r == ERROR_SUCCESS, "Failed to save summary information: %u\n", r ); + + MsiCloseHandle( hsi ); + + r = MsiDatabaseOpenView( hdb, + "SELECT * FROM `_Streams` WHERE `Name` = '\5SummaryInformation'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "Unexpected result: %u\n", r ); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + /* insert a file into the _Streams table */ + create_file( "test.txt" ); + + rec = MsiCreateRecord( 2 ); + MsiRecordSetString( rec, 1, "data" ); + + r = MsiRecordSetStream( rec, 2, "test.txt" ); + ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r); + + DeleteFile("test.txt"); + + r = MsiDatabaseOpenView( hdb, + "INSERT INTO `_Streams` ( `Name`, `Data` ) VALUES ( ?, ? )", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, rec ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + /* insert another one */ + create_file( "test1.txt" ); + + rec = MsiCreateRecord( 2 ); + MsiRecordSetString( rec, 1, "data1" ); + + r = MsiRecordSetStream( rec, 2, "test1.txt" ); + ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r); + + DeleteFile("test1.txt"); + + r = MsiDatabaseOpenView( hdb, + "INSERT INTO `_Streams` ( `Name`, `Data` ) VALUES ( ?, ? )", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, rec ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, + "SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = 'data'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "Failed to fetch record: %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok( !lstrcmp(file, "data"), "Expected 'data', got %s\n", file); + + size = MAX_PATH; + memset(buf, 0, MAX_PATH); + r = MsiRecordReadStream( rec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, + "SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = 'data1'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok( !lstrcmp(file, "data1"), "Expected 'data1', got %s\n", file); + + size = MAX_PATH; + memset(buf, 0, MAX_PATH); + r = MsiRecordReadStream( rec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + ok( !lstrcmp(buf, "test1.txt\n"), "Expected 'test1.txt\\n', got %s\n", buf); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + /* perform an update */ + create_file( "test2.txt" ); + rec = MsiCreateRecord( 1 ); + + r = MsiRecordSetStream( rec, 1, "test2.txt" ); + ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r); + + DeleteFile("test2.txt"); + + r = MsiDatabaseOpenView( hdb, + "UPDATE `_Streams` SET `Data` = ? WHERE `Name` = 'data1'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, rec ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, + "SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = 'data1'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "Failed to fetch record: %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok( !lstrcmp(file, "data1"), "Expected 'data1', got %s\n", file); + + size = MAX_PATH; + memset(buf, 0, MAX_PATH); + r = MsiRecordReadStream( rec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + todo_wine ok( !lstrcmp(buf, "test2.txt\n"), "Expected 'test2.txt\\n', got %s\n", buf); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + MsiCloseHandle( hdb ); + DeleteFile(msifile); +} + +static void test_binary(void) +{ + MSIHANDLE hdb = 0, rec; + char file[MAX_PATH]; + char buf[MAX_PATH]; + DWORD size; + LPCSTR query; + UINT r; + + /* insert a file into the Binary table */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb ); + ok( r == ERROR_SUCCESS , "Failed to open database\n" ); + + query = "CREATE TABLE `Binary` ( `Name` CHAR(72) NOT NULL, `ID` INT NOT NULL, `Data` OBJECT PRIMARY KEY `Name`, `ID`)"; + r = run_query( hdb, 0, query ); + ok( r == ERROR_SUCCESS, "Cannot create Binary table: %d\n", r ); + + create_file( "test.txt" ); + rec = MsiCreateRecord( 1 ); + r = MsiRecordSetStream( rec, 1, "test.txt" ); + ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r); + DeleteFile( "test.txt" ); + + query = "INSERT INTO `Binary` ( `Name`, `ID`, `Data` ) VALUES ( 'filename1', 1, ? )"; + r = run_query( hdb, rec, query ); + ok( r == ERROR_SUCCESS, "Insert into Binary table failed: %d\n", r ); + + r = MsiCloseHandle( rec ); + ok( r == ERROR_SUCCESS , "Failed to close record handle\n" ); + + r = MsiDatabaseCommit( hdb ); + ok( r == ERROR_SUCCESS , "Failed to commit database\n" ); + + r = MsiCloseHandle( hdb ); + ok( r == ERROR_SUCCESS , "Failed to close database\n" ); + + /* read file from the Stream table */ + r = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY, &hdb ); + ok( r == ERROR_SUCCESS , "Failed to open database\n" ); + + query = "SELECT * FROM `_Streams`"; + r = do_query( hdb, query, &rec ); + ok( r == ERROR_SUCCESS, "SELECT query failed: %d\n", r ); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r ); + ok( !lstrcmp(file, "Binary.filename1.1"), "Expected 'Binary.filename1.1', got %s\n", file ); + + size = MAX_PATH; + memset( buf, 0, MAX_PATH ); + r = MsiRecordReadStream( rec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r ); + ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf ); + + r = MsiCloseHandle( rec ); + ok( r == ERROR_SUCCESS , "Failed to close record handle\n" ); + + /* read file from the Binary table */ + query = "SELECT * FROM `Binary`"; + r = do_query( hdb, query, &rec ); + ok( r == ERROR_SUCCESS, "SELECT query failed: %d\n", r ); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r ); + ok( !lstrcmp(file, "filename1"), "Expected 'filename1', got %s\n", file ); + + size = MAX_PATH; + memset( buf, 0, MAX_PATH ); + r = MsiRecordReadStream( rec, 3, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r ); + ok( !lstrcmp(buf, "test.txt\n"), "Expected 'test.txt\\n', got %s\n", buf ); + + r = MsiCloseHandle( rec ); + ok( r == ERROR_SUCCESS , "Failed to close record handle\n" ); + + r = MsiCloseHandle( hdb ); + ok( r == ERROR_SUCCESS , "Failed to close database\n" ); + + DeleteFile( msifile ); +} + +static void test_where_not_in_selected(void) +{ + MSIHANDLE hdb = 0, rec, view; + LPCSTR query; + UINT r; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + r = run_query(hdb, 0, + "CREATE TABLE `IESTable` (" + "`Action` CHAR(64), " + "`Condition` CHAR(64), " + "`Sequence` LONG PRIMARY KEY `Sequence`)"); + ok( r == S_OK, "Cannot create IESTable table: %d\n", r); + + r = run_query(hdb, 0, + "CREATE TABLE `CATable` (" + "`Action` CHAR(64), " + "`Type` LONG PRIMARY KEY `Type`)"); + ok( r == S_OK, "Cannot create CATable table: %d\n", r); + + r = run_query(hdb, 0, "INSERT INTO `IESTable` " + "( `Action`, `Condition`, `Sequence`) " + "VALUES ( 'clean', 'cond4', 4)"); + ok( r == S_OK, "cannot add entry to IESTable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `IESTable` " + "( `Action`, `Condition`, `Sequence`) " + "VALUES ( 'depends', 'cond1', 1)"); + ok( r == S_OK, "cannot add entry to IESTable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `IESTable` " + "( `Action`, `Condition`, `Sequence`) " + "VALUES ( 'build', 'cond2', 2)"); + ok( r == S_OK, "cannot add entry to IESTable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `IESTable` " + "( `Action`, `Condition`, `Sequence`) " + "VALUES ( 'build2', 'cond6', 6)"); + ok( r == S_OK, "cannot add entry to IESTable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `IESTable` " + "( `Action`, `Condition`, `Sequence`) " + "VALUES ( 'build', 'cond3', 3)"); + ok(r == S_OK, "cannot add entry to IESTable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `CATable` " + "( `Action`, `Type` ) " + "VALUES ( 'build', 32)"); + ok(r == S_OK, "cannot add entry to CATable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `CATable` " + "( `Action`, `Type` ) " + "VALUES ( 'depends', 64)"); + ok(r == S_OK, "cannot add entry to CATable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `CATable` " + "( `Action`, `Type` ) " + "VALUES ( 'clean', 63)"); + ok(r == S_OK, "cannot add entry to CATable table:%d\n", r ); + + r = run_query(hdb, 0, "INSERT INTO `CATable` " + "( `Action`, `Type` ) " + "VALUES ( 'build2', 34)"); + ok(r == S_OK, "cannot add entry to CATable table:%d\n", r ); + query = "Select IESTable.Condition from CATable, IESTable where " + "CATable.Action = IESTable.Action and CATable.Type = 32"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(view, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + r = MsiViewFetch(view, &rec); + ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r ); + + ok( check_record( rec, 1, "cond2"), "wrong condition\n"); + + MsiCloseHandle( rec ); + r = MsiViewFetch(view, &rec); + ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r ); + + ok( check_record( rec, 1, "cond3"), "wrong condition\n"); + + MsiCloseHandle( rec ); + MsiViewClose(view); + MsiCloseHandle(view); + + MsiCloseHandle( hdb ); + DeleteFile(msifile); + +} + + +static void test_where(void) +{ + MSIHANDLE hdb = 0, rec, view; + LPCSTR query; + UINT r; + DWORD size; + CHAR buf[MAX_PATH]; + UINT count; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + r = run_query( hdb, 0, + "CREATE TABLE `Media` (" + "`DiskId` SHORT NOT NULL, " + "`LastSequence` LONG, " + "`DiskPrompt` CHAR(64) LOCALIZABLE, " + "`Cabinet` CHAR(255), " + "`VolumeLabel` CHAR(32), " + "`Source` CHAR(72) " + "PRIMARY KEY `DiskId`)" ); + ok( r == S_OK, "cannot create Media table: %d\n", r ); + + r = run_query( hdb, 0, "INSERT INTO `Media` " + "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) " + "VALUES ( 1, 0, '', 'zero.cab', '', '' )" ); + ok( r == S_OK, "cannot add file to the Media table: %d\n", r ); + + r = run_query( hdb, 0, "INSERT INTO `Media` " + "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) " + "VALUES ( 2, 1, '', 'one.cab', '', '' )" ); + ok( r == S_OK, "cannot add file to the Media table: %d\n", r ); + + r = run_query( hdb, 0, "INSERT INTO `Media` " + "( `DiskId`, `LastSequence`, `DiskPrompt`, `Cabinet`, `VolumeLabel`, `Source` ) " + "VALUES ( 3, 2, '', 'two.cab', '', '' )" ); + ok( r == S_OK, "cannot add file to the Media table: %d\n", r ); + + query = "SELECT * FROM `Media`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r); + ok( check_record( rec, 4, "zero.cab"), "wrong cabinet\n"); + MsiCloseHandle( rec ); + + query = "SELECT * FROM `Media` WHERE `LastSequence` >= 1"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed: %d\n", r); + ok( check_record( rec, 4, "one.cab"), "wrong cabinet\n"); + + r = MsiRecordGetInteger(rec, 1); + ok( 2 == r, "field wrong\n"); + r = MsiRecordGetInteger(rec, 2); + ok( 1 == r, "field wrong\n"); + MsiCloseHandle( rec ); + + query = "SELECT `DiskId` FROM `Media` WHERE `LastSequence` >= 1 AND DiskId >= 0"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(view, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + r = MsiViewFetch(view, &rec); + ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r ); + + count = MsiRecordGetFieldCount( rec ); + ok( count == 1, "Expected 1 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + ok( !lstrcmp( buf, "2" ), + "For (row %d, column 1) expected '%d', got %s\n", 0, 2, buf ); + MsiCloseHandle( rec ); + + r = MsiViewFetch(view, &rec); + ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r ); + + size = MAX_PATH; + r = MsiRecordGetString( rec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + ok( !lstrcmp( buf, "3" ), + "For (row %d, column 1) expected '%d', got %s\n", 1, 3, buf ); + MsiCloseHandle( rec ); + + r = MsiViewFetch(view, &rec); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(view); + MsiCloseHandle(view); + + MsiCloseHandle( rec ); + + rec = 0; + query = "SELECT * FROM `Media` WHERE `DiskPrompt` IS NULL"; + r = do_query(hdb, query, &rec); + ok( r == ERROR_SUCCESS, "query failed: %d\n", r ); + MsiCloseHandle( rec ); + + rec = 0; + query = "SELECT * FROM `Media` WHERE `DiskPrompt` < 'Cabinet'"; + r = do_query(hdb, query, &rec); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %d\n", r ); + MsiCloseHandle( rec ); + + rec = 0; + query = "SELECT * FROM `Media` WHERE `DiskPrompt` > 'Cabinet'"; + r = do_query(hdb, query, &rec); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %d\n", r ); + MsiCloseHandle( rec ); + + rec = 0; + query = "SELECT * FROM `Media` WHERE `DiskPrompt` <> 'Cabinet'"; + r = do_query(hdb, query, &rec); + ok( r == ERROR_SUCCESS, "query failed: %d\n", r ); + MsiCloseHandle( rec ); + + rec = 0; + query = "SELECT * FROM `Media` WHERE `DiskPrompt` = 'Cabinet'"; + r = do_query(hdb, query, &rec); + ok( r == ERROR_NO_MORE_ITEMS, "query failed: %d\n", r ); + MsiCloseHandle( rec ); + + rec = MsiCreateRecord(1); + MsiRecordSetString(rec, 1, ""); + + query = "SELECT * FROM `Media` WHERE `DiskPrompt` = ?"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + + MsiCloseHandle( hdb ); + DeleteFile(msifile); +} + +static CHAR CURR_DIR[MAX_PATH]; + +static const CHAR test_data[] = "FirstPrimaryColumn\tSecondPrimaryColumn\tShortInt\tShortIntNullable\tLongInt\tLongIntNullable\tString\tLocalizableString\tLocalizableStringNullable\n" + "s255\ti2\ti2\tI2\ti4\tI4\tS255\tS0\ts0\n" + "TestTable\tFirstPrimaryColumn\n" + "stringage\t5\t2\t\t2147483640\t-2147483640\tanother string\tlocalizable\tduh\n"; + +static const CHAR two_primary[] = "PrimaryOne\tPrimaryTwo\n" + "s255\ts255\n" + "TwoPrimary\tPrimaryOne\tPrimaryTwo\n" + "papaya\tleaf\n" + "papaya\tflower\n"; + +static const CHAR endlines1[] = "A\tB\tC\tD\tE\tF\r\n" + "s72\ts72\ts72\ts72\ts72\ts72\n" + "Table\tA\r\n" + "a\tb\tc\td\te\tf\n" + "g\th\ti\t\rj\tk\tl\r\n"; + +static const CHAR endlines2[] = "A\tB\tC\tD\tE\tF\r" + "s72\ts72\ts72\ts72\ts72\ts72\n" + "Table2\tA\r\n" + "a\tb\tc\td\te\tf\n" + "g\th\ti\tj\tk\tl\r\n"; + +static const CHAR suminfo[] = "PropertyId\tValue\n" + "i2\tl255\n" + "_SummaryInformation\tPropertyId\n" + "1\t1252\n" + "2\tInstaller Database\n" + "3\tInstaller description\n" + "4\tWineHQ\n" + "5\tInstaller\n" + "6\tInstaller comments\n" + "7\tIntel;1033,2057\n" + "9\t{12345678-1234-1234-1234-123456789012}\n" + "12\t2009/04/12 15:46:11\n" + "13\t2009/04/12 15:46:11\n" + "14\t200\n" + "15\t2\n" + "18\tVim\n" + "19\t2\n"; + +static void write_file(const CHAR *filename, const char *data, int data_size) +{ + DWORD size; + + HANDLE hf = CreateFile(filename, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + WriteFile(hf, data, data_size, &size, NULL); + CloseHandle(hf); +} + +static UINT add_table_to_db(MSIHANDLE hdb, LPCSTR table_data) +{ + UINT r; + + write_file("temp_file", table_data, (lstrlen(table_data) - 1) * sizeof(char)); + r = MsiDatabaseImportA(hdb, CURR_DIR, "temp_file"); + DeleteFileA("temp_file"); + + return r; +} + +static void test_suminfo_import(void) +{ + MSIHANDLE hdb, hsi, view = 0; + LPCSTR query; + UINT r, count, size, type; + char str_value[50]; + INT int_value; + FILETIME ft_value; + + GetCurrentDirectoryA(MAX_PATH, CURR_DIR); + + r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + r = add_table_to_db(hdb, suminfo); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + /* _SummaryInformation is not imported as a regular table... */ + + query = "SELECT * FROM `_SummaryInformation`"; + r = MsiDatabaseOpenViewA(hdb, query, &view); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %u\n", r); + MsiCloseHandle(view); + + /* ...its data is added to the special summary information stream */ + + r = MsiGetSummaryInformationA(hdb, NULL, 0, &hsi); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + r = MsiSummaryInfoGetPropertyCount(hsi, &count); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(count == 14, "Expected 14, got %u\n", count); + + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_CODEPAGE, &type, &int_value, NULL, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_I2, "Expected VT_I2, got %u\n", type); + ok(int_value == 1252, "Expected 1252, got %d\n", int_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_TITLE, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(size == 18, "Expected 18, got %u\n", size); + ok(!strcmp(str_value, "Installer Database"), + "Expected \"Installer Database\", got %s\n", str_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_SUBJECT, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(!strcmp(str_value, "Installer description"), + "Expected \"Installer description\", got %s\n", str_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_AUTHOR, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(!strcmp(str_value, "WineHQ"), + "Expected \"WineHQ\", got %s\n", str_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_KEYWORDS, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(!strcmp(str_value, "Installer"), + "Expected \"Installer\", got %s\n", str_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_COMMENTS, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(!strcmp(str_value, "Installer comments"), + "Expected \"Installer comments\", got %s\n", str_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_TEMPLATE, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(!strcmp(str_value, "Intel;1033,2057"), + "Expected \"Intel;1033,2057\", got %s\n", str_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_REVNUMBER, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(!strcmp(str_value, "{12345678-1234-1234-1234-123456789012}"), + "Expected \"{12345678-1234-1234-1234-123456789012}\", got %s\n", str_value); + + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_CREATE_DTM, &type, NULL, &ft_value, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_FILETIME, "Expected VT_FILETIME, got %u\n", type); + + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_LASTSAVE_DTM, &type, NULL, &ft_value, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_FILETIME, "Expected VT_FILETIME, got %u\n", type); + + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_PAGECOUNT, &type, &int_value, NULL, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_I4, "Expected VT_I4, got %u\n", type); + ok(int_value == 200, "Expected 200, got %d\n", int_value); + + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_WORDCOUNT, &type, &int_value, NULL, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_I4, "Expected VT_I4, got %u\n", type); + ok(int_value == 2, "Expected 2, got %d\n", int_value); + + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_SECURITY, &type, &int_value, NULL, NULL, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_I4, "Expected VT_I4, got %u\n", type); + ok(int_value == 2, "Expected 2, got %d\n", int_value); + + size = sizeof(str_value); + r = MsiSummaryInfoGetPropertyA(hsi, MSI_PID_APPNAME, &type, NULL, NULL, str_value, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %u\n", type); + ok(!strcmp(str_value, "Vim"), "Expected \"Vim\", got %s\n", str_value); + + MsiCloseHandle(hsi); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_msiimport(void) +{ + MSIHANDLE hdb, view, rec; + LPCSTR query; + UINT r, count; + signed int i; + + GetCurrentDirectoryA(MAX_PATH, CURR_DIR); + + r = MsiOpenDatabaseA(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_table_to_db(hdb, test_data); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_table_to_db(hdb, two_primary); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_table_to_db(hdb, endlines1); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = add_table_to_db(hdb, endlines2); + ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + query = "SELECT * FROM `TestTable`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 9, "Expected 9, got %d\n", count); + ok(check_record(rec, 1, "FirstPrimaryColumn"), "Expected FirstPrimaryColumn\n"); + ok(check_record(rec, 2, "SecondPrimaryColumn"), "Expected SecondPrimaryColumn\n"); + ok(check_record(rec, 3, "ShortInt"), "Expected ShortInt\n"); + ok(check_record(rec, 4, "ShortIntNullable"), "Expected ShortIntNullalble\n"); + ok(check_record(rec, 5, "LongInt"), "Expected LongInt\n"); + ok(check_record(rec, 6, "LongIntNullable"), "Expected LongIntNullalble\n"); + ok(check_record(rec, 7, "String"), "Expected String\n"); + ok(check_record(rec, 8, "LocalizableString"), "Expected LocalizableString\n"); + ok(check_record(rec, 9, "LocalizableStringNullable"), "Expected LocalizableStringNullable\n"); + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 9, "Expected 9, got %d\n", count); + ok(check_record(rec, 1, "s255"), "Expected s255\n"); + ok(check_record(rec, 2, "i2"), "Expected i2\n"); + ok(check_record(rec, 3, "i2"), "Expected i2\n"); + ok(check_record(rec, 4, "I2"), "Expected I2\n"); + ok(check_record(rec, 5, "i4"), "Expected i4\n"); + ok(check_record(rec, 6, "I4"), "Expected I4\n"); + ok(check_record(rec, 7, "S255"), "Expected S255\n"); + ok(check_record(rec, 8, "S0"), "Expected S0\n"); + ok(check_record(rec, 9, "s0"), "Expected s0\n"); + MsiCloseHandle(rec); + + query = "SELECT * FROM `TestTable`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(check_record(rec, 1, "stringage"), "Expected 'stringage'\n"); + ok(check_record(rec, 7, "another string"), "Expected 'another string'\n"); + ok(check_record(rec, 8, "localizable"), "Expected 'localizable'\n"); + ok(check_record(rec, 9, "duh"), "Expected 'duh'\n"); + + i = MsiRecordGetInteger(rec, 2); + ok(i == 5, "Expected 5, got %d\n", i); + + i = MsiRecordGetInteger(rec, 3); + ok(i == 2, "Expected 2, got %d\n", i); + + i = MsiRecordGetInteger(rec, 4); + ok(i == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", i); + + i = MsiRecordGetInteger(rec, 5); + ok(i == 2147483640, "Expected 2147483640, got %d\n", i); + + i = MsiRecordGetInteger(rec, 6); + ok(i == -2147483640, "Expected -2147483640, got %d\n", i); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + + query = "SELECT * FROM `TwoPrimary`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 2, "Expected 2, got %d\n", count); + ok(check_record(rec, 1, "PrimaryOne"), "Expected PrimaryOne\n"); + ok(check_record(rec, 2, "PrimaryTwo"), "Expected PrimaryTwo\n"); + + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 2, "Expected 2, got %d\n", count); + ok(check_record(rec, 1, "s255"), "Expected s255\n"); + ok(check_record(rec, 2, "s255"), "Expected s255\n"); + MsiCloseHandle(rec); + + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + ok(check_record(rec, 1, "papaya"), "Expected 'papaya'\n"); + ok(check_record(rec, 2, "leaf"), "Expected 'leaf'\n"); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + ok(check_record(rec, 1, "papaya"), "Expected 'papaya'\n"); + ok(check_record(rec, 2, "flower"), "Expected 'flower'\n"); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(view); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 6, "Expected 6, got %d\n", count); + ok(check_record(rec, 1, "A"), "Expected A\n"); + ok(check_record(rec, 2, "B"), "Expected B\n"); + ok(check_record(rec, 3, "C"), "Expected C\n"); + ok(check_record(rec, 4, "D"), "Expected D\n"); + ok(check_record(rec, 5, "E"), "Expected E\n"); + ok(check_record(rec, 6, "F"), "Expected F\n"); + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 6, "Expected 6, got %d\n", count); + ok(check_record(rec, 1, "s72"), "Expected s72\n"); + ok(check_record(rec, 2, "s72"), "Expected s72\n"); + ok(check_record(rec, 3, "s72"), "Expected s72\n"); + ok(check_record(rec, 4, "s72"), "Expected s72\n"); + ok(check_record(rec, 5, "s72"), "Expected s72\n"); + ok(check_record(rec, 6, "s72"), "Expected s72\n"); + MsiCloseHandle(rec); + + MsiViewClose(view); + MsiCloseHandle(view); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(check_record(rec, 1, "a"), "Expected 'a'\n"); + ok(check_record(rec, 2, "b"), "Expected 'b'\n"); + ok(check_record(rec, 3, "c"), "Expected 'c'\n"); + ok(check_record(rec, 4, "d"), "Expected 'd'\n"); + ok(check_record(rec, 5, "e"), "Expected 'e'\n"); + ok(check_record(rec, 6, "f"), "Expected 'f'\n"); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(check_record(rec, 1, "g"), "Expected 'g'\n"); + ok(check_record(rec, 2, "h"), "Expected 'h'\n"); + ok(check_record(rec, 3, "i"), "Expected 'i'\n"); + ok(check_record(rec, 4, "j"), "Expected 'j'\n"); + ok(check_record(rec, 5, "k"), "Expected 'k'\n"); + ok(check_record(rec, 6, "l"), "Expected 'l'\n"); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static const CHAR bin_import_dat[] = "Name\tData\r\n" + "s72\tV0\r\n" + "Binary\tName\r\n" + "filename1\tfilename1.ibd\r\n"; + +static void test_binary_import(void) +{ + MSIHANDLE hdb = 0, rec; + char file[MAX_PATH]; + char buf[MAX_PATH]; + char path[MAX_PATH]; + DWORD size; + LPCSTR query; + UINT r; + + /* create files to import */ + write_file("bin_import.idt", bin_import_dat, + (sizeof(bin_import_dat) - 1) * sizeof(char)); + CreateDirectory("bin_import", NULL); + create_file_data("bin_import/filename1.ibd", "just some words", 15); + + /* import files into database */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok( r == ERROR_SUCCESS , "Failed to open database\n"); + + GetCurrentDirectory(MAX_PATH, path); + r = MsiDatabaseImport(hdb, path, "bin_import.idt"); + ok(r == ERROR_SUCCESS , "Failed to import Binary table\n"); + + /* read file from the Binary table */ + query = "SELECT * FROM `Binary`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "SELECT query failed: %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, file, &size); + ok(r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok(!lstrcmp(file, "filename1"), "Expected 'filename1', got %s\n", file); + + size = MAX_PATH; + memset(buf, 0, MAX_PATH); + r = MsiRecordReadStream(rec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + ok(!lstrcmp(buf, "just some words"), + "Expected 'just some words', got %s\n", buf); + + r = MsiCloseHandle(rec); + ok(r == ERROR_SUCCESS , "Failed to close record handle\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS , "Failed to close database\n"); + + DeleteFile("bin_import/filename1.ibd"); + RemoveDirectory("bin_import"); + DeleteFile("bin_import.idt"); +} + +static void test_markers(void) +{ + MSIHANDLE hdb, rec; + LPCSTR query; + UINT r; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + rec = MsiCreateRecord(3); + MsiRecordSetString(rec, 1, "Table"); + MsiRecordSetString(rec, 2, "Apples"); + MsiRecordSetString(rec, 3, "Oranges"); + + /* try a legit create */ + query = "CREATE TABLE `Table` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); + + /* try table name as marker */ + rec = MsiCreateRecord(1); + MsiRecordSetString(rec, 1, "Fable"); + query = "CREATE TABLE `?` ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* verify that we just created a table called '?', not 'Fable' */ + r = try_query(hdb, "SELECT * from `Fable`"); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + r = try_query(hdb, "SELECT * from `?`"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* try table name as marker without backticks */ + MsiRecordSetString(rec, 1, "Mable"); + query = "CREATE TABLE ? ( `One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* try one column name as marker */ + MsiRecordSetString(rec, 1, "One"); + query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* try column names as markers */ + rec = MsiCreateRecord(2); + MsiRecordSetString(rec, 1, "One"); + MsiRecordSetString(rec, 2, "Two"); + query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `One`)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* try names with backticks */ + rec = MsiCreateRecord(3); + MsiRecordSetString(rec, 1, "One"); + MsiRecordSetString(rec, 2, "Two"); + MsiRecordSetString(rec, 3, "One"); + query = "CREATE TABLE `Mable` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* try names with backticks, minus definitions */ + query = "CREATE TABLE `Mable` ( `?`, `?` PRIMARY KEY `?`)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* try names without backticks */ + query = "CREATE TABLE `Mable` ( ? SHORT NOT NULL, ? CHAR(255) PRIMARY KEY ?)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* try one long marker */ + rec = MsiCreateRecord(1); + MsiRecordSetString(rec, 1, "`One` SHORT NOT NULL, `Two` CHAR(255) PRIMARY KEY `One`"); + query = "CREATE TABLE `Mable` ( ? )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* try all names as markers */ + rec = MsiCreateRecord(4); + MsiRecordSetString(rec, 1, "Mable"); + MsiRecordSetString(rec, 2, "One"); + MsiRecordSetString(rec, 3, "Two"); + MsiRecordSetString(rec, 4, "One"); + query = "CREATE TABLE `?` ( `?` SHORT NOT NULL, `?` CHAR(255) PRIMARY KEY `?`)"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* try a legit insert */ + query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( 5, 'hello' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = try_query(hdb, "SELECT * from `Table`"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* try values as markers */ + rec = MsiCreateRecord(2); + MsiRecordSetInteger(rec, 1, 4); + MsiRecordSetString(rec, 2, "hi"); + query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); + + /* try column names and values as markers */ + rec = MsiCreateRecord(4); + MsiRecordSetString(rec, 1, "One"); + MsiRecordSetString(rec, 2, "Two"); + MsiRecordSetInteger(rec, 3, 5); + MsiRecordSetString(rec, 4, "hi"); + query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( ?, '?' )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* try column names as markers */ + rec = MsiCreateRecord(2); + MsiRecordSetString(rec, 1, "One"); + MsiRecordSetString(rec, 2, "Two"); + query = "INSERT INTO `Table` ( `?`, `?` ) VALUES ( 3, 'yellow' )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* try table name as a marker */ + rec = MsiCreateRecord(1); + MsiRecordSetString(rec, 1, "Table"); + query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( 2, 'green' )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); + + /* try table name and values as markers */ + rec = MsiCreateRecord(3); + MsiRecordSetString(rec, 1, "Table"); + MsiRecordSetInteger(rec, 2, 10); + MsiRecordSetString(rec, 3, "haha"); + query = "INSERT INTO `?` ( `One`, `Two` ) VALUES ( ?, '?' )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + MsiCloseHandle(rec); + + /* try all markers */ + rec = MsiCreateRecord(5); + MsiRecordSetString(rec, 1, "Table"); + MsiRecordSetString(rec, 1, "One"); + MsiRecordSetString(rec, 1, "Two"); + MsiRecordSetInteger(rec, 2, 10); + MsiRecordSetString(rec, 3, "haha"); + query = "INSERT INTO `?` ( `?`, `?` ) VALUES ( ?, '?' )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + MsiCloseHandle(rec); + + /* insert an integer as a string */ + rec = MsiCreateRecord(2); + MsiRecordSetString(rec, 1, "11"); + MsiRecordSetString(rec, 2, "hi"); + query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, '?' )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); + + /* leave off the '' for the string */ + rec = MsiCreateRecord(2); + MsiRecordSetInteger(rec, 1, 12); + MsiRecordSetString(rec, 2, "hi"); + query = "INSERT INTO `Table` ( `One`, `Two` ) VALUES ( ?, ? )"; + r = run_query(hdb, rec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(rec); + + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +#define MY_NVIEWS 4000 /* Largest installer I've seen uses < 2k */ +static void test_handle_limit(void) +{ + int i; + MSIHANDLE hdb; + MSIHANDLE hviews[MY_NVIEWS]; + UINT r; + + /* create an empty db */ + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + memset(hviews, 0, sizeof(hviews)); + + for (i=0; i<MY_NVIEWS; i++) { + static char szQueryBuf[256] = "SELECT * from `_Tables`"; + hviews[i] = 0xdeadbeeb; + r = MsiDatabaseOpenView(hdb, szQueryBuf, &hviews[i]); + if( r != ERROR_SUCCESS || hviews[i] == 0xdeadbeeb || + hviews[i] == 0 || (i && (hviews[i] == hviews[i-1]))) + break; + } + + ok( i == MY_NVIEWS, "problem opening views\n"); + + for (i=0; i<MY_NVIEWS; i++) { + if (hviews[i] != 0 && hviews[i] != 0xdeadbeeb) { + MsiViewClose(hviews[i]); + r = MsiCloseHandle(hviews[i]); + if (r != ERROR_SUCCESS) + break; + } + } + + ok( i == MY_NVIEWS, "problem closing views\n"); + + r = MsiCloseHandle(hdb); + ok( r == ERROR_SUCCESS, "failed to close database\n"); +} + +/* data for generating a transform */ + +/* tables transform names - encoded as they would be in an msi database file */ +static const WCHAR name1[] = { 0x4840, 0x3a8a, 0x481b, 0 }; /* AAR */ +static const WCHAR name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */ +static const WCHAR name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */ +static const WCHAR name4[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */ +static const WCHAR name5[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */ +static const WCHAR name6[] = { 0x4840, 0x3e16, 0x4818, 0}; /* MOO */ +static const WCHAR name7[] = { 0x4840, 0x3c8b, 0x3a97, 0x409b, 0 }; /* BINARY */ +static const WCHAR name8[] = { 0x3c8b, 0x3a97, 0x409b, 0x387e, 0 }; /* BINARY.1 */ +static const WCHAR name9[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */ + +/* data in each table */ +static const WCHAR data1[] = { /* AAR */ + 0x0201, 0x0008, 0x8001, /* 0x0201 = add row (1), two shorts */ + 0x0201, 0x0009, 0x8002, +}; +static const WCHAR data2[] = { /* _Columns */ + 0x0401, 0x0001, 0x8003, 0x0002, 0x9502, + 0x0401, 0x0001, 0x8004, 0x0003, 0x9502, + 0x0401, 0x0005, 0x0000, 0x0006, 0xbdff, /* 0x0401 = add row (1), 4 shorts */ + 0x0401, 0x0005, 0x0000, 0x0007, 0x8502, + 0x0401, 0x000a, 0x0000, 0x000a, 0xad48, + 0x0401, 0x000a, 0x0000, 0x000b, 0x9d00, +}; +static const WCHAR data3[] = { /* _Tables */ + 0x0101, 0x0005, /* 0x0101 = add row (1), 1 short */ + 0x0101, 0x000a, +}; +static const char data4[] = /* _StringData */ + "MOOCOWPIGcAARCARBARvwbmwPropertyValuepropval"; /* all the strings squashed together */ +static const WCHAR data5[] = { /* _StringPool */ +/* len, refs */ + 0, 0, /* string 0 '' */ + 3, 2, /* string 1 'MOO' */ + 3, 1, /* string 2 'COW' */ + 3, 1, /* string 3 'PIG' */ + 1, 1, /* string 4 'c' */ + 3, 3, /* string 5 'AAR' */ + 3, 1, /* string 6 'CAR' */ + 3, 1, /* string 7 'BAR' */ + 2, 1, /* string 8 'vw' */ + 3, 1, /* string 9 'bmw' */ + 8, 4, /* string 10 'Property' */ + 5, 1, /* string 11 'Value' */ + 4, 1, /* string 12 'prop' */ + 3, 1, /* string 13 'val' */ +}; +/* update row, 0x0002 is a bitmask of present column data, keys are excluded */ +static const WCHAR data6[] = { /* MOO */ + 0x000a, 0x8001, 0x0004, 0x8005, /* update row */ + 0x0000, 0x8003, /* delete row */ +}; + +static const WCHAR data7[] = { /* BINARY */ + 0x0201, 0x8001, 0x0001, +}; + +static const char data8[] = /* stream data for the BINARY table */ + "naengmyon"; + +static const WCHAR data9[] = { /* Property */ + 0x0201, 0x000c, 0x000d, +}; + +static const struct { + LPCWSTR name; + const void *data; + DWORD size; +} table_transform_data[] = +{ + { name1, data1, sizeof data1 }, + { name2, data2, sizeof data2 }, + { name3, data3, sizeof data3 }, + { name4, data4, sizeof data4 - 1 }, + { name5, data5, sizeof data5 }, + { name6, data6, sizeof data6 }, + { name7, data7, sizeof data7 }, + { name8, data8, sizeof data8 - 1 }, + { name9, data9, sizeof data9 }, +}; + +#define NUM_TRANSFORM_TABLES (sizeof table_transform_data/sizeof table_transform_data[0]) + +static void generate_transform_manual(void) +{ + IStorage *stg = NULL; + IStream *stm; + WCHAR name[0x20]; + HRESULT r; + DWORD i, count; + const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE; + + const CLSID CLSID_MsiTransform = { 0xc1082,0,0,{0xc0,0,0,0,0,0,0,0x46}}; + + MultiByteToWideChar(CP_ACP, 0, mstfile, -1, name, 0x20); + + r = StgCreateDocfile(name, mode, 0, &stg); + ok(r == S_OK, "failed to create storage\n"); + if (!stg) + return; + + r = IStorage_SetClass( stg, &CLSID_MsiTransform ); + ok(r == S_OK, "failed to set storage type\n"); + + for (i=0; i<NUM_TRANSFORM_TABLES; i++) + { + r = IStorage_CreateStream( stg, table_transform_data[i].name, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + if (FAILED(r)) + { + ok(0, "failed to create stream %08x\n", r); + continue; + } + + r = IStream_Write( stm, table_transform_data[i].data, + table_transform_data[i].size, &count ); + if (FAILED(r) || count != table_transform_data[i].size) + ok(0, "failed to write stream\n"); + IStream_Release(stm); + } + + IStorage_Release(stg); +} + +static UINT set_summary_info(MSIHANDLE hdb) +{ + UINT res; + MSIHANDLE suminfo; + + /* build summary info */ + res = MsiGetSummaryInformation(hdb, NULL, 7, &suminfo); + ok( res == ERROR_SUCCESS , "Failed to open summaryinfo\n" ); + + res = MsiSummaryInfoSetProperty(suminfo,2, VT_LPSTR, 0,NULL, + "Installation Database"); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = MsiSummaryInfoSetProperty(suminfo,3, VT_LPSTR, 0,NULL, + "Installation Database"); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = MsiSummaryInfoSetProperty(suminfo,4, VT_LPSTR, 0,NULL, + "Wine Hackers"); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = MsiSummaryInfoSetProperty(suminfo,7, VT_LPSTR, 0,NULL, + ";1033,2057"); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = MsiSummaryInfoSetProperty(suminfo,9, VT_LPSTR, 0,NULL, + "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}"); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = MsiSummaryInfoSetProperty(suminfo, 14, VT_I4, 100, NULL, NULL); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = MsiSummaryInfoSetProperty(suminfo, 15, VT_I4, 0, NULL, NULL); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = MsiSummaryInfoPersist(suminfo); + ok( res == ERROR_SUCCESS , "Failed to make summary info persist\n" ); + + res = MsiCloseHandle( suminfo); + ok( res == ERROR_SUCCESS , "Failed to close suminfo\n" ); + + return res; +} + +static MSIHANDLE create_package_db(LPCSTR filename) +{ + MSIHANDLE hdb = 0; + UINT res; + + DeleteFile(msifile); + + /* create an empty database */ + res = MsiOpenDatabase(filename, MSIDBOPEN_CREATE, &hdb ); + ok( res == ERROR_SUCCESS , "Failed to create database\n" ); + if( res != ERROR_SUCCESS ) + return hdb; + + res = MsiDatabaseCommit( hdb ); + ok( res == ERROR_SUCCESS , "Failed to commit database\n" ); + + res = set_summary_info(hdb); + ok( res == ERROR_SUCCESS , "Failed to set summary info\n" ); + + res = create_directory_table(hdb); + ok( res == ERROR_SUCCESS , "Failed to create directory table\n" ); + + return hdb; +} + +static void test_try_transform(void) +{ + MSIHANDLE hdb, hview, hrec, hpkg = 0; + LPCSTR query; + UINT r; + DWORD sz; + char buffer[MAX_PATH]; + + DeleteFile(msifile); + DeleteFile(mstfile); + + /* create the database */ + hdb = create_package_db(msifile); + ok(hdb, "Failed to create package db\n"); + + query = "CREATE TABLE `MOO` ( `NOO` SHORT NOT NULL, `OOO` CHAR(255) PRIMARY KEY `NOO`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + query = "INSERT INTO `MOO` ( `NOO`, `OOO` ) VALUES ( 1, 'a' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add row\n"); + + query = "INSERT INTO `MOO` ( `NOO`, `OOO` ) VALUES ( 2, 'b' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add row\n"); + + query = "INSERT INTO `MOO` ( `NOO`, `OOO` ) VALUES ( 3, 'c' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add row\n"); + + query = "CREATE TABLE `BINARY` ( `ID` SHORT NOT NULL, `BLOB` OBJECT PRIMARY KEY `ID`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + hrec = MsiCreateRecord(2); + r = MsiRecordSetInteger(hrec, 1, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + + write_file("testdata.bin", "lamyon", 6); + r = MsiRecordSetStream(hrec, 2, "testdata.bin"); + ok(r == ERROR_SUCCESS, "failed to set stream\n"); + + query = "INSERT INTO `BINARY` ( `ID`, `BLOB` ) VALUES ( ?, ? )"; + r = run_query(hdb, hrec, query); + ok(r == ERROR_SUCCESS, "failed to add row with blob\n"); + + MsiCloseHandle(hrec); + + r = MsiDatabaseCommit( hdb ); + ok( r == ERROR_SUCCESS , "Failed to commit database\n" ); + + MsiCloseHandle( hdb ); + DeleteFileA("testdata.bin"); + + generate_transform_manual(); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_DIRECT, &hdb ); + ok( r == ERROR_SUCCESS , "Failed to create database\n" ); + + r = MsiDatabaseApplyTransform( hdb, mstfile, 0 ); + ok( r == ERROR_SUCCESS, "return code %d, should be ERROR_SUCCESS\n", r ); + + MsiDatabaseCommit( hdb ); + + /* check new values */ + hrec = 0; + query = "select `BAR`,`CAR` from `AAR` where `BAR` = 1 AND `CAR` = 'vw'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "select query failed\n"); + MsiCloseHandle(hrec); + + query = "select `BAR`,`CAR` from `AAR` where `BAR` = 2 AND `CAR` = 'bmw'"; + hrec = 0; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "select query failed\n"); + MsiCloseHandle(hrec); + + /* check updated values */ + hrec = 0; + query = "select `NOO`,`OOO` from `MOO` where `NOO` = 1 AND `OOO` = 'c'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "select query failed\n"); + MsiCloseHandle(hrec); + + /* check unchanged value */ + hrec = 0; + query = "select `NOO`,`OOO` from `MOO` where `NOO` = 2 AND `OOO` = 'b'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "select query failed\n"); + MsiCloseHandle(hrec); + + /* check deleted value */ + hrec = 0; + query = "select * from `MOO` where `NOO` = 3"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "select query failed\n"); + if (hrec) MsiCloseHandle(hrec); + + /* check added stream */ + hrec = 0; + query = "select `BLOB` from `BINARY` where `ID` = 1"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "select query failed\n"); + + /* check the contents of the stream */ + sz = sizeof buffer; + r = MsiRecordReadStream( hrec, 1, buffer, &sz ); + ok(r == ERROR_SUCCESS, "read stream failed\n"); + ok(!memcmp(buffer, "naengmyon", 9), "stream data was wrong\n"); + ok(sz == 9, "stream data was wrong size\n"); + if (hrec) MsiCloseHandle(hrec); + + /* check the validity of the table with a deleted row */ + hrec = 0; + query = "select * from `MOO`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "open view failed\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "view execute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "view fetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = sizeof buffer; + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "record get string failed\n"); + ok(!lstrcmpA(buffer, "c"), "Expected c, got %s\n", buffer); + + r = MsiRecordGetInteger(hrec, 3); + ok(r == 0x80000000, "Expected 0x80000000, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 4); + ok(r == 5, "Expected 5, got %d\n", r); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "view fetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 2, "Expected 2, got %d\n", r); + + sz = sizeof buffer; + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "record get string failed\n"); + ok(!lstrcmpA(buffer, "b"), "Expected b, got %s\n", buffer); + + r = MsiRecordGetInteger(hrec, 3); + ok(r == 0x80000000, "Expected 0x80000000, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 4); + ok(r == 0x80000000, "Expected 0x80000000, got %d\n", r); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "view fetch succeeded\n"); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + +#if 0 + /* check that the property was added */ + r = package_from_db(hdb, &hpkg); + if (r == ERROR_INSTALL_PACKAGE_REJECTED) + { + skip("Not enough rights to perform tests\n"); + goto error; + } + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + + sz = MAX_PATH; + r = MsiGetProperty(hpkg, "prop", buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "val"), "Expected val, got %s\n", buffer); +#endif + + MsiCloseHandle(hpkg); + +error: + MsiCloseHandle(hdb); + DeleteFile(msifile); + DeleteFile(mstfile); +} + +struct join_res +{ + const CHAR one[MAX_PATH]; + const CHAR two[MAX_PATH]; +}; + +struct join_res_4col +{ + const CHAR one[MAX_PATH]; + const CHAR two[MAX_PATH]; + const CHAR three[MAX_PATH]; + const CHAR four[MAX_PATH]; +}; + +struct join_res_uint +{ + UINT one; + UINT two; + UINT three; + UINT four; + UINT five; + UINT six; +}; + +static const struct join_res join_res_first[] = +{ + { "alveolar", "procerus" }, + { "septum", "procerus" }, + { "septum", "nasalis" }, + { "ramus", "nasalis" }, + { "malar", "mentalis" }, +}; + +static const struct join_res join_res_second[] = +{ + { "nasal", "septum" }, + { "mandible", "ramus" }, +}; + +static const struct join_res join_res_third[] = +{ + { "msvcp.dll", "abcdefgh" }, + { "msvcr.dll", "ijklmnop" }, +}; + +static const struct join_res join_res_fourth[] = +{ + { "msvcp.dll.01234", "single.dll.31415" }, +}; + +static const struct join_res join_res_fifth[] = +{ + { "malar", "procerus" }, +}; + +static const struct join_res join_res_sixth[] = +{ + { "malar", "procerus" }, + { "malar", "procerus" }, + { "malar", "nasalis" }, + { "malar", "nasalis" }, + { "malar", "nasalis" }, + { "malar", "mentalis" }, +}; + +static const struct join_res join_res_seventh[] = +{ + { "malar", "nasalis" }, + { "malar", "nasalis" }, + { "malar", "nasalis" }, +}; + +static const struct join_res_4col join_res_eighth[] = +{ + { "msvcp.dll", "msvcp.dll.01234", "msvcp.dll.01234", "abcdefgh" }, + { "msvcr.dll", "msvcr.dll.56789", "msvcp.dll.01234", "abcdefgh" }, + { "msvcp.dll", "msvcp.dll.01234", "msvcr.dll.56789", "ijklmnop" }, + { "msvcr.dll", "msvcr.dll.56789", "msvcr.dll.56789", "ijklmnop" }, + { "msvcp.dll", "msvcp.dll.01234", "single.dll.31415", "msvcp.dll" }, + { "msvcr.dll", "msvcr.dll.56789", "single.dll.31415", "msvcp.dll" }, +}; + +static const struct join_res_uint join_res_ninth[] = +{ + { 1, 2, 3, 4, 7, 8 }, + { 1, 2, 5, 6, 7, 8 }, + { 1, 2, 3, 4, 9, 10 }, + { 1, 2, 5, 6, 9, 10 }, + { 1, 2, 3, 4, 11, 12 }, + { 1, 2, 5, 6, 11, 12 }, +}; + +static void test_join(void) +{ + MSIHANDLE hdb, hview, hrec; + LPCSTR query; + CHAR buf[MAX_PATH]; + UINT r, count; + DWORD size, i; + BOOL data_correct; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + r = create_component_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create Component table: %d\n", r ); + + r = add_component_entry( hdb, "'zygomatic', 'malar', 'INSTALLDIR', 0, '', ''" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + r = add_component_entry( hdb, "'maxilla', 'alveolar', 'INSTALLDIR', 0, '', ''" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + r = add_component_entry( hdb, "'nasal', 'septum', 'INSTALLDIR', 0, '', ''" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + r = add_component_entry( hdb, "'mandible', 'ramus', 'INSTALLDIR', 0, '', ''" ); + ok( r == ERROR_SUCCESS, "cannot add component: %d\n", r ); + + r = create_feature_components_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create FeatureComponents table: %d\n", r ); + + r = add_feature_components_entry( hdb, "'procerus', 'maxilla'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'procerus', 'nasal'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'nasalis', 'nasal'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'nasalis', 'mandible'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'nasalis', 'notacomponent'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = add_feature_components_entry( hdb, "'mentalis', 'zygomatic'" ); + ok( r == ERROR_SUCCESS, "cannot add feature components: %d\n", r ); + + r = create_std_dlls_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create StdDlls table: %d\n", r ); + + r = add_std_dlls_entry( hdb, "'msvcp.dll', 'msvcp.dll.01234'" ); + ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r ); + + r = add_std_dlls_entry( hdb, "'msvcr.dll', 'msvcr.dll.56789'" ); + ok( r == ERROR_SUCCESS, "cannot add std dlls: %d\n", r ); + + r = create_binary_table( hdb ); + ok( r == ERROR_SUCCESS, "cannot create Binary table: %d\n", r ); + + r = add_binary_entry( hdb, "'msvcp.dll.01234', 'abcdefgh'" ); + ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r ); + + r = add_binary_entry( hdb, "'msvcr.dll.56789', 'ijklmnop'" ); + ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r ); + + r = add_binary_entry( hdb, "'single.dll.31415', 'msvcp.dll'" ); + ok( r == ERROR_SUCCESS, "cannot add binary: %d\n", r ); + + query = "CREATE TABLE `One` (`A` SHORT, `B` SHORT PRIMARY KEY `A`)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot create table: %d\n", r ); + + query = "CREATE TABLE `Two` (`C` SHORT, `D` SHORT PRIMARY KEY `C`)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot create table: %d\n", r ); + + query = "CREATE TABLE `Three` (`E` SHORT, `F` SHORT PRIMARY KEY `E`)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot create table: %d\n", r ); + + query = "INSERT INTO `One` (`A`, `B`) VALUES (1, 2)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "INSERT INTO `Two` (`C`, `D`) VALUES (3, 4)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "INSERT INTO `Two` (`C`, `D`) VALUES (5, 6)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "INSERT INTO `Three` (`E`, `F`) VALUES (7, 8)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "INSERT INTO `Three` (`E`, `F`) VALUES (9, 10)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "INSERT INTO `Three` (`E`, `F`) VALUES (11, 12)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "CREATE TABLE `Four` (`G` SHORT, `H` SHORT PRIMARY KEY `G`)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot create table: %d\n", r ); + + query = "CREATE TABLE `Five` (`I` SHORT, `J` SHORT PRIMARY KEY `I`)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot create table: %d\n", r ); + + query = "INSERT INTO `Five` (`I`, `J`) VALUES (13, 14)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "INSERT INTO `Five` (`I`, `J`) VALUES (15, 16)"; + r = run_query( hdb, 0, query); + ok(r == ERROR_SUCCESS, "cannot insert into table: %d\n", r ); + + query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` " + "FROM `Component`, `FeatureComponents` " + "WHERE `Component`.`Component` = `FeatureComponents`.`Component_` " + "ORDER BY `Feature_`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + ok( !lstrcmp( buf, join_res_first[i].one ), + "For (row %d, column 1) expected '%s', got %s\n", i, join_res_first[i].one, buf ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + ok( !lstrcmp( buf, join_res_first[i].two ), + "For (row %d, column 2) expected '%s', got %s\n", i, join_res_first[i].two, buf ); + + i++; + MsiCloseHandle(hrec); + } + + ok( i == 5, "Expected 5 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + /* try a join without a WHERE condition */ + query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` " + "FROM `Component`, `FeatureComponents` "; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + i++; + MsiCloseHandle(hrec); + } + ok( i == 24, "Expected 24 rows, got %d\n", i ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT DISTINCT Component, ComponentId FROM FeatureComponents, Component " + "WHERE FeatureComponents.Component_=Component.Component " + "AND (Feature_='nasalis') ORDER BY Feature_"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_second[i].one )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_second[i].two )) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + + ok( data_correct, "data returned in the wrong order\n"); + + ok( i == 2, "Expected 2 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `StdDlls`.`File`, `Binary`.`Data` " + "FROM `StdDlls`, `Binary` " + "WHERE `StdDlls`.`Binary_` = `Binary`.`Name` " + "ORDER BY `File`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_third[i].one ) ) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_third[i].two ) ) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + ok( data_correct, "data returned in the wrong order\n"); + + ok( i == 2, "Expected 2 rows, got %d\n", i ); + + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `StdDlls`.`Binary_`, `Binary`.`Name` " + "FROM `StdDlls`, `Binary` " + "WHERE `StdDlls`.`File` = `Binary`.`Data` " + "ORDER BY `Name`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_fourth[i].one )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_fourth[i].two )) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + ok( data_correct, "data returned in the wrong order\n"); + + ok( i == 1, "Expected 1 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` " + "FROM `Component`, `FeatureComponents` " + "WHERE `Component`.`Component` = 'zygomatic' " + "AND `FeatureComponents`.`Component_` = 'maxilla' " + "ORDER BY `Feature_`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_fifth[i].one )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_fifth[i].two )) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + ok( data_correct, "data returned in the wrong order\n"); + + ok( i == 1, "Expected 1 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` " + "FROM `Component`, `FeatureComponents` " + "WHERE `Component` = 'zygomatic' " + "ORDER BY `Feature_`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_sixth[i].one )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_sixth[i].two )) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + ok( data_correct, "data returned in the wrong order\n"); + + ok( i == 6, "Expected 6 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` " + "FROM `Component`, `FeatureComponents` " + "WHERE `Component` = 'zygomatic' " + "AND `Feature_` = 'nasalis' " + "ORDER BY `Feature_`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_seventh[i].one )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_seventh[i].two )) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + + ok( data_correct, "data returned in the wrong order\n"); + ok( i == 3, "Expected 3 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `StdDlls`.`File`, `Binary`.`Data` " + "FROM `StdDlls`, `Binary` "; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 2, "Expected 2 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_eighth[i].one )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_eighth[i].four )) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + + ok( data_correct, "data returned in the wrong order\n"); + ok( i == 6, "Expected 6 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `StdDlls`, `Binary` "; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 4, "Expected 4 record fields, got %d\n", count ); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_eighth[i].one )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_eighth[i].two )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 3, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_eighth[i].three )) + data_correct = FALSE; + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 4, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + if( lstrcmp( buf, join_res_eighth[i].four )) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + ok( data_correct, "data returned in the wrong order\n"); + + ok( i == 6, "Expected 6 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `One`, `Two`, `Three` "; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + i = 0; + data_correct = TRUE; + while ((r = MsiViewFetch(hview, &hrec)) == ERROR_SUCCESS) + { + count = MsiRecordGetFieldCount( hrec ); + ok( count == 6, "Expected 6 record fields, got %d\n", count ); + + r = MsiRecordGetInteger( hrec, 1 ); + if( r != join_res_ninth[i].one ) + data_correct = FALSE; + + r = MsiRecordGetInteger( hrec, 2 ); + if( r != join_res_ninth[i].two ) + data_correct = FALSE; + + r = MsiRecordGetInteger( hrec, 3 ); + if( r != join_res_ninth[i].three ) + data_correct = FALSE; + + r = MsiRecordGetInteger( hrec, 4 ); + if( r != join_res_ninth[i].four ) + data_correct = FALSE; + + r = MsiRecordGetInteger( hrec, 5 ); + if( r != join_res_ninth[i].five ) + data_correct = FALSE; + + r = MsiRecordGetInteger( hrec, 6); + if( r != join_res_ninth[i].six ) + data_correct = FALSE; + + i++; + MsiCloseHandle(hrec); + } + ok( data_correct, "data returned in the wrong order\n"); + + ok( i == 6, "Expected 6 rows, got %d\n", i ); + ok( r == ERROR_NO_MORE_ITEMS, "expected no more items: %d\n", r ); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Four`, `Five`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Nonexistent`, `One`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r ); + + /* try updating a row in a join table */ + query = "SELECT `Component`.`ComponentId`, `FeatureComponents`.`Feature_` " + "FROM `Component`, `FeatureComponents` " + "WHERE `Component`.`Component` = `FeatureComponents`.`Component_` " + "ORDER BY `Feature_`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok( r == ERROR_SUCCESS, "failed to open view: %d\n", r ); + + r = MsiViewExecute(hview, 0); + ok( r == ERROR_SUCCESS, "failed to execute view: %d\n", r ); + + r = MsiViewFetch(hview, &hrec); + ok( r == ERROR_SUCCESS, "failed to fetch view: %d\n", r ); + + r = MsiRecordSetString( hrec, 1, "epicranius" ); + ok( r == ERROR_SUCCESS, "failed to set string: %d\n", r ); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok( r == ERROR_SUCCESS, "failed to update row: %d\n", r ); + + /* try another valid operation for joins */ + r = MsiViewModify(hview, MSIMODIFY_REFRESH, hrec); + todo_wine ok( r == ERROR_SUCCESS, "failed to refresh row: %d\n", r ); + + /* try an invalid operation for joins */ + r = MsiViewModify(hview, MSIMODIFY_DELETE, hrec); + ok( r == ERROR_FUNCTION_FAILED, "unexpected result: %d\n", r ); + + r = MsiRecordSetString( hrec, 2, "epicranius" ); + ok( r == ERROR_SUCCESS, "failed to set string: %d\n", r ); + + /* primary key cannot be updated */ + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok( r == ERROR_FUNCTION_FAILED, "failed to update row: %d\n", r ); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + size = MAX_PATH; + r = MsiRecordGetString( hrec, 1, buf, &size ); + ok( r == ERROR_SUCCESS, "failed to get record string: %d\n", r ); + ok( !lstrcmp( buf, "epicranius" ), "expected 'epicranius', got %s\n", buf ); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + MsiCloseHandle(hdb); + DeleteFile(msifile); +} + +static void test_temporary_table(void) +{ + MSICONDITION cond; + MSIHANDLE hdb = 0, view = 0, rec; + const char *query; + UINT r; + char buf[0x10]; + DWORD sz; + + cond = MsiDatabaseIsTablePersistent(0, NULL); + ok( cond == MSICONDITION_ERROR, "wrong return condition\n"); + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, NULL); + ok( cond == MSICONDITION_ERROR, "wrong return condition\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "_Tables"); + ok( cond == MSICONDITION_NONE, "wrong return condition\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "_Columns"); + ok( cond == MSICONDITION_NONE, "wrong return condition\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "_Storages"); + ok( cond == MSICONDITION_NONE, "wrong return condition\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "_Streams"); + ok( cond == MSICONDITION_NONE, "wrong return condition\n"); + + query = "CREATE TABLE `P` ( `B` SHORT NOT NULL, `C` CHAR(255) PRIMARY KEY `C`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "P"); + ok( cond == MSICONDITION_TRUE, "wrong return condition\n"); + + query = "CREATE TABLE `P2` ( `B` SHORT NOT NULL, `C` CHAR(255) PRIMARY KEY `C`) HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "P2"); + ok( cond == MSICONDITION_TRUE, "wrong return condition\n"); + + query = "CREATE TABLE `T` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`) HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "T"); + ok( cond == MSICONDITION_FALSE, "wrong return condition\n"); + + query = "CREATE TABLE `T2` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + query = "SELECT * FROM `T2`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + cond = MsiDatabaseIsTablePersistent(hdb, "T2"); + ok( cond == MSICONDITION_NONE, "wrong return condition\n"); + + query = "CREATE TABLE `T3` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) PRIMARY KEY `C`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "T3"); + ok( cond == MSICONDITION_TRUE, "wrong return condition\n"); + + query = "CREATE TABLE `T4` ( `B` SHORT NOT NULL, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_FUNCTION_FAILED, "failed to add table\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "T4"); + ok( cond == MSICONDITION_NONE, "wrong return condition\n"); + + query = "CREATE TABLE `T5` ( `B` SHORT NOT NULL TEMP, `C` CHAR(255) TEMP PRIMARY KEY `C`) HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to add table\n"); + + query = "select * from `T`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "failed to query table\n"); + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + ok(r == ERROR_SUCCESS, "failed to get column info\n"); + + sz = sizeof buf; + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to get string\n"); + ok( 0 == strcmp("G255", buf), "wrong column type\n"); + + sz = sizeof buf; + r = MsiRecordGetString(rec, 2, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to get string\n"); + ok( 0 == strcmp("j2", buf), "wrong column type\n"); + + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + + /* query the table data */ + rec = 0; + r = do_query(hdb, "select * from `_Tables` where `Name` = 'T'", &rec); + ok( r == ERROR_SUCCESS, "temporary table exists in _Tables\n"); + MsiCloseHandle( rec ); + + /* query the column data */ + rec = 0; + r = do_query(hdb, "select * from `_Columns` where `Table` = 'T' AND `Name` = 'B'", &rec); + ok( r == ERROR_NO_MORE_ITEMS, "temporary table exists in _Columns\n"); + if (rec) MsiCloseHandle( rec ); + + r = do_query(hdb, "select * from `_Columns` where `Table` = 'T' AND `Name` = 'C'", &rec); + ok( r == ERROR_NO_MORE_ITEMS, "temporary table exists in _Columns\n"); + if (rec) MsiCloseHandle( rec ); + + MsiCloseHandle( hdb ); + + DeleteFile(msifile); +} + +static void test_alter(void) +{ + MSICONDITION cond; + MSIHANDLE hdb = 0; + const char *query; + UINT r; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + query = "CREATE TABLE `T` ( `B` SHORT NOT NULL TEMPORARY, `C` CHAR(255) TEMPORARY PRIMARY KEY `C`) HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to add table\n"); + + cond = MsiDatabaseIsTablePersistent(hdb, "T"); + ok( cond == MSICONDITION_FALSE, "wrong return condition\n"); + + query = "ALTER TABLE `T` HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to hold table %d\n", r); + + query = "ALTER TABLE `T` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to free table\n"); + + query = "ALTER TABLE `T` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to free table\n"); + + query = "ALTER TABLE `T` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to free table\n"); + + query = "ALTER TABLE `T` HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "failed to hold table %d\n", r); + + /* table T is removed */ + query = "SELECT * FROM `T`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* create the table again */ + query = "CREATE TABLE `U` ( `A` INTEGER, `B` INTEGER PRIMARY KEY `B`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* up the ref count */ + query = "ALTER TABLE `U` HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to free table\n"); + + /* add column, no data type */ + query = "ALTER TABLE `U` ADD `C`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "ALTER TABLE `U` ADD `C` INTEGER"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* add column C again */ + query = "ALTER TABLE `U` ADD `C` INTEGER"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "ALTER TABLE `U` ADD `D` INTEGER TEMPORARY"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `U` ( `A`, `B`, `C`, `D` ) VALUES ( 1, 2, 3, 4 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "ALTER TABLE `U` ADD `D` INTEGER TEMPORARY HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `U` ( `A`, `B`, `C`, `D` ) VALUES ( 5, 6, 7, 8 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `U` WHERE `D` = 8"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "ALTER TABLE `U` ADD `D` INTEGER TEMPORARY FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "ALTER COLUMN `D` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* drop the ref count */ + query = "ALTER TABLE `U` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* table is not empty */ + query = "SELECT * FROM `U`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* column D is removed */ + query = "SELECT * FROM `U` WHERE `D` = 8"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `U` ( `A`, `B`, `C`, `D` ) VALUES ( 9, 10, 11, 12 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* add the column again */ + query = "ALTER TABLE `U` ADD `E` INTEGER TEMPORARY HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* up the ref count */ + query = "ALTER TABLE `U` HOLD"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `U` ( `A`, `B`, `C`, `E` ) VALUES ( 13, 14, 15, 16 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `U` WHERE `E` = 16"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* drop the ref count */ + query = "ALTER TABLE `U` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `U` ( `A`, `B`, `C`, `E` ) VALUES ( 17, 18, 19, 20 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `U` WHERE `E` = 20"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* drop the ref count */ + query = "ALTER TABLE `U` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* table still exists */ + query = "SELECT * FROM `U`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* col E is removed */ + query = "SELECT * FROM `U` WHERE `E` = 20"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `U` ( `A`, `B`, `C`, `E` ) VALUES ( 20, 21, 22, 23 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* drop the ref count once more */ + query = "ALTER TABLE `U` FREE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* table still exists */ + query = "SELECT * FROM `U`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle( hdb ); + DeleteFile(msifile); +} + +static void test_integers(void) +{ + MSIHANDLE hdb = 0, view = 0, rec = 0; + DWORD count, i; + const char *query; + UINT r; + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + /* create a table */ + query = "CREATE TABLE `integers` ( " + "`one` SHORT, `two` INT, `three` INTEGER, `four` LONG, " + "`five` SHORT NOT NULL, `six` INT NOT NULL, " + "`seven` INTEGER NOT NULL, `eight` LONG NOT NULL " + "PRIMARY KEY `one`)"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `integers`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 8, "Expected 8, got %d\n", count); + ok(check_record(rec, 1, "one"), "Expected one\n"); + ok(check_record(rec, 2, "two"), "Expected two\n"); + ok(check_record(rec, 3, "three"), "Expected three\n"); + ok(check_record(rec, 4, "four"), "Expected four\n"); + ok(check_record(rec, 5, "five"), "Expected five\n"); + ok(check_record(rec, 6, "six"), "Expected six\n"); + ok(check_record(rec, 7, "seven"), "Expected seven\n"); + ok(check_record(rec, 8, "eight"), "Expected eight\n"); + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + count = MsiRecordGetFieldCount(rec); + ok(count == 8, "Expected 8, got %d\n", count); + ok(check_record(rec, 1, "I2"), "Expected I2\n"); + ok(check_record(rec, 2, "I2"), "Expected I2\n"); + ok(check_record(rec, 3, "I2"), "Expected I2\n"); + ok(check_record(rec, 4, "I4"), "Expected I4\n"); + ok(check_record(rec, 5, "i2"), "Expected i2\n"); + ok(check_record(rec, 6, "i2"), "Expected i2\n"); + ok(check_record(rec, 7, "i2"), "Expected i2\n"); + ok(check_record(rec, 8, "i4"), "Expected i4\n"); + MsiCloseHandle(rec); + + MsiViewClose(view); + MsiCloseHandle(view); + + /* insert values into it, NULL where NOT NULL is specified */ + query = "INSERT INTO `integers` ( `one`, `two`, `three`, `four`, `five`, `six`, `seven`, `eight` )" + "VALUES('', '', '', '', '', '', '', '')"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + + query = "SELECT * FROM `integers`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiRecordGetFieldCount(rec); + ok(r == -1, "record count wrong: %d\n", r); + + MsiCloseHandle(rec); + + /* insert legitimate values into it */ + query = "INSERT INTO `integers` ( `one`, `two`, `three`, `four`, `five`, `six`, `seven`, `eight` )" + "VALUES('', '2', '', '4', '5', '6', '7', '8')"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `integers`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetFieldCount(rec); + ok(r == 8, "record count wrong: %d\n", r); + + i = MsiRecordGetInteger(rec, 1); + ok(i == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", i); + i = MsiRecordGetInteger(rec, 3); + ok(i == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", i); + i = MsiRecordGetInteger(rec, 2); + ok(i == 2, "Expected 2, got %d\n", i); + i = MsiRecordGetInteger(rec, 4); + ok(i == 4, "Expected 4, got %d\n", i); + i = MsiRecordGetInteger(rec, 5); + ok(i == 5, "Expected 5, got %d\n", i); + i = MsiRecordGetInteger(rec, 6); + ok(i == 6, "Expected 6, got %d\n", i); + i = MsiRecordGetInteger(rec, 7); + ok(i == 7, "Expected 7, got %d\n", i); + i = MsiRecordGetInteger(rec, 8); + ok(i == 8, "Expected 8, got %d\n", i); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = DeleteFile(msifile); + ok(r == TRUE, "file didn't exist after commit\n"); +} + +static void test_update(void) +{ + MSIHANDLE hdb = 0, view = 0, rec = 0; + CHAR result[MAX_PATH]; + const char *query; + DWORD size; + UINT r; + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + /* create the Control table */ + query = "CREATE TABLE `Control` ( " + "`Dialog_` CHAR(72) NOT NULL, `Control` CHAR(50) NOT NULL, `Type` SHORT NOT NULL, " + "`X` SHORT NOT NULL, `Y` SHORT NOT NULL, `Width` SHORT NOT NULL, `Height` SHORT NOT NULL," + "`Attributes` LONG, `Property` CHAR(50), `Text` CHAR(0) LOCALIZABLE, " + "`Control_Next` CHAR(50), `Help` CHAR(50) LOCALIZABLE PRIMARY KEY `Dialog_`, `Control`)"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* add a control */ + query = "INSERT INTO `Control` ( " + "`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, " + "`Property`, `Text`, `Control_Next`, `Help` )" + "VALUES('ErrorDialog', 'ErrorText', '1', '5', '5', '5', '5', '', '', '', '')"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* add a second control */ + query = "INSERT INTO `Control` ( " + "`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, " + "`Property`, `Text`, `Control_Next`, `Help` )" + "VALUES('ErrorDialog', 'Button', '1', '5', '5', '5', '5', '', '', '', '')"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* add a third control */ + query = "INSERT INTO `Control` ( " + "`Dialog_`, `Control`, `Type`, `X`, `Y`, `Width`, `Height`, " + "`Property`, `Text`, `Control_Next`, `Help` )" + "VALUES('AnotherDialog', 'ErrorText', '1', '5', '5', '5', '5', '', '', '', '')"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* bad table */ + query = "UPDATE `NotATable` SET `Text` = 'this is text' WHERE `Dialog_` = 'ErrorDialog'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* bad set column */ + query = "UPDATE `Control` SET `NotAColumn` = 'this is text' WHERE `Dialog_` = 'ErrorDialog'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* bad where condition */ + query = "UPDATE `Control` SET `Text` = 'this is text' WHERE `NotAColumn` = 'ErrorDialog'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + /* just the dialog_ specified */ + query = "UPDATE `Control` SET `Text` = 'this is text' WHERE `Dialog_` = 'ErrorDialog'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* check the modified text */ + query = "SELECT `Text` FROM `Control` WHERE `Control` = 'ErrorText'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, result, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, result, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrlen(result), "Expected an empty string, got %s\n", result); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* dialog_ and control specified */ + query = "UPDATE `Control` SET `Text` = 'this is text' WHERE `Dialog_` = 'ErrorDialog' AND `Control` = 'ErrorText'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* check the modified text */ + query = "SELECT `Text` FROM `Control` WHERE `Control` = 'ErrorText'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, result, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, result, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrlen(result), "Expected an empty string, got %s\n", result); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* no where condition */ + query = "UPDATE `Control` SET `Text` = 'this is text'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* check the modified text */ + query = "SELECT `Text` FROM `Control`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, result, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, result, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(rec, 1, result, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(result, "this is text"), "Expected `this is text`, got %s\n", result); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(view); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(view); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "CREATE TABLE `Apple` ( `Banana` CHAR(72) NOT NULL, " + "`Orange` CHAR(72), `Pear` INT PRIMARY KEY `Banana`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Apple` ( `Banana`, `Orange`, `Pear` )" + "VALUES('one', 'two', 3)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Apple` ( `Banana`, `Orange`, `Pear` )" + "VALUES('three', 'four', 5)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Apple` ( `Banana`, `Orange`, `Pear` )" + "VALUES('six', 'two', 7)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + rec = MsiCreateRecord(2); + MsiRecordSetInteger(rec, 1, 8); + MsiRecordSetString(rec, 2, "two"); + + query = "UPDATE `Apple` SET `Pear` = ? WHERE `Orange` = ?"; + r = run_query(hdb, rec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(rec); + + query = "SELECT `Pear` FROM `Apple` ORDER BY `Orange`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == 8, "Expected 8, got %d\n", r); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == 8, "Expected 8, got %d\n", r); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == 5, "Expected 5, got %d\n", r); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expectd ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "MsiDatabaseCommit failed\n"); + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + DeleteFile(msifile); +} + +static void test_special_tables(void) +{ + const char *query; + MSIHANDLE hdb = 0; + UINT r; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `_Properties` ( " + "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + query = "CREATE TABLE `_Storages` ( " + "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "created _Streams table\n"); + + query = "CREATE TABLE `_Streams` ( " + "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "created _Streams table\n"); + + query = "CREATE TABLE `_Tables` ( " + "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "created _Tables table\n"); + + query = "CREATE TABLE `_Columns` ( " + "`foo` INT NOT NULL, `bar` INT LOCALIZABLE PRIMARY KEY `foo`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "created _Columns table\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); +} + +static void test_tables_order(void) +{ + const char *query; + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + UINT r; + char buffer[100]; + DWORD sz; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `foo` ( " + "`baz` INT NOT NULL PRIMARY KEY `baz`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + query = "CREATE TABLE `bar` ( " + "`foo` INT NOT NULL PRIMARY KEY `foo`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + query = "CREATE TABLE `baz` ( " + "`bar` INT NOT NULL, " + "`baz` INT NOT NULL, " + "`foo` INT NOT NULL PRIMARY KEY `bar`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + /* The names of the tables in the _Tables table must + be in the same order as these names are created in + the strings table. */ + query = "SELECT * FROM `_Tables`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "foo"), "Expected foo, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "bar"), "Expected bar, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* The names of the tables in the _Columns table must + be in the same order as these names are created in + the strings table. */ + query = "SELECT * FROM `_Columns`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "foo"), "Expected foo, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "bar"), "Expected bar, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "foo"), "Expected foo, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "bar"), "Expected bar, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "foo"), "Expected foo, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + DeleteFile(msifile); +} + +static void test_rows_order(void) +{ + const char *query; + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + UINT r; + char buffer[100]; + DWORD sz; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `foo` ( " + "`bar` LONGCHAR NOT NULL PRIMARY KEY `bar`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + r = run_query(hdb, 0, "INSERT INTO `foo` " + "( `bar` ) VALUES ( 'A' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `foo` " + "( `bar` ) VALUES ( 'B' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `foo` " + "( `bar` ) VALUES ( 'C' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `foo` " + "( `bar` ) VALUES ( 'D' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `foo` " + "( `bar` ) VALUES ( 'E' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `foo` " + "( `bar` ) VALUES ( 'F' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + query = "CREATE TABLE `bar` ( " + "`foo` LONGCHAR NOT NULL, " + "`baz` LONGCHAR NOT NULL " + "PRIMARY KEY `foo` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + r = run_query(hdb, 0, "INSERT INTO `bar` " + "( `foo`, `baz` ) VALUES ( 'C', 'E' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `bar` " + "( `foo`, `baz` ) VALUES ( 'F', 'A' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `bar` " + "( `foo`, `baz` ) VALUES ( 'A', 'B' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + r = run_query(hdb, 0, "INSERT INTO `bar` " + "( `foo`, `baz` ) VALUES ( 'D', 'E' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table\n"); + + /* The rows of the table must be ordered by the column values of + each row. For strings, the column value is the string id + in the string table. */ + + query = "SELECT * FROM `bar`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "A"), "Expected A, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "B"), "Expected B, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "C"), "Expected E, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "E"), "Expected E, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "D"), "Expected D, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "E"), "Expected E, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "F"), "Expected F, got %s\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "A"), "Expected A, got %s\n", buffer); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + DeleteFile(msifile); +} + +static void test_collation(void) +{ + static const WCHAR query1[] = + {'I','N','S','E','R','T',' ','I','N','T','O',' ','`','b','a','r','`',' ', + '(','`','f','o','o','`',',','`','b','a','z','`',')',' ','V','A','L','U','E','S',' ', + '(','\'','a',0x30a,'\'',',','\'','C','\'',')',0}; + static const WCHAR query2[] = + {'I','N','S','E','R','T',' ','I','N','T','O',' ','`','b','a','r','`',' ', + '(','`','f','o','o','`',',','`','b','a','z','`',')',' ','V','A','L','U','E','S',' ', + '(','\'',0xe5,'\'',',','\'','D','\'',')',0}; + static const WCHAR query3[] = + {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','b','a','z','`',' ', + '(',' ','`','a',0x30a,'`',' ','L','O','N','G','C','H','A','R',' ','N','O','T',' ','N','U','L','L',',', + ' ','`',0xe5,'`',' ','L','O','N','G','C','H','A','R',' ','N','O','T',' ','N','U','L','L',' ', + 'P','R','I','M','A','R','Y',' ','K','E','Y',' ','`','a',0x30a,'`',')',0}; + static const WCHAR query4[] = + {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','a',0x30a,'`',' ', + '(',' ','`','f','o','o','`',' ','L','O','N','G','C','H','A','R',' ','N','O','T',' ', + 'N','U','L','L',' ','P','R','I','M','A','R','Y',' ','K','E','Y',' ','`','f','o','o','`',')',0}; + static const WCHAR query5[] = + {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`',0xe5,'`',' ', + '(',' ','`','f','o','o','`',' ','L','O','N','G','C','H','A','R',' ','N','O','T',' ', + 'N','U','L','L',' ','P','R','I','M','A','R','Y',' ','K','E','Y',' ','`','f','o','o','`',')',0}; + static const WCHAR query6[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','b','a','r','`',' ','W','H','E','R','E', + ' ','`','f','o','o','`',' ','=','\'',0xe5,'\'',0}; + static const WCHAR letter_C[] = {'C',0}; + static const WCHAR letter_D[] = {'D',0}; + static const WCHAR letter_a_ring[] = {'a',0x30a,0}; + static const WCHAR letter_a_with_ring[] = {0xe5,0}; + const char *query; + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + UINT r; + char buffer[100]; + WCHAR bufferW[100]; + DWORD sz; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `bar` ( " + "`foo` LONGCHAR NOT NULL, " + "`baz` LONGCHAR NOT NULL " + "PRIMARY KEY `foo` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "failed to create table\n"); + + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "wrong error %u\n", r); + + r = run_query(hdb, 0, "INSERT INTO `bar` " + "( `foo`, `baz` ) VALUES ( '\2', 'A' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table %u\n", r); + + r = run_query(hdb, 0, "INSERT INTO `bar` " + "( `foo`, `baz` ) VALUES ( '\1', 'B' )"); + ok(r == ERROR_SUCCESS, "cannot add value to table %u\n", r); + + r = run_queryW(hdb, 0, query1); + ok(r == ERROR_SUCCESS, "cannot add value to table %u\n", r); + + r = run_queryW(hdb, 0, query2); + ok(r == ERROR_SUCCESS, "cannot add value to table %u\n", r); + + r = run_queryW(hdb, 0, query3); + ok(r == ERROR_SUCCESS, "cannot create table %u\n", r); + + r = run_queryW(hdb, 0, query4); + ok(r == ERROR_SUCCESS, "cannot create table %u\n", r); + + r = run_queryW(hdb, 0, query5); + ok(r == ERROR_SUCCESS, "cannot create table %u\n", r); + + query = "SELECT * FROM `bar`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "\2"), "Expected \\2, got '%s'\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "A"), "Expected A, got '%s'\n", buffer); + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "\1"), "Expected \\1, got '%s'\n", buffer); + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "B"), "Expected B, got '%s'\n", buffer); + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(bufferW) / sizeof(bufferW[0]); + r = MsiRecordGetStringW(hrec, 1, bufferW, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!memcmp(bufferW, letter_a_ring, sizeof(letter_a_ring)), + "Expected %s, got %s\n", wine_dbgstr_w(letter_a_ring), wine_dbgstr_w(bufferW)); + sz = sizeof(bufferW) / sizeof(bufferW[0]); + r = MsiRecordGetStringW(hrec, 2, bufferW, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpW(bufferW, letter_C), "Expected C, got %s\n", wine_dbgstr_w(bufferW)); + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(bufferW) / sizeof(bufferW[0]); + r = MsiRecordGetStringW(hrec, 1, bufferW, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!memcmp(bufferW, letter_a_with_ring, sizeof(letter_a_with_ring)), + "Expected %s, got %s\n", wine_dbgstr_w(letter_a_with_ring), wine_dbgstr_w(bufferW)); + sz = sizeof(bufferW) / sizeof(bufferW[0]); + r = MsiRecordGetStringW(hrec, 2, bufferW, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpW(bufferW, letter_D), "Expected D, got %s\n", wine_dbgstr_w(bufferW)); + MsiCloseHandle(hrec); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiDatabaseOpenViewW(hdb, query6, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + sz = sizeof(bufferW) / sizeof(bufferW[0]); + r = MsiRecordGetStringW(hrec, 1, bufferW, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!memcmp(bufferW, letter_a_with_ring, sizeof(letter_a_with_ring)), + "Expected %s, got %s\n", wine_dbgstr_w(letter_a_with_ring), wine_dbgstr_w(bufferW)); + sz = sizeof(bufferW) / sizeof(bufferW[0]); + r = MsiRecordGetStringW(hrec, 2, bufferW, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpW(bufferW, letter_D), "Expected D, got %s\n", wine_dbgstr_w(bufferW)); + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "MsiViewFetch failed\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + DeleteFile(msifile); +} + +static void test_select_markers(void) +{ + MSIHANDLE hdb = 0, rec, view, res; + LPCSTR query; + UINT r; + DWORD size; + CHAR buf[MAX_PATH]; + + hdb = create_db(); + ok( hdb, "failed to create db\n"); + + r = run_query(hdb, 0, + "CREATE TABLE `Table` (`One` CHAR(72), `Two` CHAR(72), `Three` SHORT PRIMARY KEY `One`, `Two`, `Three`)"); + ok(r == S_OK, "cannot create table: %d\n", r); + + r = run_query(hdb, 0, "INSERT INTO `Table` " + "( `One`, `Two`, `Three` ) VALUES ( 'apple', 'one', 1 )"); + ok(r == S_OK, "cannot add file to the Media table: %d\n", r); + + r = run_query(hdb, 0, "INSERT INTO `Table` " + "( `One`, `Two`, `Three` ) VALUES ( 'apple', 'two', 1 )"); + ok(r == S_OK, "cannot add file to the Media table: %d\n", r); + + r = run_query(hdb, 0, "INSERT INTO `Table` " + "( `One`, `Two`, `Three` ) VALUES ( 'apple', 'two', 2 )"); + ok(r == S_OK, "cannot add file to the Media table: %d\n", r); + + r = run_query(hdb, 0, "INSERT INTO `Table` " + "( `One`, `Two`, `Three` ) VALUES ( 'banana', 'three', 3 )"); + ok(r == S_OK, "cannot add file to the Media table: %d\n", r); + + rec = MsiCreateRecord(2); + MsiRecordSetString(rec, 1, "apple"); + MsiRecordSetString(rec, 2, "two"); + + query = "SELECT * FROM `Table` WHERE `One`=? AND `Two`=? ORDER BY `Three`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(view, rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &res); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(res, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "apple"), "Expected apple, got %s\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(res, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "two"), "Expected two, got %s\n", buf); + + r = MsiRecordGetInteger(res, 3); + ok(r == 1, "Expected 1, got %d\n", r); + + MsiCloseHandle(res); + + r = MsiViewFetch(view, &res); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(res, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "apple"), "Expected apple, got %s\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(res, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "two"), "Expected two, got %s\n", buf); + + r = MsiRecordGetInteger(res, 3); + ok(r == 2, "Expected 2, got %d\n", r); + + MsiCloseHandle(res); + + r = MsiViewFetch(view, &res); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + + rec = MsiCreateRecord(2); + MsiRecordSetString(rec, 1, "one"); + MsiRecordSetInteger(rec, 2, 1); + + query = "SELECT * FROM `Table` WHERE `Two`<>? AND `Three`>? ORDER BY `Three`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &res); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(res, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "apple"), "Expected apple, got %s\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(res, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "two"), "Expected two, got %s\n", buf); + + r = MsiRecordGetInteger(res, 3); + ok(r == 2, "Expected 2, got %d\n", r); + + MsiCloseHandle(res); + + r = MsiViewFetch(view, &res); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(res, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "banana"), "Expected banana, got %s\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(res, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "three"), "Expected three, got %s\n", buf); + + r = MsiRecordGetInteger(res, 3); + ok(r == 3, "Expected 3, got %d\n", r); + + MsiCloseHandle(res); + + r = MsiViewFetch(view, &res); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + MsiCloseHandle(hdb); + DeleteFile(msifile); +} + +static void test_viewmodify_update(void) +{ + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + UINT i, test_max, offset, count; + const char *query; + UINT r; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `table` (`A` INT, `B` INT PRIMARY KEY `A`)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + query = "INSERT INTO `table` (`A`, `B`) VALUES (1, 2)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + query = "INSERT INTO `table` (`A`, `B`) VALUES (3, 4)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + query = "INSERT INTO `table` (`A`, `B`) VALUES (5, 6)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + query = "SELECT `B` FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordSetInteger(hrec, 1, 0); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed: %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 0, "Expected 0, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 3, "Expected 3, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 4, "Expected 4, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 5, "Expected 5, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 6, "Expected 6, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* loop through all elements */ + query = "SELECT `B` FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + while (TRUE) + { + r = MsiViewFetch(hview, &hrec); + if (r != ERROR_SUCCESS) + break; + + r = MsiRecordSetInteger(hrec, 1, 0); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed: %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + } + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 0, "Expected 0, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 3, "Expected 3, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 0, "Expected 0, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 5, "Expected 5, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 0, "Expected 0, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "CREATE TABLE `table2` (`A` INT, `B` INT PRIMARY KEY `A`)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + query = "INSERT INTO `table2` (`A`, `B`) VALUES (?, ?)"; + r = MsiDatabaseOpenView( hdb, query, &hview ); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + + test_max = 100; + offset = 1234; + for(i = 0; i < test_max; i++) + { + + hrec = MsiCreateRecord( 2 ); + MsiRecordSetInteger( hrec, 1, test_max - i ); + MsiRecordSetInteger( hrec, 2, i ); + + r = MsiViewExecute( hview, hrec ); + ok(r == ERROR_SUCCESS, "Got %d\n", r); + + r = MsiCloseHandle( hrec ); + ok(r == ERROR_SUCCESS, "Got %d\n", r); + } + + r = MsiViewClose( hview ); + ok(r == ERROR_SUCCESS, "Got %d\n", r); + r = MsiCloseHandle( hview ); + ok(r == ERROR_SUCCESS, "Got %d\n", r); + + /* Update. */ + query = "SELECT * FROM `table2` ORDER BY `B`"; + r = MsiDatabaseOpenView( hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute( hview, 0 ); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + count = 0; + while (MsiViewFetch( hview, &hrec ) == ERROR_SUCCESS) + { + UINT b = MsiRecordGetInteger( hrec, 2 ); + + r = MsiRecordSetInteger( hrec, 2, b + offset); + ok(r == ERROR_SUCCESS, "Got %d\n", r); + + r = MsiViewModify( hview, MSIMODIFY_UPDATE, hrec ); + ok(r == ERROR_SUCCESS, "Got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + count++; + } + ok(count == test_max, "Got count %d\n", count); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* Recheck. */ + query = "SELECT * FROM `table2` ORDER BY `B`"; + r = MsiDatabaseOpenView( hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute( hview, 0 ); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + count = 0; + while (MsiViewFetch( hview, &hrec ) == ERROR_SUCCESS) + { + UINT a = MsiRecordGetInteger( hrec, 1 ); + UINT b = MsiRecordGetInteger( hrec, 2 ); + ok( ( test_max - a + offset) == b, "Got (%d, %d), expected (%d, %d)\n", + a, b, test_max - a + offset, b); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + count++; + } + ok(count == test_max, "Got count %d\n", count); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n"); +} + +static void test_viewmodify_assign(void) +{ + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + const char *query; + UINT r; + + /* setup database */ + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `table` (`A` INT, `B` INT PRIMARY KEY `A`)"; + r = run_query( hdb, 0, query ); + ok(r == ERROR_SUCCESS, "query failed\n"); + + /* assign to view, new primary key */ + query = "SELECT * FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + hrec = MsiCreateRecord(2); + ok(hrec != 0, "MsiCreateRecord failed\n"); + + r = MsiRecordSetInteger(hrec, 1, 1); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetInteger(hrec, 2, 2); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + + r = MsiViewModify(hview, MSIMODIFY_ASSIGN, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed: %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* assign to view, primary key matches */ + query = "SELECT * FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + + hrec = MsiCreateRecord(2); + ok(hrec != 0, "MsiCreateRecord failed\n"); + + r = MsiRecordSetInteger(hrec, 1, 1); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + r = MsiRecordSetInteger(hrec, 2, 4); + ok(r == ERROR_SUCCESS, "failed to set integer\n"); + + r = MsiViewModify(hview, MSIMODIFY_ASSIGN, hrec); + ok(r == ERROR_SUCCESS, "MsiViewModify failed: %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "SELECT * FROM `table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "MsiViewFetch failed\n"); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + r = MsiRecordGetInteger(hrec, 2); + ok(r == 4, "Expected 4, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "failed to close record\n"); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* close database */ + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase close failed\n"); +} + +static const WCHAR data10[] = { /* MOO */ + 0x8001, 0x000b, +}; +static const WCHAR data11[] = { /* AAR */ + 0x8002, 0x8005, + 0x000c, 0x000f, +}; +static const char data12[] = /* _StringData */ + "MOOABAARCDonetwofourfive"; +static const WCHAR data13[] = { /* _StringPool */ +/* len, refs */ + 0, 0, /* string 0 '' */ + 0, 0, /* string 1 '' */ + 0, 0, /* string 2 '' */ + 0, 0, /* string 3 '' */ + 0, 0, /* string 4 '' */ + 3, 3, /* string 5 'MOO' */ + 1, 1, /* string 6 'A' */ + 1, 1, /* string 7 'B' */ + 3, 3, /* string 8 'AAR' */ + 1, 1, /* string 9 'C' */ + 1, 1, /* string a 'D' */ + 3, 1, /* string b 'one' */ + 3, 1, /* string c 'two' */ + 0, 0, /* string d '' */ + 4, 1, /* string e 'four' */ + 4, 1, /* string f 'five' */ +}; + +static void test_stringtable(void) +{ + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + IStorage *stg = NULL; + IStream *stm; + WCHAR name[0x20]; + HRESULT hr; + const char *query; + char buffer[MAX_PATH]; + WCHAR data[MAX_PATH]; + DWORD sz, read; + UINT r; + + static const DWORD mode = STGM_DIRECT | STGM_READ | STGM_SHARE_DENY_WRITE; + static const WCHAR stringdata[] = {0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0}; /* _StringData */ + static const WCHAR stringpool[] = {0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0}; /* _StringPool */ + static const WCHAR moo[] = {0x4840, 0x3e16, 0x4818, 0}; /* MOO */ + static const WCHAR aar[] = {0x4840, 0x3a8a, 0x481b, 0}; /* AAR */ + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `MOO` (`A` INT, `B` CHAR(72) PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `AAR` (`C` INT, `D` CHAR(72) PRIMARY KEY `C`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* insert persistent row */ + query = "INSERT INTO `MOO` (`A`, `B`) VALUES (1, 'one')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* insert persistent row */ + query = "INSERT INTO `AAR` (`C`, `D`) VALUES (2, 'two')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* open a view */ + query = "SELECT * FROM `MOO`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hrec = MsiCreateRecord(2); + + r = MsiRecordSetInteger(hrec, 1, 3); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiRecordSetString(hrec, 2, "three"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* insert a nonpersistent row */ + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* insert persistent row */ + query = "INSERT INTO `MOO` (`A`, `B`) VALUES (4, 'four')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* insert persistent row */ + query = "INSERT INTO `AAR` (`C`, `D`) VALUES (5, 'five')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `MOO`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetFieldCount(hrec); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "one"), "Expected one, got '%s'\n", buffer); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `AAR`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetFieldCount(hrec); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 2, "Expected 2, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "two"), "Expected two, got '%s'\n", buffer); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetFieldCount(hrec); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 5, "Expected 5, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "five"), "Expected five, got '%s'\n", buffer); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MultiByteToWideChar(CP_ACP, 0, msifile, -1, name, 0x20); + hr = StgOpenStorage(name, NULL, mode, NULL, 0, &stg); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(stg != NULL, "Expected non-NULL storage\n"); + + hr = IStorage_OpenStream(stg, moo, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(stm != NULL, "Expected non-NULL stream\n"); + + hr = IStream_Read(stm, data, MAX_PATH, &read); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(read == 4, "Expected 4, got %d\n", read); + todo_wine ok(!memcmp(data, data10, read), "Unexpected data\n"); + + hr = IStream_Release(stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + hr = IStorage_OpenStream(stg, aar, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(stm != NULL, "Expected non-NULL stream\n"); + + hr = IStream_Read(stm, data, MAX_PATH, &read); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(read == 8, "Expected 8, got %d\n", read); + todo_wine + { + ok(!memcmp(data, data11, read), "Unexpected data\n"); + } + + hr = IStream_Release(stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + hr = IStorage_OpenStream(stg, stringdata, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(stm != NULL, "Expected non-NULL stream\n"); + + hr = IStream_Read(stm, buffer, MAX_PATH, &read); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(read == 24, "Expected 24, got %d\n", read); + ok(!memcmp(buffer, data12, read), "Unexpected data\n"); + + hr = IStream_Release(stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + hr = IStorage_OpenStream(stg, stringpool, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(stm != NULL, "Expected non-NULL stream\n"); + + hr = IStream_Read(stm, data, MAX_PATH, &read); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + todo_wine + { + ok(read == 64, "Expected 64, got %d\n", read); + ok(!memcmp(data, data13, read), "Unexpected data\n"); + } + + hr = IStream_Release(stm); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + hr = IStorage_Release(stg); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + + DeleteFileA(msifile); +} + +static void test_viewmodify_delete(void) +{ + MSIHANDLE hdb = 0, hview = 0, hrec = 0; + UINT r; + const char *query; + char buffer[0x100]; + DWORD sz; + + DeleteFile(msifile); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `phone` ( " + "`id` INT, `name` CHAR(32), `number` CHAR(32) " + "PRIMARY KEY `id`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `phone` ( `id`, `name`, `number` )" + "VALUES('1', 'Alan', '5030581')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `phone` ( `id`, `name`, `number` )" + "VALUES('2', 'Barry', '928440')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `phone` ( `id`, `name`, `number` )" + "VALUES('3', 'Cindy', '2937550')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `phone` WHERE `id` <= 2"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* delete 1 */ + r = MsiViewModify(hview, MSIMODIFY_DELETE, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* delete 2 */ + r = MsiViewModify(hview, MSIMODIFY_DELETE, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `phone`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 3, "Expected 3, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 2, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "Cindy"), "Expected Cindy, got %s\n", buffer); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 3, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "2937550"), "Expected 2937550, got %s\n", buffer); + + r = MsiCloseHandle(hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); +} + +static const WCHAR _Tables[] = {0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0}; +static const WCHAR _StringData[] = {0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0}; +static const WCHAR _StringPool[] = {0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0}; + +static const WCHAR data14[] = { /* _StringPool */ +/* len, refs */ + 0, 0, /* string 0 '' */ +}; + +static const struct { + LPCWSTR name; + const void *data; + DWORD size; +} database_table_data[] = +{ + {_Tables, NULL, 0}, + {_StringData, NULL, 0}, + {_StringPool, data14, sizeof data14}, +}; + +static void enum_stream_names(IStorage *stg) +{ + IEnumSTATSTG *stgenum = NULL; + IStream *stm; + HRESULT hr; + STATSTG stat; + ULONG n, count; + BYTE data[MAX_PATH]; + BYTE check[MAX_PATH]; + DWORD sz; + + memset(check, 'a', MAX_PATH); + + hr = IStorage_EnumElements(stg, 0, NULL, 0, &stgenum); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + n = 0; + while(TRUE) + { + count = 0; + hr = IEnumSTATSTG_Next(stgenum, 1, &stat, &count); + if(FAILED(hr) || !count) + break; + + ok(!lstrcmpW(stat.pwcsName, database_table_data[n].name), + "Expected table %d name to match\n", n); + + stm = NULL; + hr = IStorage_OpenStream(stg, stat.pwcsName, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(stm != NULL, "Expected non-NULL stream\n"); + + CoTaskMemFree(stat.pwcsName); + + sz = MAX_PATH; + memset(data, 'a', MAX_PATH); + hr = IStream_Read(stm, data, sz, &count); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + + ok(count == database_table_data[n].size, + "Expected %d, got %d\n", database_table_data[n].size, count); + + if (!database_table_data[n].size) + ok(!memcmp(data, check, MAX_PATH), "data should not be changed\n"); + else + ok(!memcmp(data, database_table_data[n].data, database_table_data[n].size), + "Expected table %d data to match\n", n); + + IStream_Release(stm); + n++; + } + + ok(n == 3, "Expected 3, got %d\n", n); + + IEnumSTATSTG_Release(stgenum); +} + +static void test_defaultdatabase(void) +{ + UINT r; + HRESULT hr; + MSIHANDLE hdb; + IStorage *stg = NULL; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hdb); + + hr = StgOpenStorage(msifileW, NULL, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(stg != NULL, "Expected non-NULL stg\n"); + + enum_stream_names(stg); + + IStorage_Release(stg); + DeleteFileA(msifile); +} + +static void test_order(void) +{ + MSIHANDLE hdb, hview, hrec; + CHAR buffer[MAX_PATH]; + LPCSTR query; + UINT r, sz; + int val; + + hdb = create_db(); + ok(hdb, "failed to create db\n"); + + query = "CREATE TABLE `Empty` ( `A` SHORT NOT NULL PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Mesa` ( `A` SHORT NOT NULL, `B` SHORT, `C` SHORT PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Mesa` ( `A`, `B`, `C` ) VALUES ( 1, 2, 9 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Mesa` ( `A`, `B`, `C` ) VALUES ( 3, 4, 7 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Mesa` ( `A`, `B`, `C` ) VALUES ( 5, 6, 8 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Sideboard` ( `D` SHORT NOT NULL, `E` SHORT, `F` SHORT PRIMARY KEY `D`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Sideboard` ( `D`, `E`, `F` ) VALUES ( 10, 11, 18 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Sideboard` ( `D`, `E`, `F` ) VALUES ( 12, 13, 16 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Sideboard` ( `D`, `E`, `F` ) VALUES ( 14, 15, 17 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT `A`, `B` FROM `Mesa` ORDER BY `C`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 3, "Expected 3, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 4, "Expected 3, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 5, "Expected 5, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 6, "Expected 6, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 1, "Expected 1, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 2, "Expected 2, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `A`, `D` FROM `Mesa`, `Sideboard` ORDER BY `F`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 1, "Expected 1, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 12, "Expected 12, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 3, "Expected 3, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 12, "Expected 12, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 5, "Expected 5, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 12, "Expected 12, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 1, "Expected 1, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 14, "Expected 14, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 3, "Expected 3, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 14, "Expected 14, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 5, "Expected 5, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 14, "Expected 14, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 1, "Expected 1, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 10, "Expected 10, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 3, "Expected 3, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 10, "Expected 10, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(hrec, 1); + ok(val == 5, "Expected 5, got %d\n", val); + + val = MsiRecordGetInteger(hrec, 2); + ok(val == 10, "Expected 10, got %d\n", val); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Empty` ORDER BY `A`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "CREATE TABLE `Buffet` ( `One` CHAR(72), `Two` SHORT PRIMARY KEY `One`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Buffet` ( `One`, `Two` ) VALUES ( 'uno', 2)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Buffet` ( `One`, `Two` ) VALUES ( 'dos', 3)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Buffet` ( `One`, `Two` ) VALUES ( 'tres', 1)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Buffet` WHERE `One` = 'dos' ORDER BY `Two`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = sizeof(buffer); + r = MsiRecordGetString(hrec, 1, buffer, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buffer, "dos"), "Expected \"dos\", got \"%s\"\n", buffer); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 3, "Expected 3, got %d\n", r); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); +} + +static void test_viewmodify_delete_temporary(void) +{ + MSIHANDLE hdb, hview, hrec; + const char *query; + UINT r; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` SHORT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hrec = MsiCreateRecord(1); + MsiRecordSetInteger(hrec, 1, 1); + + r = MsiViewModify(hview, MSIMODIFY_INSERT, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + + hrec = MsiCreateRecord(1); + MsiRecordSetInteger(hrec, 1, 2); + + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + + hrec = MsiCreateRecord(1); + MsiRecordSetInteger(hrec, 1, 3); + + r = MsiViewModify(hview, MSIMODIFY_INSERT, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + + hrec = MsiCreateRecord(1); + MsiRecordSetInteger(hrec, 1, 4); + + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Table` WHERE `A` = 2"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewModify(hview, MSIMODIFY_DELETE, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Table` WHERE `A` = 3"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewModify(hview, MSIMODIFY_DELETE, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Table` ORDER BY `A`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 4, "Expected 4, got %d\n", r); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_deleterow(void) +{ + MSIHANDLE hdb, hview, hrec; + const char *query; + char buf[MAX_PATH]; + UINT r; + DWORD size; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` (`A`) VALUES ('one')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` (`A`) VALUES ('two')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DELETE FROM `Table` WHERE `A` = 'one'"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hdb); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "two"), "Expected two, got %s\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static const CHAR import_dat[] = "A\n" + "s72\n" + "Table\tA\n" + "This is a new 'string' ok\n"; + +static void test_quotes(void) +{ + MSIHANDLE hdb, hview, hrec; + const char *query; + char buf[MAX_PATH]; + UINT r; + DWORD size; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( 'This is a 'string' ok' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( \"This is a 'string' ok\" )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( \"test\" )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( 'This is a ''string'' ok' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( 'This is a '''string''' ok' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( 'This is a \'string\' ok' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( 'This is a \"string\" ok' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "This is a \"string\" ok"), + "Expected \"This is a \"string\" ok\", got %s\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + write_file("import.idt", import_dat, (sizeof(import_dat) - 1) * sizeof(char)); + + r = MsiDatabaseImportA(hdb, CURR_DIR, "import.idt"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + DeleteFileA("import.idt"); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmp(buf, "This is a new 'string' ok"), + "Expected \"This is a new 'string' ok\", got %s\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_carriagereturn(void) +{ + MSIHANDLE hdb, hview, hrec; + const char *query; + char buf[MAX_PATH]; + UINT r; + DWORD size; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table`\r ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` \r( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE\r TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE\r `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` (\r `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A`\r CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72)\r NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT\r NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT \rNULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL\r PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL \rPRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY\r KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY \rKEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY\r `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A`\r )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )\r"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `\rOne` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Tw\ro` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Three\r` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Four` ( `A\r` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Four` ( `\rA` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Four` ( `A` CHAR(72\r) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Four` ( `A` CHAR(\r72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Four` ( `A` CHAR(72) NOT NULL PRIMARY KEY `\rA` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Four` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A\r` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Four` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A\r` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `_Tables`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "\rOne"), "Expected \"\\rOne\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Tw\ro"), "Expected \"Tw\\ro\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Three\r"), "Expected \"Three\r\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_noquotes(void) +{ + MSIHANDLE hdb, hview, hrec; + const char *query; + char buf[MAX_PATH]; + UINT r; + DWORD size; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE Table ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( A CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table2` ( `A` CHAR(72) NOT NULL PRIMARY KEY A )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table3` ( A CHAR(72) NOT NULL PRIMARY KEY A )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `_Tables`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Table"), "Expected \"Table\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Table2"), "Expected \"Table2\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Table3"), "Expected \"Table3\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `_Columns`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Table"), "Expected \"Table\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Table2"), "Expected \"Table2\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Table3"), "Expected \"Table3\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "INSERT INTO Table ( `A` ) VALUES ( 'hi' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `Table` ( A ) VALUES ( 'hi' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` ( `A` ) VALUES ( hi )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM Table WHERE `A` = 'hi'"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `Table` WHERE `A` = hi"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM Table"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM Table2"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Table` WHERE A = 'hi'"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void read_file_data(LPCSTR filename, LPSTR buffer) +{ + HANDLE file; + DWORD read; + + file = CreateFileA( filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + ZeroMemory(buffer, MAX_PATH); + ReadFile(file, buffer, MAX_PATH, &read, NULL); + CloseHandle(file); +} + +static void test_forcecodepage(void) +{ + MSIHANDLE hdb; + const char *query; + char buffer[MAX_PATH]; + UINT r; + + DeleteFile(msifile); + GetCurrentDirectoryA(MAX_PATH, CURR_DIR); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `_ForceCodepage`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `_ForceCodepage`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `_ForceCodepage`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + MsiCloseHandle(hdb); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_DIRECT, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `_ForceCodepage`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + r = MsiDatabaseExport(hdb, "_ForceCodepage", CURR_DIR, "forcecodepage.idt"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + read_file_data("forcecodepage.idt", buffer); + ok(!lstrcmpA(buffer, "\r\n\r\n0\t_ForceCodepage\r\n"), + "Expected \"\r\n\r\n0\t_ForceCodepage\r\n\", got \"%s\"\n", buffer); + + create_file_data("forcecodepage.idt", "\r\n\r\n850\t_ForceCodepage\r\n", 0); + + r = MsiDatabaseImportA(hdb, CURR_DIR, "forcecodepage.idt"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseExport(hdb, "_ForceCodepage", CURR_DIR, "forcecodepage.idt"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + read_file_data("forcecodepage.idt", buffer); + ok(!lstrcmpA(buffer, "\r\n\r\n850\t_ForceCodepage\r\n"), + "Expected \"\r\n\r\n850\t_ForceCodepage\r\n\", got \"%s\"\n", buffer); + + create_file_data("forcecodepage.idt", "\r\n\r\n9999\t_ForceCodepage\r\n", 0); + + r = MsiDatabaseImportA(hdb, CURR_DIR, "forcecodepage.idt"); + ok(r == ERROR_FUNCTION_FAILED, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hdb); + DeleteFileA(msifile); + DeleteFileA("forcecodepage.idt"); +} + +static void test_viewmodify_refresh(void) +{ + MSIHANDLE hdb, hview, hrec; + const char *query; + char buffer[MAX_PATH]; + UINT r; + DWORD size; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` CHAR(72) NOT NULL, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 'hi', 1 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Table`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "UPDATE `Table` SET `B` = 2 WHERE `A` = 'hi'"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewModify(hview, MSIMODIFY_REFRESH, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buffer, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buffer, "hi"), "Expected \"hi\", got \"%s\"\n", buffer); + ok(size == 2, "Expected 2, got %d\n", size); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 'hello', 3 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Table` WHERE `B` = 3"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "UPDATE `Table` SET `B` = 2 WHERE `A` = 'hello'"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 'hithere', 3 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewModify(hview, MSIMODIFY_REFRESH, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buffer, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buffer, "hello"), "Expected \"hello\", got \"%s\"\n", buffer); + ok(size == 5, "Expected 5, got %d\n", size); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_where_viewmodify(void) +{ + MSIHANDLE hdb, hview, hrec; + const char *query; + UINT r; + + DeleteFile(msifile); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `Table` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 1, 2 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 3, 4 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `Table` ( `A`, `B` ) VALUES ( 5, 6 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* `B` = 3 doesn't match, but the view shouldn't be executed */ + query = "SELECT * FROM `Table` WHERE `B` = 3"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + hrec = MsiCreateRecord(2); + MsiRecordSetInteger(hrec, 1, 7); + MsiRecordSetInteger(hrec, 2, 8); + + r = MsiViewModify(hview, MSIMODIFY_INSERT_TEMPORARY, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Table` WHERE `A` = 7"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 7, "Expected 7, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 8, "Expected 8, got %d\n", r); + + MsiRecordSetInteger(hrec, 2, 9); + + r = MsiViewModify(hview, MSIMODIFY_UPDATE, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `Table` WHERE `A` = 7"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 7, "Expected 7, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 9, "Expected 9, got %d\n", r); + + query = "UPDATE `Table` SET `B` = 10 WHERE `A` = 7"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewModify(hview, MSIMODIFY_REFRESH, hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 7, "Expected 7, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 10, "Expected 10, got %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); +} + +static BOOL create_storage(LPCSTR name) +{ + WCHAR nameW[MAX_PATH]; + IStorage *stg; + IStream *stm; + HRESULT hr; + DWORD count; + BOOL res = FALSE; + + MultiByteToWideChar(CP_ACP, 0, name, -1, nameW, MAX_PATH); + hr = StgCreateDocfile(nameW, STGM_CREATE | STGM_READWRITE | + STGM_DIRECT | STGM_SHARE_EXCLUSIVE, 0, &stg); + if (FAILED(hr)) + return FALSE; + + hr = IStorage_CreateStream(stg, nameW, STGM_WRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &stm); + if (FAILED(hr)) + goto done; + + hr = IStream_Write(stm, "stgdata", 8, &count); + if (SUCCEEDED(hr)) + res = TRUE; + +done: + IStream_Release(stm); + IStorage_Release(stg); + + return res; +} + +static void test_storages_table(void) +{ + MSIHANDLE hdb, hview, hrec; + IStorage *stg, *inner; + IStream *stm; + char file[MAX_PATH]; + char buf[MAX_PATH]; + WCHAR name[MAX_PATH]; + LPCSTR query; + HRESULT hr; + DWORD size; + UINT r; + + hdb = create_db(); + ok(hdb, "failed to create db\n"); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS , "Failed to commit database\n"); + + MsiCloseHandle(hdb); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb); + ok(r == ERROR_SUCCESS , "Failed to open database\n"); + + /* check the column types */ + hrec = get_column_info(hdb, "SELECT * FROM `_Storages`", MSICOLINFO_TYPES); + ok(hrec, "failed to get column info hrecord\n"); + ok(check_record(hrec, 1, "s62"), "wrong hrecord type\n"); + ok(check_record(hrec, 2, "V0"), "wrong hrecord type\n"); + + MsiCloseHandle(hrec); + + /* now try the names */ + hrec = get_column_info(hdb, "SELECT * FROM `_Storages`", MSICOLINFO_NAMES); + ok(hrec, "failed to get column info hrecord\n"); + ok(check_record(hrec, 1, "Name"), "wrong hrecord type\n"); + ok(check_record(hrec, 2, "Data"), "wrong hrecord type\n"); + + MsiCloseHandle(hrec); + + create_storage("storage.bin"); + + hrec = MsiCreateRecord(2); + MsiRecordSetString(hrec, 1, "stgname"); + + r = MsiRecordSetStream(hrec, 2, "storage.bin"); + ok(r == ERROR_SUCCESS, "Failed to add stream data to the hrecord: %d\n", r); + + DeleteFileA("storage.bin"); + + query = "INSERT INTO `_Storages` (`Name`, `Data`) VALUES (?, ?)"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Failed to open database hview: %d\n", r); + + r = MsiViewExecute(hview, hrec); + ok(r == ERROR_SUCCESS, "Failed to execute hview: %d\n", r); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT `Name`, `Data` FROM `_Storages`"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Failed to open database hview: %d\n", r); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Failed to execute hview: %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Failed to fetch hrecord: %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, file, &size); + ok(r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok(!lstrcmp(file, "stgname"), "Expected \"stgname\", got \"%s\"\n", file); + + size = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordReadStream(hrec, 2, buf, &size); + ok(r == ERROR_INVALID_DATA, "Expected ERROR_INVALID_DATA, got %d\n", r); + ok(!lstrcmp(buf, "apple"), "Expected buf to be unchanged, got %s\n", buf); + ok(size == 0, "Expected 0, got %d\n", size); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + MsiDatabaseCommit(hdb); + MsiCloseHandle(hdb); + + MultiByteToWideChar(CP_ACP, 0, msifile, -1, name, MAX_PATH); + hr = StgOpenStorage(name, NULL, STGM_DIRECT | STGM_READ | + STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(stg != NULL, "Expected non-NULL storage\n"); + + MultiByteToWideChar(CP_ACP, 0, "stgname", -1, name, MAX_PATH); + hr = IStorage_OpenStorage(stg, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, + NULL, 0, &inner); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(inner != NULL, "Expected non-NULL storage\n"); + + MultiByteToWideChar(CP_ACP, 0, "storage.bin", -1, name, MAX_PATH); + hr = IStorage_OpenStream(inner, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); + ok(stm != NULL, "Expected non-NULL stream\n"); + + hr = IStream_Read(stm, buf, MAX_PATH, &size); + ok(hr == S_OK, "Expected S_OK, got %d\n", hr); + ok(size == 8, "Expected 8, got %d\n", size); + ok(!lstrcmpA(buf, "stgdata"), "Expected \"stgdata\", got \"%s\"\n", buf); + + IStream_Release(stm); + IStorage_Release(inner); + + IStorage_Release(stg); + DeleteFileA(msifile); +} + +static void test_droptable(void) +{ + MSIHANDLE hdb, hview, hrec; + CHAR buf[MAX_PATH]; + LPCSTR query; + DWORD size; + UINT r; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "A"), "Expected \"A\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "DROP `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + hview = 0; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `IDontExist`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE One"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "CREATE TABLE `One` ( `B` INT, `C` INT PRIMARY KEY `B` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "B"), "Expected \"B\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 3, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "C"), "Expected \"C\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "DROP TABLE One"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `_Tables` WHERE `Name` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'One'"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_dbmerge(void) +{ + MSIHANDLE hdb, href, hview, hrec; + CHAR buf[MAX_PATH]; + LPCSTR query; + DWORD size; + UINT r; + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiOpenDatabase("refdb.msi", MSIDBOPEN_CREATE, &href); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* hDatabase is invalid */ + r = MsiDatabaseMergeA(0, href, "MergeErrors"); + ok(r == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", r); + + /* hDatabaseMerge is invalid */ + r = MsiDatabaseMergeA(hdb, 0, "MergeErrors"); + ok(r == ERROR_INVALID_HANDLE, + "Expected ERROR_INVALID_HANDLE, got %d\n", r); + + /* szTableName is NULL */ + r = MsiDatabaseMergeA(hdb, href, NULL); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* szTableName is empty */ + r = MsiDatabaseMergeA(hdb, href, ""); + ok(r == ERROR_INVALID_TABLE, "Expected ERROR_INVALID_TABLE, got %d\n", r); + + /* both DBs empty, szTableName is valid */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` CHAR(72) PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* column types don't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` CHAR(72), " + "`B` CHAR(56), " + "`C` CHAR(64) LOCALIZABLE, " + "`D` LONGCHAR, " + "`E` CHAR(72) NOT NULL, " + "`F` CHAR(56) NOT NULL, " + "`G` CHAR(64) NOT NULL LOCALIZABLE, " + "`H` LONGCHAR NOT NULL " + "PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` CHAR(64), " + "`B` CHAR(64), " + "`C` CHAR(64), " + "`D` CHAR(64), " + "`E` CHAR(64) NOT NULL, " + "`F` CHAR(64) NOT NULL, " + "`G` CHAR(64) NOT NULL, " + "`H` CHAR(64) NOT NULL " + "PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* column sting types don't match exactly */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, + "Expected ERROR_SUCCESS, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `C` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* column names don't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `B` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* primary keys don't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A`, `B` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* number of primary keys doesn't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_DATATYPE_MISMATCH, + "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT, `C` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 2 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* number of columns doesn't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 3); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT, `C` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B`, `C` ) VALUES ( 1, 2, 3 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* number of columns doesn't match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 3); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 1 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 2, 2 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 2 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 2, 3 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* primary keys match, rows do not */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "One"), "Expected \"One\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + MsiCloseHandle(hrec); + + r = MsiDatabaseOpenViewA(hdb, "SELECT * FROM `MergeErrors`", &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(hview, MSICOLINFO_NAMES, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "Table"), "Expected \"Table\", got \"%s\"\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "NumRowMergeConflicts"), + "Expected \"NumRowMergeConflicts\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewGetColumnInfo(hview, MSICOLINFO_TYPES, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "s255"), "Expected \"s255\", got \"%s\"\n", buf); + + size = MAX_PATH; + r = MsiRecordGetString(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "i2"), "Expected \"i2\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + MsiViewClose(hview); + MsiCloseHandle(hview); + + query = "DROP TABLE `MergeErrors`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` CHAR(72) PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 'hi' )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* table from merged database is not in target database */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` CHAR(72), `B` INT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` CHAR(72), `B` INT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 'hi', 1 )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* primary key is string */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 1, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(hrec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + create_file_data("codepage.idt", "\r\n\r\n850\t_ForceCodepage\r\n", 0); + + GetCurrentDirectoryA(MAX_PATH, buf); + r = MsiDatabaseImportA(hdb, buf, "codepage.idt"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` INT, `B` CHAR(72) LOCALIZABLE PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( " + "`A` INT, `B` CHAR(72) LOCALIZABLE PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 'hi' )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* code page does not match */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` OBJECT PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` OBJECT PRIMARY KEY `A` )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + create_file("binary.dat"); + hrec = MsiCreateRecord(1); + MsiRecordSetStreamA(hrec, 1, "binary.dat"); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, ? )"; + r = run_query(href, hrec, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + MsiCloseHandle(hrec); + + /* binary data to merge */ + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + ZeroMemory(buf, MAX_PATH); + r = MsiRecordReadStream(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "binary.dat\n"), + "Expected \"binary.dat\\n\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + /* nothing in MergeErrors */ + query = "SELECT * FROM `MergeErrors`"; + r = do_query(hdb, query, &hrec); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "DROP TABLE `One`"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `One` ( `A` INT, `B` CHAR(72) PRIMARY KEY `A` )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 1, 'foo' )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `One` ( `A`, `B` ) VALUES ( 2, 'bar' )"; + r = run_query(href, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseMergeA(hdb, href, "MergeErrors"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `One`"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 1, "Expected 1, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "foo"), "Expected \"foo\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(hrec, 1); + ok(r == 2, "Expected 2, got %d\n", r); + + size = MAX_PATH; + r = MsiRecordGetStringA(hrec, 2, buf, &size); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "bar"), "Expected \"bar\", got \"%s\"\n", buf); + + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, + "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + + MsiCloseHandle(hdb); + MsiCloseHandle(href); + DeleteFileA(msifile); + DeleteFileA("refdb.msi"); + DeleteFileA("codepage.idt"); + DeleteFileA("binary.dat"); +} + +static void test_select_with_tablenames(void) +{ + MSIHANDLE hdb, view, rec; + LPCSTR query; + UINT r; + int i; + + int vals[4][2] = { + {1,12}, + {4,12}, + {1,15}, + {4,15}}; + + hdb = create_db(); + ok(hdb, "failed to create db\n"); + + /* Build a pair of tables with the same column names, but unique data */ + query = "CREATE TABLE `T1` ( `A` SHORT, `B` SHORT PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T1` ( `A`, `B` ) VALUES ( 1, 2 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T1` ( `A`, `B` ) VALUES ( 4, 5 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `T2` ( `A` SHORT, `B` SHORT PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T2` ( `A`, `B` ) VALUES ( 11, 12 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T2` ( `A`, `B` ) VALUES ( 14, 15 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + + /* Test that selection based on prefixing the column with the table + * actually selects the right data */ + + query = "SELECT T1.A, T2.B FROM T1,T2"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + for (i = 0; i < 4; i++) + { + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == vals[i][0], "Expected %d, got %d\n", vals[i][0], r); + + r = MsiRecordGetInteger(rec, 2); + ok(r == vals[i][1], "Expected %d, got %d\n", vals[i][1], r); + + MsiCloseHandle(rec); + } + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static const UINT ordervals[6][3] = +{ + { MSI_NULL_INTEGER, 12, 13 }, + { 1, 2, 3 }, + { 6, 4, 5 }, + { 8, 9, 7 }, + { 10, 11, MSI_NULL_INTEGER }, + { 14, MSI_NULL_INTEGER, 15 } +}; + +static void test_insertorder(void) +{ + MSIHANDLE hdb, view, rec; + LPCSTR query; + UINT r; + int i; + + hdb = create_db(); + ok(hdb, "failed to create db\n"); + + query = "CREATE TABLE `T` ( `A` SHORT, `B` SHORT, `C` SHORT PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `A`, `B`, `C` ) VALUES ( 1, 2, 3 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `B`, `C`, `A` ) VALUES ( 4, 5, 6 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `C`, `A`, `B` ) VALUES ( 7, 8, 9 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `A`, `B` ) VALUES ( 10, 11 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `B`, `C` ) VALUES ( 12, 13 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* fails because the primary key already + * has an MSI_NULL_INTEGER value set above + */ + query = "INSERT INTO `T` ( `C` ) VALUES ( 14 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + /* replicate the error where primary key is set twice */ + query = "INSERT INTO `T` ( `A`, `C` ) VALUES ( 1, 14 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + query = "INSERT INTO `T` ( `A`, `C` ) VALUES ( 14, 15 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` VALUES ( 16 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `T` VALUES ( 17, 18 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `T` VALUES ( 19, 20, 21 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `T`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + for (i = 0; i < 6; i++) + { + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == ordervals[i][0], "Expected %d, got %d\n", ordervals[i][0], r); + + r = MsiRecordGetInteger(rec, 2); + ok(r == ordervals[i][1], "Expected %d, got %d\n", ordervals[i][1], r); + + r = MsiRecordGetInteger(rec, 3); + ok(r == ordervals[i][2], "Expected %d, got %d\n", ordervals[i][2], r); + + MsiCloseHandle(rec); + } + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + + query = "DELETE FROM `T` WHERE `A` IS NULL"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `B`, `C` ) VALUES ( 12, 13 ) TEMPORARY"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `T`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + for (i = 0; i < 6; i++) + { + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == ordervals[i][0], "Expected %d, got %d\n", ordervals[i][0], r); + + r = MsiRecordGetInteger(rec, 2); + ok(r == ordervals[i][1], "Expected %d, got %d\n", ordervals[i][1], r); + + r = MsiRecordGetInteger(rec, 3); + ok(r == ordervals[i][2], "Expected %d, got %d\n", ordervals[i][2], r); + + MsiCloseHandle(rec); + } + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_columnorder(void) +{ + MSIHANDLE hdb, view, rec; + char buf[MAX_PATH]; + LPCSTR query; + DWORD sz; + UINT r; + + hdb = create_db(); + ok(hdb, "failed to create db\n"); + + /* Each column is a slot: + * --------------------- + * | B | C | A | E | D | + * --------------------- + * + * When a column is selected as a primary key, + * the column occupying the nth primary key slot is swapped + * with the current position of the primary key in question: + * + * set primary key `D` + * --------------------- --------------------- + * | B | C | A | E | D | -> | D | C | A | E | B | + * --------------------- --------------------- + * + * set primary key `E` + * --------------------- --------------------- + * | D | C | A | E | B | -> | D | E | A | C | B | + * --------------------- --------------------- + */ + + query = "CREATE TABLE `T` ( `B` SHORT NOT NULL, `C` SHORT NOT NULL, " + "`A` CHAR(255), `E` INT, `D` CHAR(255) NOT NULL " + "PRIMARY KEY `D`, `E`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `T`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("s255", buf), "Expected \"s255\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 2, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("I2", buf), "Expected \"I2\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("S255", buf), "Expected \"S255\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 4, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("i2", buf), "Expected \"i2\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 5, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("i2", buf), "Expected \"i2\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("D", buf), "Expected \"D\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 2, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("E", buf), "Expected \"E\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("A", buf), "Expected \"A\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 4, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("C", buf), "Expected \"C\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 5, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("B", buf), "Expected \"B\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + + query = "INSERT INTO `T` ( `B`, `C`, `A`, `E`, `D` ) " + "VALUES ( 1, 2, 'a', 3, 'bc' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `T`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("bc", buf), "Expected \"bc\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 3, "Expected 3, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("a", buf), "Expected \"a\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 4); + ok(r == 2, "Expected 2, got %d\n", r); + + r = MsiRecordGetInteger(rec, 5); + ok(r == 1, "Expected 1, got %d\n", r); + + MsiCloseHandle(rec); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'T'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("D", buf), "Expected \"D\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("E", buf), "Expected \"E\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 3, "Expected 3, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("A", buf), "Expected \"A\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 4, "Expected 4, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("C", buf), "Expected \"C\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 5, "Expected 5, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("B", buf), "Expected \"B\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + + query = "CREATE TABLE `Z` ( `B` SHORT NOT NULL, `C` SHORT NOT NULL, " + "`A` CHAR(255), `E` INT, `D` CHAR(255) NOT NULL " + "PRIMARY KEY `C`, `A`, `D`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Z`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("i2", buf), "Expected \"i2\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 2, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("S255", buf), "Expected \"S255\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("s255", buf), "Expected \"s255\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 4, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("I2", buf), "Expected \"I2\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 5, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("i2", buf), "Expected \"i2\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("C", buf), "Expected \"C\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 2, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("A", buf), "Expected \"A\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("D", buf), "Expected \"D\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 4, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("E", buf), "Expected \"E\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 5, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("B", buf), "Expected \"B\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + MsiViewClose(view); + MsiCloseHandle(view); + + query = "INSERT INTO `Z` ( `B`, `C`, `A`, `E`, `D` ) " + "VALUES ( 1, 2, 'a', 3, 'bc' )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT * FROM `Z`"; + r = do_query(hdb, query, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == 2, "Expected 2, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 2, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("a", buf), "Expected \"a\", got \"%s\"\n", buf); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("bc", buf), "Expected \"bc\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 4); + ok(r == 3, "Expected 3, got %d\n", r); + + r = MsiRecordGetInteger(rec, 5); + ok(r == 1, "Expected 1, got %d\n", r); + + MsiCloseHandle(rec); + + query = "SELECT * FROM `_Columns` WHERE `Table` = 'T'"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 1, "Expected 1, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("D", buf), "Expected \"D\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 2, "Expected 2, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("E", buf), "Expected \"E\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 3, "Expected 3, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("A", buf), "Expected \"A\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 4, "Expected 4, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("C", buf), "Expected \"C\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("T", buf), "Expected \"T\", got \"%s\"\n", buf); + + r = MsiRecordGetInteger(rec, 2); + ok(r == 5, "Expected 5, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "kiwi"); + r = MsiRecordGetString(rec, 3, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA("B", buf), "Expected \"B\", got \"%s\"\n", buf); + + MsiCloseHandle(rec); + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +static void test_createtable(void) +{ + MSIHANDLE hdb, htab = 0, hrec = 0; + LPCSTR query; + UINT res; + DWORD size; + char buffer[0x20]; + + hdb = create_db(); + ok(hdb, "failed to create db\n"); + + query = "CREATE TABLE `blah` (`foo` CHAR(72) NOT NULL PRIMARY KEY `foo`)"; + res = MsiDatabaseOpenView( hdb, query, &htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + if(res == ERROR_SUCCESS ) + { + res = MsiViewExecute( htab, hrec ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiViewGetColumnInfo( htab, MSICOLINFO_NAMES, &hrec ); + todo_wine ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + size = sizeof(buffer); + res = MsiRecordGetString(hrec, 1, buffer, &size ); + todo_wine ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + MsiCloseHandle( hrec ); + + res = MsiViewClose( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiCloseHandle( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + } + + query = "CREATE TABLE `a` (`b` INT PRIMARY KEY `b`)"; + res = MsiDatabaseOpenView( hdb, query, &htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + if(res == ERROR_SUCCESS ) + { + res = MsiViewExecute( htab, 0 ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiViewClose( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiCloseHandle( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + query = "SELECT * FROM `a`"; + res = MsiDatabaseOpenView( hdb, query, &htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiViewGetColumnInfo( htab, MSICOLINFO_NAMES, &hrec ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + buffer[0] = 0; + size = sizeof(buffer); + res = MsiRecordGetString(hrec, 1, buffer, &size ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!strcmp(buffer,"b"), "b != %s\n", buffer); + MsiCloseHandle( hrec ); + + res = MsiViewClose( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiCloseHandle( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiDatabaseCommit(hdb); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiCloseHandle(hdb); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiOpenDatabase(msifile, MSIDBOPEN_TRANSACT, &hdb ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + query = "SELECT * FROM `a`"; + res = MsiDatabaseOpenView( hdb, query, &htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiViewGetColumnInfo( htab, MSICOLINFO_NAMES, &hrec ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + buffer[0] = 0; + size = sizeof(buffer); + res = MsiRecordGetString(hrec, 1, buffer, &size ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + ok(!strcmp(buffer,"b"), "b != %s\n", buffer); + + res = MsiCloseHandle( hrec ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiViewClose( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiCloseHandle( htab ); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + } + + res = MsiDatabaseCommit(hdb); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + res = MsiCloseHandle(hdb); + ok(res == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", res); + + DeleteFileA(msifile); +} + +static void test_embedded_nulls(void) +{ + static const char control_table[] = + "Dialog\tText\n" + "s72\tL0\n" + "Control\tDialog\n" + "LicenseAgreementDlg\ttext\x11\x19text\0text"; + UINT r, sz; + MSIHANDLE hdb, hrec; + char buffer[32]; + + r = MsiOpenDatabaseA( msifile, MSIDBOPEN_CREATE, &hdb ); + ok( r == ERROR_SUCCESS, "failed to open database %u\n", r ); + + GetCurrentDirectoryA( MAX_PATH, CURR_DIR ); + write_file( "temp_file", control_table, sizeof(control_table) ); + r = MsiDatabaseImportA( hdb, CURR_DIR, "temp_file" ); + ok( r == ERROR_SUCCESS, "failed to import table %u\n", r ); + DeleteFileA( "temp_file" ); + + r = do_query( hdb, "SELECT `Text` FROM `Control` WHERE `Dialog` = 'LicenseAgreementDlg'", &hrec ); + ok( r == ERROR_SUCCESS, "query failed %u\n", r ); + + buffer[0] = 0; + sz = sizeof(buffer); + r = MsiRecordGetStringA( hrec, 1, buffer, &sz ); + ok( r == ERROR_SUCCESS, "failed to get string %u\n", r ); + ok( !memcmp( "text\r\ntext\ntext", buffer, sizeof("text\r\ntext\ntext") - 1 ), "wrong buffer contents \"%s\"\n", buffer ); + + MsiCloseHandle( hrec ); + MsiCloseHandle( hdb ); + DeleteFileA( msifile ); +} + +static void test_select_column_names(void) +{ + MSIHANDLE hdb = 0, rec, rec2, view; + char buffer[32]; + UINT r, size; + + DeleteFile(msifile); + + r = MsiOpenDatabase( msifile, MSIDBOPEN_CREATE, &hdb ); + ok( r == ERROR_SUCCESS , "failed to open database: %u\n", r ); + + r = try_query( hdb, "CREATE TABLE `t` (`a` CHAR NOT NULL, `b` CHAR PRIMARY KEY `a`)"); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b` FROM `t` WHERE `t`.`b` = `x`" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT '', `t`.`b` FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT *, `t`.`b` FROM `t` WHERE `t`.`b` = 'x'" ); + todo_wine ok( r == ERROR_SUCCESS, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT 'b', `t`.`b` FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, '' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, '' FROM `t` WHERE `t`.`b` = 'x' ORDER BY `b`" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, '' FROM `t` WHERE `t`.`b` = 'x' ORDER BY 'b'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT 't'.'b' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT 'b' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "INSERT INTO `t` ( `a`, `b` ) VALUES( '1', '2' )" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "INSERT INTO `t` ( `a`, `b` ) VALUES( '3', '4' )" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = MsiDatabaseOpenView( hdb, "SELECT '' FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 1, "got %u\n", r ); + r = MsiViewGetColumnInfo( view, MSICOLINFO_NAMES, &rec2 ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec2 ); + ok( r == 1, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec2, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec2 ); + r = MsiViewGetColumnInfo( view, MSICOLINFO_TYPES, &rec2 ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec2 ); + ok( r == 1, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec2, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "f0" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec2 ); + + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, "SELECT `a`, '' FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 2, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "1" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, "SELECT '', `a` FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 2, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "1" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "3" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = MsiDatabaseOpenView( hdb, "SELECT `a`, '', `b` FROM `t`", &view ); + ok( r == ERROR_SUCCESS, "failed to open database view: %u\n", r ); + + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "failed to execute view: %u\n", r ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + r = MsiRecordGetFieldCount( rec ); + ok( r == 3, "got %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "1" ), "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 3, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "2" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 1, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "3" ), "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 2, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !buffer[0], "got \"%s\"\n", buffer ); + size = sizeof(buffer); + memset( buffer, 0x55, sizeof(buffer) ); + r = MsiRecordGetStringA( rec, 3, buffer, &size ); + ok( r == ERROR_SUCCESS, "unexpected result: %u\n", r ); + ok( !lstrcmpA( buffer, "4" ), "got \"%s\"\n", buffer ); + MsiCloseHandle( rec ); + + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_NO_MORE_ITEMS, "unexpected result: %u\n", r ); + MsiCloseHandle( rec ); + + MsiViewClose( view ); + MsiCloseHandle( view ); + + r = try_query( hdb, "SELECT '' FROM `t` WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_SUCCESS , "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `` FROM `t` WHERE `t`.`b` = 'x'" ); + todo_wine ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `b` FROM 't' WHERE `t`.`b` = 'x'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `b` FROM `t` WHERE 'b' = 'x'" ); + ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = try_query( hdb, "SELECT `t`.`b`, `` FROM `t` WHERE `t`.`b` = 'x'" ); + todo_wine ok( r == ERROR_BAD_QUERY_SYNTAX, "query failed: %u\n", r ); + + r = MsiCloseHandle( hdb ); + ok(r == ERROR_SUCCESS , "failed to close database: %u\n", r); +} + +void main() +{ + test_msidatabase(); + test_msiinsert(); + test_msibadqueries(); + test_viewmodify(); + test_viewgetcolumninfo(); + test_getcolinfo(); + test_msiexport(); + test_longstrings(); + test_streamtable(); + test_binary(); + test_where_not_in_selected(); + test_where(); + test_msiimport(); + test_binary_import(); + test_markers(); + test_handle_limit(); + test_try_transform(); + test_join(); + test_temporary_table(); + test_alter(); + test_integers(); + test_update(); + test_special_tables(); + test_tables_order(); + test_rows_order(); + test_select_markers(); + test_viewmodify_update(); + test_viewmodify_assign(); + test_stringtable(); + test_viewmodify_delete(); + test_defaultdatabase(); + test_order(); + test_viewmodify_delete_temporary(); + test_deleterow(); + test_quotes(); + test_carriagereturn(); + test_noquotes(); + test_forcecodepage(); + test_viewmodify_refresh(); + test_where_viewmodify(); + test_storages_table(); + test_droptable(); +#if 0 + test_dbmerge(); +#endif + test_select_with_tablenames(); + test_insertorder(); + test_columnorder(); + test_suminfo_import(); + test_createtable(); + test_collation(); + test_embedded_nulls(); + test_select_column_names(); +} diff --git a/tests/testdatabase.ok b/tests/testdatabase.ok new file mode 100644 index 0000000..819f76d --- /dev/null +++ b/tests/testdatabase.ok @@ -0,0 +1,2955 @@ +ok: res == ERROR_OPEN_FAILED
+ok: res == ERROR_INVALID_PARAMETER
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile )
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 )
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile2 )
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile2 )
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile )
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile )
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES == GetFileAttributes( msifile )
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: INVALID_FILE_ATTRIBUTES != GetFileAttributes( msifile )
+ok: res == ERROR_SUCCESS
+ok: res == TRUE
+ok: res == TRUE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 3
+ok: r == FALSE
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !strcmp(buf,"Abe")
+ok: r == ERROR_SUCCESS
+ok: !strcmp(buf,"8675309")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: hrec == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == TRUE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r != ERROR_SUCCESS
+ok: r != ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == TRUE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: err == MSIDBERROR_INVALIDARG
+ok: r == ERROR_SUCCESS
+ok: err == MSIDBERROR_INVALIDARG || err == MSIDBERROR_NOERROR
+ok: err == MSIDBERROR_INVALIDARG
+ok: err == MSIDBERROR_MOREDATA
+ok: buffer[0] == 'x'
+ok: sz == 0
+ok: err == MSIDBERROR_NOERROR
+ok: sz == 0
+ok: err == MSIDBERROR_NOERROR
+ok: buffer[0] == 0
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_HANDLE
+ok: r == ERROR_INVALID_HANDLE
+ok: r == ERROR_INVALID_DATA
+ok: err == MSIDBERROR_NOERROR
+ok: buffer[0] == 0
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_DATA
+ok: err == MSIDBERROR_DUPLICATEKEY
+ok: !strcmp(buffer, "id")
+ok: sz == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "bob")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "7654321")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "bob")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "3141592")
+ok: r == ERROR_SUCCESS
+ok: hrec != 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: hrec != 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: rec
+ok: check_record( rec, 1, "S255")
+ok: check_record( rec, 2, "S1")
+ok: check_record( rec, 3, "I2")
+ok: check_record( rec, 4, "I2")
+ok: check_record( rec, 5, "I2")
+ok: check_record( rec, 6, "I4")
+ok: check_record( rec, 7, "S0")
+ok: 0x3dff == get_columns_table_type(hdb, "Properties", 1 )
+ok: 0x1d01 == get_columns_table_type(hdb, "Properties", 2 )
+ok: 0x1502 == get_columns_table_type(hdb, "Properties", 3 )
+ok: 0x1502 == get_columns_table_type(hdb, "Properties", 4 )
+ok: 0x1502 == get_columns_table_type(hdb, "Properties", 5 )
+ok: 0x1104 == get_columns_table_type(hdb, "Properties", 6 )
+ok: 0x1d00 == get_columns_table_type(hdb, "Properties", 7 )
+ok: rec
+ok: check_record( rec, 1, "Property")
+ok: check_record( rec, 2, "Value")
+ok: check_record( rec, 3, "Intvalue")
+ok: check_record( rec, 4, "Integervalue")
+ok: check_record( rec, 5, "Shortvalue")
+ok: check_record( rec, 6, "Longvalue")
+ok: check_record( rec, 7, "Longcharvalue")
+ok: r == ERROR_SUCCESS
+ok: rec
+ok: check_record( rec, 1, "S255")
+ok: check_record( rec, 2, "V0")
+ok: 0x3dff == get_columns_table_type(hdb, "Binary", 1 )
+ok: 0x1900 == get_columns_table_type(hdb, "Binary", 2 )
+ok: rec
+ok: check_record( rec, 1, "Name")
+ok: check_record( rec, 2, "Data")
+ok: r == ERROR_SUCCESS
+ok: 0x2d48 == get_columns_table_type(hdb, "UIText", 1 )
+ok: 0x1fff == get_columns_table_type(hdb, "UIText", 2 )
+ok: rec
+ok: check_record( rec, 1, "Key")
+ok: check_record( rec, 2, "Text")
+ok: rec
+ok: check_record( rec, 1, "s72")
+ok: check_record( rec, 2, "L255")
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !strcmp(buffer,"Name")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !strcmp(buffer,"s64")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_PARAMETER
+ok: rec == 0
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_INVALID_HANDLE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: length == strlen(expected)
+ok: !lstrcmp(buffer, expected)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: len == STRING_LENGTH
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: rec
+ok: check_record( rec, 1, "s62")
+ok: check_record( rec, 2, "V0")
+ok: rec
+ok: check_record( rec, 1, "Name")
+ok: check_record( rec, 2, "Data")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(file, "data")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "test.txt\n")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(file, "data1")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "test1.txt\n")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(file, "data1")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(file, "Binary.filename1.1")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "test.txt\n")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(file, "filename1")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "test.txt\n")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: check_record( rec, 1, "cond2")
+ok: r == ERROR_SUCCESS
+ok: check_record( rec, 1, "cond3")
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == ERROR_SUCCESS
+ok: check_record( rec, 4, "zero.cab")
+ok: r == ERROR_SUCCESS
+ok: check_record( rec, 4, "one.cab")
+ok: 2 == r
+ok: 1 == r
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, "2" )
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, "3" )
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 9
+ok: check_record(rec, 1, "FirstPrimaryColumn")
+ok: check_record(rec, 2, "SecondPrimaryColumn")
+ok: check_record(rec, 3, "ShortInt")
+ok: check_record(rec, 4, "ShortIntNullable")
+ok: check_record(rec, 5, "LongInt")
+ok: check_record(rec, 6, "LongIntNullable")
+ok: check_record(rec, 7, "String")
+ok: check_record(rec, 8, "LocalizableString")
+ok: check_record(rec, 9, "LocalizableStringNullable")
+ok: r == ERROR_SUCCESS
+ok: count == 9
+ok: check_record(rec, 1, "s255")
+ok: check_record(rec, 2, "i2")
+ok: check_record(rec, 3, "i2")
+ok: check_record(rec, 4, "I2")
+ok: check_record(rec, 5, "i4")
+ok: check_record(rec, 6, "I4")
+ok: check_record(rec, 7, "S255")
+ok: check_record(rec, 8, "S0")
+ok: check_record(rec, 9, "s0")
+ok: r == ERROR_SUCCESS
+ok: check_record(rec, 1, "stringage")
+ok: check_record(rec, 7, "another string")
+ok: check_record(rec, 8, "localizable")
+ok: check_record(rec, 9, "duh")
+ok: i == 5
+ok: i == 2
+ok: i == MSI_NULL_INTEGER
+ok: i == 2147483640
+ok: i == -2147483640
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: check_record(rec, 1, "PrimaryOne")
+ok: check_record(rec, 2, "PrimaryTwo")
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: check_record(rec, 1, "s255")
+ok: check_record(rec, 2, "s255")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: check_record(rec, 1, "papaya")
+ok: check_record(rec, 2, "leaf")
+ok: r == ERROR_SUCCESS
+ok: check_record(rec, 1, "papaya")
+ok: check_record(rec, 2, "flower")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 6
+ok: check_record(rec, 1, "A")
+ok: check_record(rec, 2, "B")
+ok: check_record(rec, 3, "C")
+ok: check_record(rec, 4, "D")
+ok: check_record(rec, 5, "E")
+ok: check_record(rec, 6, "F")
+ok: r == ERROR_SUCCESS
+ok: count == 6
+ok: check_record(rec, 1, "s72")
+ok: check_record(rec, 2, "s72")
+ok: check_record(rec, 3, "s72")
+ok: check_record(rec, 4, "s72")
+ok: check_record(rec, 5, "s72")
+ok: check_record(rec, 6, "s72")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: check_record(rec, 1, "a")
+ok: check_record(rec, 2, "b")
+ok: check_record(rec, 3, "c")
+ok: check_record(rec, 4, "d")
+ok: check_record(rec, 5, "e")
+ok: check_record(rec, 6, "f")
+ok: r == ERROR_SUCCESS
+ok: check_record(rec, 1, "g")
+ok: check_record(rec, 2, "h")
+ok: check_record(rec, 3, "i")
+ok: check_record(rec, 4, "j")
+ok: check_record(rec, 5, "k")
+ok: check_record(rec, 6, "l")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(file, "filename1")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "just some words")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: i == MY_NVIEWS
+ok: i == MY_NVIEWS
+ok: r == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == S_OK
+ok: r == S_OK
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !memcmp(buffer, "naengmyon", 9)
+ok: sz == 9
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buffer, "c")
+ok: r == 0x80000000
+ok: r == 5
+ok: r == ERROR_SUCCESS
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buffer, "b")
+ok: r == 0x80000000
+ok: r == 0x80000000
+ok: r == ERROR_NO_MORE_ITEMS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].one )
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].two )
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].one )
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].two )
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].one )
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].two )
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].one )
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].two )
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].one )
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, join_res_first[i].two )
+ok: i == 5
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: i == 24
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 2
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 2
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 1
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 1
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 6
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 3
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 6
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: data_correct
+ok: i == 6
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 6
+ok: count == 6
+ok: count == 6
+ok: count == 6
+ok: count == 6
+ok: count == 6
+ok: data_correct
+ok: i == 6
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp( buf, "epicranius" )
+ok: cond == MSICONDITION_ERROR
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: cond == MSICONDITION_ERROR
+ok: cond == MSICONDITION_NONE
+ok: cond == MSICONDITION_NONE
+ok: cond == MSICONDITION_NONE
+ok: cond == MSICONDITION_NONE
+ok: r == ERROR_SUCCESS
+ok: cond == MSICONDITION_TRUE
+ok: r == ERROR_SUCCESS
+ok: cond == MSICONDITION_TRUE
+ok: r == ERROR_SUCCESS
+ok: cond == MSICONDITION_FALSE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: cond == MSICONDITION_NONE
+ok: r == ERROR_SUCCESS
+ok: cond == MSICONDITION_TRUE
+ok: r == ERROR_FUNCTION_FAILED
+ok: cond == MSICONDITION_NONE
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: 0 == strcmp("G255", buf)
+ok: r == ERROR_SUCCESS
+ok: 0 == strcmp("j2", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: cond == MSICONDITION_FALSE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 8
+ok: check_record(rec, 1, "one")
+ok: check_record(rec, 2, "two")
+ok: check_record(rec, 3, "three")
+ok: check_record(rec, 4, "four")
+ok: check_record(rec, 5, "five")
+ok: check_record(rec, 6, "six")
+ok: check_record(rec, 7, "seven")
+ok: check_record(rec, 8, "eight")
+ok: r == ERROR_SUCCESS
+ok: count == 8
+ok: check_record(rec, 1, "I2")
+ok: check_record(rec, 2, "I2")
+ok: check_record(rec, 3, "I2")
+ok: check_record(rec, 4, "I4")
+ok: check_record(rec, 5, "i2")
+ok: check_record(rec, 6, "i2")
+ok: check_record(rec, 7, "i2")
+ok: check_record(rec, 8, "i4")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == -1
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 8
+ok: i == MSI_NULL_INTEGER
+ok: i == MSI_NULL_INTEGER
+ok: i == 2
+ok: i == 4
+ok: i == 5
+ok: i == 6
+ok: i == 7
+ok: i == 8
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == TRUE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(result, "this is text")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrlen(result)
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(result, "this is text")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrlen(result)
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(result, "this is text")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(result, "this is text")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(result, "this is text")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 8
+ok: r == ERROR_SUCCESS
+ok: r == 8
+ok: r == ERROR_SUCCESS
+ok: r == 5
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "foo")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "baz")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "bar")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "foo")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "baz")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "baz")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "bar")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "baz")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "baz")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "baz")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "foo")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "bar")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "foo")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "A")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "B")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "C")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "E")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "D")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "E")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "F")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "A")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "apple")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "two")
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "apple")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "two")
+ok: r == 2
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "apple")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "two")
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "banana")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "three")
+ok: r == 3
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 3
+ok: r == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 5
+ok: r == 6
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 3
+ok: r == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 5
+ok: r == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == test_max
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: ( test_max - a + offset) == b
+ok: r == ERROR_SUCCESS
+ok: count == test_max
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: hrec != 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: hrec != 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == 4
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 2
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "one")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 2
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "two")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 2
+ok: r == 5
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "five")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: hr == S_OK
+ok: stg != NULL
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: read == 4
+ok: hr == S_OK
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: read == 8
+ok: hr == S_OK
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: read == 24
+ok: !memcmp(buffer, data12, read)
+ok: hr == S_OK
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: hr == S_OK
+ok: hr == S_OK
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 3
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "Cindy")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "2937550")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: hr == S_OK
+ok: stg != NULL
+ok: hr == S_OK
+ok: !lstrcmpW(stat.pwcsName, database_table_data[n].name)
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: count == database_table_data[n].size
+ok: !memcmp(data, check, MAX_PATH)
+ok: !lstrcmpW(stat.pwcsName, database_table_data[n].name)
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: count == database_table_data[n].size
+ok: !memcmp(data, check, MAX_PATH)
+ok: !lstrcmpW(stat.pwcsName, database_table_data[n].name)
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: count == database_table_data[n].size
+ok: !memcmp(data, database_table_data[n].data, database_table_data[n].size)
+ok: n == 3
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: val == 3
+ok: val == 4
+ok: r == ERROR_SUCCESS
+ok: val == 5
+ok: val == 6
+ok: r == ERROR_SUCCESS
+ok: val == 1
+ok: val == 2
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: val == 1
+ok: val == 12
+ok: r == ERROR_SUCCESS
+ok: val == 3
+ok: val == 12
+ok: r == ERROR_SUCCESS
+ok: val == 5
+ok: val == 12
+ok: r == ERROR_SUCCESS
+ok: val == 1
+ok: val == 14
+ok: r == ERROR_SUCCESS
+ok: val == 3
+ok: val == 14
+ok: r == ERROR_SUCCESS
+ok: val == 5
+ok: val == 14
+ok: r == ERROR_SUCCESS
+ok: val == 1
+ok: val == 10
+ok: r == ERROR_SUCCESS
+ok: val == 3
+ok: val == 10
+ok: r == ERROR_SUCCESS
+ok: val == 5
+ok: val == 10
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "dos")
+ok: r == 3
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: r == 4
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "two")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "This is a \"string\" ok")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buf, "This is a new 'string' ok")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "\rOne")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Tw\ro")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Three\r")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Table")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Table2")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Table3")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Table")
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "A")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Table2")
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "A")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "Table3")
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "A")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "hi")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buffer, "\r\n\r\n0\t_ForceCodepage\r\n")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buffer, "\r\n\r\n850\t_ForceCodepage\r\n")
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buffer, "hi")
+ok: size == 2
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buffer, "hello")
+ok: size == 5
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 7
+ok: r == 8
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 7
+ok: r == 9
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 7
+ok: r == 10
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: hrec
+ok: check_record(hrec, 1, "s62")
+ok: check_record(hrec, 2, "V0")
+ok: hrec
+ok: check_record(hrec, 1, "Name")
+ok: check_record(hrec, 2, "Data")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(file, "stgname")
+ok: r == ERROR_INVALID_DATA
+ok: !lstrcmp(buf, "apple")
+ok: size == 0
+ok: r == ERROR_NO_MORE_ITEMS
+ok: hr == S_OK
+ok: stg != NULL
+ok: hr == S_OK
+ok: inner != NULL
+ok: hr == S_OK
+ok: stm != NULL
+ok: hr == S_OK
+ok: size == 8
+ok: !lstrcmpA(buf, "stgdata")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "One")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "One")
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "A")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "One")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "One")
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "B")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "One")
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "C")
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_NO_MORE_ITEMS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == vals[i][0]
+ok: r == vals[i][1]
+ok: r == ERROR_SUCCESS
+ok: r == vals[i][0]
+ok: r == vals[i][1]
+ok: r == ERROR_SUCCESS
+ok: r == vals[i][0]
+ok: r == vals[i][1]
+ok: r == ERROR_SUCCESS
+ok: r == vals[i][0]
+ok: r == vals[i][1]
+ok: r == ERROR_NO_MORE_ITEMS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_SUCCESS
+ok: r == ordervals[i][0]
+ok: r == ordervals[i][1]
+ok: r == ordervals[i][2]
+ok: r == ERROR_NO_MORE_ITEMS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("s255", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("I2", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("S255", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("i2", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("i2", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("D", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("E", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("A", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("C", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("B", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("bc", buf)
+ok: r == 3
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("a", buf)
+ok: r == 2
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("D", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("E", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 3
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("A", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 4
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("C", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 5
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("B", buf)
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("i2", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("S255", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("s255", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("I2", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("i2", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("C", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("A", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("D", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("E", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("B", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("a", buf)
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("bc", buf)
+ok: r == 3
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("D", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("E", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 3
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("A", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 4
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("C", buf)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("T", buf)
+ok: r == 5
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA("B", buf)
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 14
+ok: r == ERROR_SUCCESS
+ok: type == VT_I2
+ok: int_value == 1252
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: size == 18
+ok: !strcmp(str_value, "Installer Database")
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: !strcmp(str_value, "Installer description")
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: !strcmp(str_value, "WineHQ")
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: !strcmp(str_value, "Installer")
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: !strcmp(str_value, "Installer comments")
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: !strcmp(str_value, "Intel;1033,2057")
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: !strcmp(str_value, "{12345678-1234-1234-1234-123456789012}")
+ok: r == ERROR_SUCCESS
+ok: type == VT_FILETIME
+ok: r == ERROR_SUCCESS
+ok: type == VT_FILETIME
+ok: r == ERROR_SUCCESS
+ok: type == VT_I4
+ok: int_value == 200
+ok: r == ERROR_SUCCESS
+ok: type == VT_I4
+ok: int_value == 2
+ok: r == ERROR_SUCCESS
+ok: type == VT_I4
+ok: int_value == 2
+ok: r == ERROR_SUCCESS
+ok: type == VT_LPSTR
+ok: !strcmp(str_value, "Vim")
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: hdb
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: !strcmp(buffer,"b")
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: !strcmp(buffer,"b")
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: res == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "\2")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "A")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "\1")
+ok: r == ERROR_SUCCESS
+ok: !lstrcmp(buffer, "B")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !memcmp(bufferW, letter_a_ring, sizeof(letter_a_ring))
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpW(bufferW, letter_C)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !memcmp(bufferW, letter_a_with_ring, sizeof(letter_a_with_ring))
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpW(bufferW, letter_D)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !memcmp(bufferW, letter_a_with_ring, sizeof(letter_a_with_ring))
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpW(bufferW, letter_D)
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !memcmp( "text\r\ntext\ntext", buffer, sizeof("text\r\ntext\ntext") - 1 )
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_SUCCESS
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "f0" )
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "1" )
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 2
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "1" )
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "3" )
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == 3
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "1" )
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "2" )
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "3" )
+ok: r == ERROR_SUCCESS
+ok: !buffer[0]
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA( buffer, "4" )
+ok: r == ERROR_NO_MORE_ITEMS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_BAD_QUERY_SYNTAX
+ok: r == ERROR_SUCCESS
diff --git a/tests/testrecord.c b/tests/testrecord.c new file mode 100644 index 0000000..7dfd4e3 --- /dev/null +++ b/tests/testrecord.c @@ -0,0 +1,621 @@ +/* + * Copyright (C) 2005 Mike McCormack for CodeWeavers + * + * A test program for MSI records + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include <windows.h> +#include <libmsi.h> + +#include "test.h" + +static const char *msifile = "winetest-record.msi"; + +static BOOL create_temp_file(char *name) +{ + UINT r; + unsigned char buffer[26], i; + DWORD sz; + HANDLE handle; + + r = GetTempFileName(".", "msitest",0,name); + if(!r) + return r; + handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, + 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if(handle==INVALID_HANDLE_VALUE) + return 0; + for(i=0; i<26; i++) + buffer[i]=i+'a'; + r = WriteFile(handle,buffer,sizeof buffer,&sz,NULL); + CloseHandle(handle); + return r; +} + +static void test_msirecord(void) +{ + DWORD r, sz; + INT i; + MSIHANDLE h; + char buf[10]; + WCHAR bufW[10]; + const char str[] = "hello"; + const WCHAR strW[] = { 'h','e','l','l','o',0}; + char filename[MAX_PATH]; + + /* check behaviour with an invalid record */ + r = MsiRecordGetFieldCount(0); + ok(r==-1, "field count for invalid record not -1\n"); + SetLastError(0); + r = MsiRecordIsNull(0, 0); + ok(r==0, "invalid handle not considered to be non-null...\n"); + ok(GetLastError()==0, "MsiRecordIsNull set LastError\n"); + r = MsiRecordGetInteger(0,0); + ok(r == MSI_NULL_INTEGER, "got integer from invalid record\n"); + r = MsiRecordSetInteger(0,0,0); + ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n"); + r = MsiRecordSetInteger(0,-1,0); + ok(r == ERROR_INVALID_HANDLE, "MsiRecordSetInteger returned wrong error\n"); + SetLastError(0); + h = MsiCreateRecord(-1); + ok(h==0, "created record with -1 elements\n"); + h = MsiCreateRecord(0x10000); + ok(h==0, "created record with 0x10000 elements\n"); + /* doesn't set LastError */ + ok(GetLastError()==0, "MsiCreateRecord set last error\n"); + r = MsiRecordClearData(0); + ok(r == ERROR_INVALID_HANDLE, "MsiRecordClearData returned wrong error\n"); + r = MsiRecordDataSize(0,0); + ok(r == 0, "MsiRecordDataSize returned wrong error\n"); + + + /* check behaviour of a record with 0 elements */ + h = MsiCreateRecord(0); + ok(h!=0, "couldn't create record with zero elements\n"); + r = MsiRecordGetFieldCount(h); + ok(r==0, "field count should be zero\n"); + r = MsiRecordIsNull(h,0); + ok(r, "new record wasn't null\n"); + r = MsiRecordIsNull(h,1); + ok(r, "out of range record wasn't null\n"); + r = MsiRecordIsNull(h,-1); + ok(r, "out of range record wasn't null\n"); + r = MsiRecordDataSize(h,0); + ok(r==0, "size of null record is 0\n"); + sz = sizeof buf; + strcpy(buf,"x"); + r = MsiRecordGetString(h, 0, buf, &sz); + ok(r==ERROR_SUCCESS, "failed to get null string\n"); + ok(sz==0, "null string too long\n"); + ok(buf[0]==0, "null string not set\n"); + + /* same record, but add an integer to it */ + r = MsiRecordSetInteger(h, 0, 0); + ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 0\n"); + r = MsiRecordIsNull(h,0); + ok(r==0, "new record is null after setting an integer\n"); + r = MsiRecordDataSize(h,0); + ok(r==sizeof(DWORD), "size of integer record is 4\n"); + r = MsiRecordSetInteger(h, 0, 1); + ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 1\n"); + r = MsiRecordSetInteger(h, 1, 1); + ok(r == ERROR_INVALID_PARAMETER, "set integer at 1\n"); + r = MsiRecordSetInteger(h, -1, 0); + ok(r == ERROR_INVALID_PARAMETER, "set integer at -1\n"); + r = MsiRecordIsNull(h,0); + ok(r==0, "new record is null after setting an integer\n"); + r = MsiRecordGetInteger(h, 0); + ok(r == 1, "failed to get integer\n"); + + /* same record, but add a null or empty string to it */ + r = MsiRecordSetString(h, 0, NULL); + ok(r == ERROR_SUCCESS, "Failed to set null string at 0\n"); + r = MsiRecordIsNull(h, 0); + ok(r == TRUE, "null string not null field\n"); + r = MsiRecordDataSize(h, 0); + ok(r == 0, "size of string record is strlen\n"); + buf[0] = 0; + sz = sizeof buf; + r = MsiRecordGetStringA(h, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "Failed to get string at 0\n"); + ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n"); + ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n"); + bufW[0] = 0; + sz = sizeof bufW / sizeof bufW[0]; + r = MsiRecordGetStringW(h, 0, bufW, &sz); + ok(r == ERROR_SUCCESS, "Failed to get string at 0\n"); + ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n"); + ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n"); + r = MsiRecordSetString(h, 0, ""); + ok(r == ERROR_SUCCESS, "Failed to set empty string at 0\n"); + r = MsiRecordIsNull(h, 0); + ok(r == TRUE, "null string not null field\n"); + r = MsiRecordDataSize(h, 0); + ok(r == 0, "size of string record is strlen\n"); + buf[0] = 0; + sz = sizeof buf; + r = MsiRecordGetStringA(h, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "Failed to get string at 0\n"); + ok(buf[0] == 0, "MsiRecordGetStringA returned the wrong string\n"); + ok(sz == 0, "MsiRecordGetStringA returned the wrong length\n"); + bufW[0] = 0; + sz = sizeof bufW / sizeof bufW[0]; + r = MsiRecordGetStringW(h, 0, bufW, &sz); + ok(r == ERROR_SUCCESS, "Failed to get string at 0\n"); + ok(bufW[0] == 0, "MsiRecordGetStringW returned the wrong string\n"); + ok(sz == 0, "MsiRecordGetStringW returned the wrong length\n"); + + /* same record, but add a string to it */ + r = MsiRecordSetString(h,0,str); + ok(r == ERROR_SUCCESS, "Failed to set string at 0\n"); + r = MsiRecordGetInteger(h, 0); + ok(r == MSI_NULL_INTEGER, "should get invalid integer\n"); + r = MsiRecordDataSize(h,0); + ok(r==sizeof str-1, "size of string record is strlen\n"); + buf[0]=0; + sz = sizeof buf; + r = MsiRecordGetString(h,0,buf,&sz); + ok(r == ERROR_SUCCESS, "Failed to get string at 0\n"); + ok(0==strcmp(buf,str), "MsiRecordGetString returned the wrong string\n"); + ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n"); + buf[0]=0; + sz = sizeof str - 2; + r = MsiRecordGetString(h,0,buf,&sz); + ok(r == ERROR_MORE_DATA, "small buffer should yield ERROR_MORE_DATA\n"); + ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n"); + ok(0==strncmp(buf,str,sizeof str-3), "MsiRecordGetString returned the wrong string\n"); + ok(buf[sizeof str - 3]==0, "string wasn't nul terminated\n"); + + buf[0]=0; + sz = sizeof str; + r = MsiRecordGetString(h,0,buf,&sz); + ok(r == ERROR_SUCCESS, "wrong error\n"); + ok(sz == sizeof str-1, "MsiRecordGetString returned the wrong length\n"); + ok(0==strcmp(buf,str), "MsiRecordGetString returned the wrong string\n"); + + + memset(bufW, 0, sizeof bufW); + sz = 5; + r = MsiRecordGetStringW(h,0,bufW,&sz); + ok(r == ERROR_MORE_DATA, "wrong error\n"); + ok(sz == 5, "MsiRecordGetString returned the wrong length\n"); + ok(0==memcmp(bufW,strW,8), "MsiRecordGetString returned the wrong string\n"); + + sz = 0; + bufW[0] = 'x'; + r = MsiRecordGetStringW(h,0,bufW,&sz); + ok(r == ERROR_MORE_DATA, "wrong error\n"); + ok(sz == 5, "MsiRecordGetString returned the wrong length\n"); + ok('x'==bufW[0], "MsiRecordGetString returned the wrong string\n"); + + memset(buf, 0, sizeof buf); + sz = 5; + r = MsiRecordGetStringA(h,0,buf,&sz); + ok(r == ERROR_MORE_DATA, "wrong error\n"); + ok(sz == 5, "MsiRecordGetString returned the wrong length\n"); + ok(0==memcmp(buf,str,4), "MsiRecordGetString returned the wrong string\n"); + + sz = 0; + buf[0] = 'x'; + r = MsiRecordGetStringA(h,0,buf,&sz); + ok(r == ERROR_MORE_DATA, "wrong error\n"); + ok(sz == 5, "MsiRecordGetString returned the wrong length\n"); + ok('x'==buf[0], "MsiRecordGetString returned the wrong string\n"); + + /* same record, check we can wipe all the data */ + r = MsiRecordClearData(h); + ok(r == ERROR_SUCCESS, "Failed to clear record\n"); + r = MsiRecordClearData(h); + ok(r == ERROR_SUCCESS, "Failed to clear record again\n"); + r = MsiRecordIsNull(h,0); + ok(r, "cleared record wasn't null\n"); + + /* same record, try converting strings to integers */ + i = MsiRecordSetString(h,0,"42"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == 42, "should get invalid integer\n"); + i = MsiRecordSetString(h,0,"-42"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == -42, "should get invalid integer\n"); + i = MsiRecordSetString(h,0," 42"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get invalid integer\n"); + i = MsiRecordSetString(h,0,"42 "); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get invalid integer\n"); + i = MsiRecordSetString(h,0,"42.0"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get invalid integer\n"); + i = MsiRecordSetString(h,0,"0x42"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get invalid integer\n"); + i = MsiRecordSetString(h,0,"1000000000000000"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == -1530494976, "should get truncated integer\n"); + i = MsiRecordSetString(h,0,"2147483647"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == 2147483647, "should get maxint\n"); + i = MsiRecordSetString(h,0,"-2147483647"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == -2147483647, "should get -maxint-1\n"); + i = MsiRecordSetString(h,0,"4294967297"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == 1, "should get one\n"); + i = MsiRecordSetString(h,0,"foo"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get zero\n"); + i = MsiRecordSetString(h,0,""); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get zero\n"); + i = MsiRecordSetString(h,0,"+1"); + ok(i == ERROR_SUCCESS, "Failed to set string at 0\n"); + i = MsiRecordGetInteger(h, 0); + ok(i == MSI_NULL_INTEGER, "should get zero\n"); + + /* same record, try converting integers to strings */ + r = MsiRecordSetInteger(h, 0, 32); + ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n"); + sz = 1; + r = MsiRecordGetString(h, 0, NULL, &sz); + ok(r == ERROR_SUCCESS, "failed to get string from integer\n"); + ok(sz == 2, "length wrong\n"); + buf[0]=0; + sz = sizeof buf; + r = MsiRecordGetString(h, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to get string from integer\n"); + ok(0==strcmp(buf,"32"), "failed to get string from integer\n"); + r = MsiRecordSetInteger(h, 0, -32); + ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 32\n"); + buf[0]=0; + sz = 1; + r = MsiRecordGetString(h, 0, NULL, &sz); + ok(r == ERROR_SUCCESS, "failed to get string from integer\n"); + ok(sz == 3, "length wrong\n"); + sz = sizeof buf; + r = MsiRecordGetString(h, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to get string from integer\n"); + ok(0==strcmp(buf,"-32"), "failed to get string from integer\n"); + buf[0]=0; + + /* same record, now try streams */ + r = MsiRecordSetStream(h, 0, NULL); + ok(r == ERROR_INVALID_PARAMETER, "set NULL stream\n"); + sz = sizeof buf; + r = MsiRecordReadStream(h, 0, buf, &sz); + ok(r == ERROR_INVALID_DATATYPE, "read non-stream type\n"); + ok(sz == sizeof buf, "set sz\n"); + r = MsiRecordDataSize( h, -1); + ok(r == 0,"MsiRecordDataSize returned wrong size\n"); + r = MsiRecordDataSize( h, 0); + ok(r == 4,"MsiRecordDataSize returned wrong size\n"); + + /* same record, now close it */ + r = MsiCloseHandle(h); + ok(r == ERROR_SUCCESS, "Failed to close handle\n"); + + /* now try streams in a new record - need to create a file to play with */ + r = create_temp_file(filename); + if(!r) + return; + + /* streams can't be inserted in field 0 for some reason */ + h = MsiCreateRecord(2); + ok(h, "couldn't create a two field record\n"); + r = MsiRecordSetStream(h, 0, filename); + ok(r == ERROR_INVALID_PARAMETER, "added stream to field 0\n"); + r = MsiRecordSetStream(h, 1, filename); + ok(r == ERROR_SUCCESS, "failed to add stream to record\n"); + r = MsiRecordReadStream(h, 1, buf, NULL); + ok(r == ERROR_INVALID_PARAMETER, "should return error\n"); + DeleteFile(filename); /* Windows 98 doesn't like this at all, so don't check return. */ + r = MsiRecordReadStream(h, 1, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "should return error\n"); + sz = sizeof buf; + r = MsiRecordReadStream(h, 1, NULL, &sz); + ok(r == ERROR_SUCCESS, "failed to read stream\n"); + ok(sz==26,"couldn't get size of stream\n"); + sz = 0; + r = MsiRecordReadStream(h, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to read stream\n"); + ok(sz==0,"short read\n"); + sz = sizeof buf; + r = MsiRecordReadStream(h, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to read stream\n"); + ok(sz==sizeof buf,"short read\n"); + ok(!strncmp(buf,"abcdefghij",10), "read the wrong thing\n"); + sz = sizeof buf; + r = MsiRecordReadStream(h, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to read stream\n"); + ok(sz==sizeof buf,"short read\n"); + ok(!strncmp(buf,"klmnopqrst",10), "read the wrong thing\n"); + memset(buf,0,sizeof buf); + sz = sizeof buf; + r = MsiRecordReadStream(h, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to read stream\n"); + ok(sz==6,"short read\n"); + ok(!strcmp(buf,"uvwxyz"), "read the wrong thing\n"); + memset(buf,0,sizeof buf); + sz = sizeof buf; + r = MsiRecordReadStream(h, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "failed to read stream\n"); + ok(sz==0,"size non-zero at end of stream\n"); + ok(buf[0]==0, "read something at end of the stream\n"); + r = MsiRecordSetStream(h, 1, NULL); + ok(r == ERROR_SUCCESS, "failed to reset stream\n"); + sz = 0; + r = MsiRecordReadStream(h, 1, NULL, &sz); + ok(r == ERROR_SUCCESS, "bytes left wrong after reset\n"); + ok(sz==26,"couldn't get size of stream\n"); + r = MsiRecordDataSize(h,1); + ok(r == 26,"MsiRecordDataSize returned wrong size\n"); + + /* now close the stream record */ + r = MsiCloseHandle(h); + ok(r == ERROR_SUCCESS, "Failed to close handle\n"); + DeleteFile(filename); /* Delete it for sure, when everything else is closed. */ +} + +static void test_MsiRecordGetString(void) +{ + MSIHANDLE rec; + CHAR buf[MAX_PATH]; + DWORD sz; + UINT r; + + rec = MsiCreateRecord(2); + ok(rec != 0, "Expected a valid handle\n"); + + sz = MAX_PATH; + r = MsiRecordGetString(rec, 1, NULL, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r); + ok(sz == 0, "Expected 0, got %d\n",sz); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(sz == 0, "Expected 0, got %d\n", sz); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 10, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(sz == 0, "Expected 0, got %d\n", sz); + + MsiCloseHandle(rec); + + rec = MsiCreateRecord(1); + ok(rec != 0, "Expected a valid handle\n"); + + r = MsiRecordSetInteger(rec, 1, 5); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + r = MsiRecordGetString(rec, 1, NULL, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n",r); + ok(sz == 1, "Expected 1, got %d\n",sz); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "5"), "Expected \"5\", got \"%s\"\n", buf); + ok(sz == 1, "Expectd 1, got %d\n", sz); + + r = MsiRecordSetInteger(rec, 1, -5); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "-5"), "Expected \"-5\", got \"%s\"\n", buf); + ok(sz == 2, "Expectd 2, got %d\n", sz); + + MsiCloseHandle(rec); +} + +static void test_MsiRecordGetInteger(void) +{ + MSIHANDLE rec; + INT val; + UINT r; + + rec = MsiCreateRecord(1); + ok(rec != 0, "Expected a valid handle\n"); + + r = MsiRecordSetString(rec, 1, "5"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(rec, 1); + ok(val == 5, "Expected 5, got %d\n", val); + + r = MsiRecordSetString(rec, 1, "-5"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(rec, 1); + ok(val == -5, "Expected -5, got %d\n", val); + + r = MsiRecordSetString(rec, 1, "5apple"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + val = MsiRecordGetInteger(rec, 1); + ok(val == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", val); + + MsiCloseHandle(rec); +} + +static void test_fieldzero(void) +{ + MSIHANDLE hdb, hview, rec; + CHAR buf[MAX_PATH]; + LPCSTR query; + DWORD sz; + UINT r; + + rec = MsiCreateRecord(1); + ok(rec != 0, "Expected a valid handle\n"); + + r = MsiRecordGetInteger(rec, 0); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(sz == 0, "Expectd 0, got %d\n", sz); + + r = MsiRecordIsNull(rec, 0); + ok(r == TRUE, "Expected TRUE, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + r = MsiRecordSetInteger(rec, 1, 42); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 0); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(sz == 0, "Expectd 0, got %d\n", sz); + + r = MsiRecordIsNull(rec, 0); + ok(r == TRUE, "Expected TRUE, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == 42, "Expected 42, got %d\n", r); + + r = MsiRecordSetString(rec, 1, "bologna"); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 0); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, ""), "Expected \"\", got \"%s\"\n", buf); + ok(sz == 0, "Expectd 0, got %d\n", sz); + + r = MsiRecordIsNull(rec, 0); + ok(r == TRUE, "Expected TRUE, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 1, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "bologna"), "Expected \"bologna\", got \"%s\"\n", buf); + ok(sz == 7, "Expectd 7, got %d\n", sz); + + MsiCloseHandle(rec); + + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + query = "CREATE TABLE `drone` ( " + "`id` INT, `name` CHAR(32), `number` CHAR(32) " + "PRIMARY KEY `id`)"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + query = "INSERT INTO `drone` ( `id`, `name`, `number` )" + "VALUES('1', 'Abe', '8675309')"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "MsiDatabaseOpenView failed\n"); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "MsiViewExecute failed\n"); + r = MsiViewClose(hview); + ok(r == ERROR_SUCCESS, "MsiViewClose failed\n"); + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiDatabaseGetPrimaryKeysA(hdb, "drone", &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 0); + ok(r == MSI_NULL_INTEGER, "Expected MSI_NULL_INTEGER, got %d\n", r); + + sz = MAX_PATH; + lstrcpyA(buf, "apple"); + r = MsiRecordGetString(rec, 0, buf, &sz); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + ok(!lstrcmpA(buf, "drone"), "Expected \"drone\", got \"%s\"\n", buf); + ok(sz == 5, "Expectd 5, got %d\n", sz); + + r = MsiRecordIsNull(rec, 0); + ok(r == FALSE, "Expected FALSE, got %d\n", r); + + MsiCloseHandle(rec); + + r = MsiDatabaseGetPrimaryKeysA(hdb, "nosuchtable", &rec); + ok(r == ERROR_INVALID_TABLE, "Expected ERROR_INVALID_TABLE, got %d\n", r); + + query = "SELECT * FROM `drone` WHERE `id` = 1"; + r = MsiDatabaseOpenView(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewFetch(hview, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 0); + ok(r != MSI_NULL_INTEGER && r != 0, "Expected non-NULL value, got %d\n", r); + + r = MsiRecordIsNull(rec, 0); + ok(r == FALSE, "Expected FALSE, got %d\n", r); + + r = MsiCloseHandle(hview); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + MsiCloseHandle(rec); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +void main() +{ + test_msirecord(); + test_MsiRecordGetString(); + test_MsiRecordGetInteger(); + test_fieldzero(); +} diff --git a/tests/testrecord.ok b/tests/testrecord.ok new file mode 100644 index 0000000..2b9aa76 --- /dev/null +++ b/tests/testrecord.ok @@ -0,0 +1,214 @@ +ok: r==-1
+ok: r==0
+ok: GetLastError()==0
+ok: r == MSI_NULL_INTEGER
+ok: r == ERROR_INVALID_HANDLE
+ok: r == ERROR_INVALID_HANDLE
+ok: h==0
+ok: h==0
+ok: GetLastError()==0
+ok: r == ERROR_INVALID_HANDLE
+ok: r == 0
+ok: h!=0
+ok: r==0
+ok: r
+ok: r
+ok: r
+ok: r==0
+ok: r==ERROR_SUCCESS
+ok: sz==0
+ok: buf[0]==0
+ok: r == ERROR_SUCCESS
+ok: r==0
+ok: r==sizeof(DWORD)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_INVALID_PARAMETER
+ok: r==0
+ok: r == 1
+ok: r == ERROR_SUCCESS
+ok: r == TRUE
+ok: r == 0
+ok: r == ERROR_SUCCESS
+ok: buf[0] == 0
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: bufW[0] == 0
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: r == TRUE
+ok: r == 0
+ok: r == ERROR_SUCCESS
+ok: buf[0] == 0
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: bufW[0] == 0
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: r == MSI_NULL_INTEGER
+ok: r==sizeof str-1
+ok: r == ERROR_SUCCESS
+ok: 0==strcmp(buf,str)
+ok: sz == sizeof str-1
+ok: r == ERROR_MORE_DATA
+ok: sz == sizeof str-1
+ok: 0==strncmp(buf,str,sizeof str-3)
+ok: buf[sizeof str - 3]==0
+ok: r == ERROR_SUCCESS
+ok: sz == sizeof str-1
+ok: 0==strcmp(buf,str)
+ok: r == ERROR_MORE_DATA
+ok: sz == 5
+ok: 0==memcmp(bufW,strW,8)
+ok: r == ERROR_MORE_DATA
+ok: sz == 5
+ok: 'x'==bufW[0]
+ok: r == ERROR_MORE_DATA
+ok: sz == 5
+ok: 0==memcmp(buf,str,4)
+ok: r == ERROR_MORE_DATA
+ok: sz == 5
+ok: 'x'==buf[0]
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r
+ok: i == ERROR_SUCCESS
+ok: i == 42
+ok: i == ERROR_SUCCESS
+ok: i == -42
+ok: i == ERROR_SUCCESS
+ok: i == MSI_NULL_INTEGER
+ok: i == ERROR_SUCCESS
+ok: i == MSI_NULL_INTEGER
+ok: i == ERROR_SUCCESS
+ok: i == MSI_NULL_INTEGER
+ok: i == ERROR_SUCCESS
+ok: i == MSI_NULL_INTEGER
+ok: i == ERROR_SUCCESS
+ok: i == -1530494976
+ok: i == ERROR_SUCCESS
+ok: i == 2147483647
+ok: i == ERROR_SUCCESS
+ok: i == -2147483647
+ok: i == ERROR_SUCCESS
+ok: i == 1
+ok: i == ERROR_SUCCESS
+ok: i == MSI_NULL_INTEGER
+ok: i == ERROR_SUCCESS
+ok: i == MSI_NULL_INTEGER
+ok: i == ERROR_SUCCESS
+ok: i == MSI_NULL_INTEGER
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: sz == 2
+ok: r == ERROR_SUCCESS
+ok: 0==strcmp(buf,"32")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: sz == 3
+ok: r == ERROR_SUCCESS
+ok: 0==strcmp(buf,"-32")
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_INVALID_DATATYPE
+ok: sz == sizeof buf
+ok: r == 0
+ok: r == 4
+ok: r == ERROR_SUCCESS
+ok: h
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_SUCCESS
+ok: sz==26
+ok: r == ERROR_SUCCESS
+ok: sz==0
+ok: r == ERROR_SUCCESS
+ok: sz==sizeof buf
+ok: !strncmp(buf,"abcdefghij",10)
+ok: r == ERROR_SUCCESS
+ok: sz==sizeof buf
+ok: !strncmp(buf,"klmnopqrst",10)
+ok: r == ERROR_SUCCESS
+ok: sz==6
+ok: !strcmp(buf,"uvwxyz")
+ok: r == ERROR_SUCCESS
+ok: sz==0
+ok: buf[0]==0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: sz==26
+ok: r == 26
+ok: r == ERROR_SUCCESS
+ok: rec != 0
+ok: r == ERROR_SUCCESS
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "")
+ok: sz == 0
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "")
+ok: sz == 0
+ok: rec != 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: sz == 1
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "5")
+ok: sz == 1
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "-5")
+ok: sz == 2
+ok: rec != 0
+ok: r == ERROR_SUCCESS
+ok: val == 5
+ok: r == ERROR_SUCCESS
+ok: val == -5
+ok: r == ERROR_SUCCESS
+ok: val == MSI_NULL_INTEGER
+ok: rec != 0
+ok: r == MSI_NULL_INTEGER
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "")
+ok: sz == 0
+ok: r == TRUE
+ok: r == MSI_NULL_INTEGER
+ok: r == ERROR_SUCCESS
+ok: r == MSI_NULL_INTEGER
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "")
+ok: sz == 0
+ok: r == TRUE
+ok: r == 42
+ok: r == ERROR_SUCCESS
+ok: r == MSI_NULL_INTEGER
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "")
+ok: sz == 0
+ok: r == TRUE
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "bologna")
+ok: sz == 7
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == MSI_NULL_INTEGER
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(buf, "drone")
+ok: sz == 5
+ok: r == FALSE
+ok: r == ERROR_INVALID_TABLE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r != MSI_NULL_INTEGER && r != 0
+ok: r == FALSE
+ok: r == ERROR_SUCCESS
diff --git a/tests/testsuminfo.c b/tests/testsuminfo.c new file mode 100644 index 0000000..650f416 --- /dev/null +++ b/tests/testsuminfo.c @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2005 Mike McCormack for CodeWeavers + * + * A test program for MSI database files. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include <stdio.h> +#include <windows.h> +#include <libmsi.h> +#include <objidl.h> + +#include "test.h" + +static const char *msifile = "winetest-suminfo.msi"; +static const WCHAR msifileW[] = { + 'w','i','n','e','t','e','s','t','-','s','u','m','i','n','f','o','.','m','s','i',0 }; + +static void test_suminfo(void) +{ + MSIHANDLE hdb = 0, hsuminfo; + UINT r, count, type; + DWORD sz; + INT val; + FILETIME ft; + char buf[0x10]; + + DeleteFile(msifile); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + r = MsiGetSummaryInformation(hdb, NULL, 0, NULL); + ok(r == ERROR_INVALID_PARAMETER, "MsiGetSummaryInformation wrong error\n"); + + r = MsiGetSummaryInformation(hdb, NULL, 0, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed %u\n", r); + + r = MsiCloseHandle(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiGetSummaryInformation(0, "", 0, &hsuminfo); + todo_wine + ok(r == ERROR_INSTALL_PACKAGE_INVALID || r == ERROR_INSTALL_PACKAGE_OPEN_FAILED, + "MsiGetSummaryInformation failed %u\n", r); + + r = MsiGetSummaryInformation(hdb, "", 0, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed %u\n", r); + + r = MsiSummaryInfoGetPropertyCount(0, NULL); + ok(r == ERROR_INVALID_HANDLE, "getpropcount failed\n"); + + r = MsiSummaryInfoGetPropertyCount(hsuminfo, NULL); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + + count = -1; + r = MsiSummaryInfoGetPropertyCount(hsuminfo, &count); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + ok(count == 0, "count should be zero\n"); + + r = MsiSummaryInfoGetProperty(hsuminfo, 0, NULL, NULL, NULL, 0, NULL); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + + r = MsiSummaryInfoGetProperty(hsuminfo, -1, NULL, NULL, NULL, 0, NULL); + ok(r == ERROR_UNKNOWN_PROPERTY, "MsiSummaryInfoGetProperty wrong error\n"); + + r = MsiSummaryInfoGetProperty(hsuminfo, MSI_PID_SECURITY+1, NULL, NULL, NULL, 0, NULL); + ok(r == ERROR_UNKNOWN_PROPERTY, "MsiSummaryInfoGetProperty wrong error\n"); + + type = -1; + r = MsiSummaryInfoGetProperty(hsuminfo, 0, &type, NULL, NULL, 0, NULL); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + ok(type == 0, "wrong type\n"); + + type = -1; + val = 1234; + r = MsiSummaryInfoGetProperty(hsuminfo, 0, &type, &val, NULL, 0, NULL); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + ok(type == 0, "wrong type\n"); + ok(val == 1234, "wrong val\n"); + + buf[0]='x'; + buf[1]=0; + sz = 0x10; + r = MsiSummaryInfoGetProperty(hsuminfo, MSI_PID_REVNUMBER, &type, &val, NULL, buf, &sz); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + ok(buf[0]=='x', "cleared buffer\n"); + ok(sz == 0x10, "count wasn't zero\n"); + ok(type == VT_EMPTY, "should be empty\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TITLE, VT_LPSTR, 0, NULL, "Mike"); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TITLE, VT_LPSTR, 1, NULL, "JungAh"); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TITLE, VT_LPSTR, 1, &ft, "Mike"); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_CODEPAGE, VT_I2, 1, &ft, "JungAh"); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiCloseHandle(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* try again with the update count set */ + r = MsiGetSummaryInformation(hdb, NULL, 1, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, 0, VT_LPSTR, 1, NULL, NULL); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_CODEPAGE, VT_LPSTR, 1, NULL, NULL); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TITLE, VT_I4, 0, NULL, "Mike"); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_AUTHOR, VT_I4, 0, NULL, "JungAh"); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_KEYWORDS, VT_I2, 0, NULL, "Mike"); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_COMMENTS, VT_FILETIME, 0, NULL, "JungAh"); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TEMPLATE, VT_I2, 0, NULL, "Mike"); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_LASTAUTHOR, VT_LPSTR, 0, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_LASTSAVE_DTM, VT_FILETIME, 0, NULL, NULL); + ok(r == ERROR_INVALID_PARAMETER, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_LASTAUTHOR, VT_LPWSTR, 0, NULL, "h\0i\0\0"); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_REVNUMBER, VT_I4, 0, NULL, "Jungah"); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_PAGECOUNT, VT_LPSTR, 1, NULL, NULL); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TITLE, VT_LPSTR, 0, NULL, "Mike"); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n"); + + sz = 2; + strcpy(buf,"x"); + r = MsiSummaryInfoGetProperty(hsuminfo, MSI_PID_TITLE, &type, NULL, NULL, buf, &sz ); + ok(r == ERROR_MORE_DATA, "MsiSummaryInfoSetProperty failed\n"); + ok(sz == 4, "count was wrong\n"); + ok(type == VT_LPSTR, "type was wrong\n"); + ok(!strcmp(buf,"M"), "buffer was wrong\n"); + + sz = 4; + strcpy(buf,"x"); + r = MsiSummaryInfoGetProperty(hsuminfo, MSI_PID_TITLE, &type, NULL, NULL, buf, &sz ); + ok(r == ERROR_MORE_DATA, "MsiSummaryInfoSetProperty failed\n"); + ok(sz == 4, "count was wrong\n"); + ok(type == VT_LPSTR, "type was wrong\n"); + ok(!strcmp(buf,"Mik"), "buffer was wrong\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TITLE, VT_LPSTR, 0, NULL, "JungAh"); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_CODEPAGE, VT_I2, 1, &ft, "Mike"); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiCloseHandle(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* try again with a higher update count */ + r = MsiGetSummaryInformation(hdb, NULL, 10, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_TITLE, VT_LPSTR, 0, NULL, "JungAh"); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty failed\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_CODEPAGE, VT_LPSTR, 1, NULL, NULL); + ok(r == ERROR_DATATYPE_MISMATCH, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_CODEPAGE, VT_I2, 1, NULL, NULL); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_CODEPAGE, VT_I2, 1, &ft, "Mike"); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_AUTHOR, VT_LPSTR, 1, &ft, "Mike"); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoPersist(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoPersist failed\n"); + + MsiDatabaseCommit(hdb); + + r = MsiCloseHandle(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = MsiCloseHandle(hdb); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + /* filename, non-zero update count */ + r = MsiGetSummaryInformation(0, msifile, 1, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n"); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_AUTHOR, VT_LPSTR, 1, &ft, "Mike"); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoSetProperty wrong error\n"); + + r = MsiSummaryInfoPersist(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoPersist failed %u\n", r); + + r = MsiCloseHandle(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed %u\n", r); + + /* filename, zero update count */ + r = MsiGetSummaryInformation(0, msifile, 0, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed %u\n", r); + + r = MsiSummaryInfoSetProperty(hsuminfo, MSI_PID_AUTHOR, VT_LPSTR, 1, &ft, "Mike"); + todo_wine ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty wrong error, %u\n", r); + + r = MsiSummaryInfoPersist(hsuminfo); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoPersist wrong error %u\n", r); + + r = MsiCloseHandle(hsuminfo); + ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n"); + + r = DeleteFile(msifile); + ok(r, "DeleteFile failed\n"); +} + +static const WCHAR tb[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */ +static const WCHAR sd[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */ +static const WCHAR sp[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */ + +#define LOSE_CONST(x) ((LPSTR)(UINT_PTR)(x)) + +static void test_create_database_binary(void) +{ + static const CLSID CLSID_MsiDatabase = + { 0xc1084, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46 } }; + static const CLSID IID_IPropertySetStorage = + { 0x13a, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46 } }; + static const CLSID FMTID_SummaryInformation = + { 0xf29f85e0, 0x4ff9, 0x1068, {0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9}}; + DWORD mode = STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE; + IPropertySetStorage *pss = NULL; + IPropertyStorage *ps = NULL; + IStorage *stg = NULL; + IStream *stm = NULL; + HRESULT r; + PROPSPEC propspec[10]; + PROPVARIANT propvar[10]; + USHORT data[2] = { 0, 0 }; + + r = StgCreateDocfile( msifileW, mode, 0, &stg ); + ok( r == S_OK, "failed to create database\n"); + + r = IStorage_SetClass( stg, &CLSID_MsiDatabase ); + ok( r == S_OK, "failed to set clsid\n"); + + /* create the _StringData stream */ + r = IStorage_CreateStream( stg, sd, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + ok( r == S_OK, "failed to create stream\n"); + + IStream_Release( stm ); + + /* create the _StringPool stream */ + r = IStorage_CreateStream( stg, sp, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + ok( r == S_OK, "failed to create stream\n"); + + r = IStream_Write( stm, data, sizeof data, NULL ); + ok( r == S_OK, "failed to write stream\n"); + + IStream_Release( stm ); + + /* create the _Tables stream */ + r = IStorage_CreateStream( stg, tb, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + ok( r == S_OK, "failed to create stream\n"); + + IStream_Release( stm ); + + r = IStorage_QueryInterface( stg, &IID_IPropertySetStorage, (void**) &pss ); + ok( r == S_OK, "failed to set clsid\n"); + + r = IPropertySetStorage_Create( pss, &FMTID_SummaryInformation, NULL, 0, mode, &ps ); + ok( r == S_OK, "failed to create property set\n"); + + r = IPropertyStorage_SetClass( ps, &FMTID_SummaryInformation ); + ok( r == S_OK, "failed to set class\n"); + + propspec[0].ulKind = PRSPEC_PROPID; + propspec[0].propid = MSI_PID_TITLE; + propvar[0].vt = VT_LPSTR; + propvar[0].pszVal = LOSE_CONST("test title"); + + propspec[1].ulKind = PRSPEC_PROPID; + propspec[1].propid = MSI_PID_SUBJECT; + propvar[1].vt = VT_LPSTR; + propvar[1].pszVal = LOSE_CONST("msi suminfo / property storage test"); + + propspec[2].ulKind = PRSPEC_PROPID; + propspec[2].propid = MSI_PID_AUTHOR; + propvar[2].vt = VT_LPSTR; + propvar[2].pszVal = LOSE_CONST("mike_m"); + + propspec[3].ulKind = PRSPEC_PROPID; + propspec[3].propid = MSI_PID_TEMPLATE; + propvar[3].vt = VT_LPSTR; + propvar[3].pszVal = LOSE_CONST(";1033"); /* actually the string table's codepage */ + + propspec[4].ulKind = PRSPEC_PROPID; + propspec[4].propid = MSI_PID_REVNUMBER; + propvar[4].vt = VT_LPSTR; + propvar[4].pszVal = LOSE_CONST("{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}"); + + propspec[5].ulKind = PRSPEC_PROPID; + propspec[5].propid = MSI_PID_PAGECOUNT; + propvar[5].vt = VT_I4; + propvar[5].lVal = 100; + + propspec[6].ulKind = PRSPEC_PROPID; + propspec[6].propid = MSI_PID_WORDCOUNT; + propvar[6].vt = VT_I4; + propvar[6].lVal = 0; + + /* MSDN says that MSI_PID_LASTPRINTED should be a VT_FILETIME... */ + propspec[7].ulKind = PRSPEC_PROPID; + propspec[7].propid = MSI_PID_LASTPRINTED; + propvar[7].vt = VT_LPSTR; + propvar[7].pszVal = LOSE_CONST("7/1/1999 5:17"); + + r = IPropertyStorage_WriteMultiple( ps, 8, propspec, propvar, MSI_PID_FIRST_USABLE ); + ok( r == S_OK, "failed to write properties\n"); + + IPropertyStorage_Commit( ps, STGC_DEFAULT ); + + IPropertyStorage_Release( ps ); + IPropertySetStorage_Release( pss ); + + IStorage_Commit( stg, STGC_DEFAULT ); + IStorage_Release( stg ); +} + +static void test_summary_binary(void) +{ + MSIHANDLE hdb = 0, hsuminfo = 0; + UINT r, type, count; + INT ival; + DWORD sz; + char sval[20]; + + DeleteFile( msifile ); + + test_create_database_binary(); + + ok( INVALID_FILE_ATTRIBUTES != GetFileAttributes(msifile), "file doesn't exist!\n"); + + /* just MsiOpenDatabase should not create a file */ + r = MsiOpenDatabase(msifile, MSIDBOPEN_READONLY, &hdb); + ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n"); + + r = MsiGetSummaryInformation(hdb, NULL, 0, &hsuminfo); + ok(r == ERROR_SUCCESS, "MsiGetSummaryInformation failed\n"); + + /* + * Check what reading MSI_PID_LASTPRINTED does... + * The string value is written to the msi file + * but it appears that we're not allowed to read it back again. + * We can still read its type though...? + */ + sz = sizeof sval; + sval[0] = 0; + type = 0; + r = MsiSummaryInfoGetProperty(hsuminfo, MSI_PID_LASTPRINTED, &type, NULL, NULL, sval, &sz); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoGetProperty failed\n"); + ok(!lstrcmpA(sval, "") || !lstrcmpA(sval, "7"), + "Expected empty string or \"7\", got \"%s\"\n", sval); + todo_wine { + ok(type == VT_LPSTR, "Expected VT_LPSTR, got %d\n", type); + ok(sz == 0 || sz == 1, "Expected 0 or 1, got %d\n", sz); + } + + ival = -1; + r = MsiSummaryInfoGetProperty(hsuminfo, MSI_PID_WORDCOUNT, &type, &ival, NULL, NULL, NULL); + ok(r == ERROR_SUCCESS, "MsiSummaryInfoGetProperty failed\n"); + todo_wine ok( ival == 0, "value incorrect\n"); + + /* looks like msi adds some of its own values in here */ + count = 0; + r = MsiSummaryInfoGetPropertyCount( hsuminfo, &count ); + ok(r == ERROR_SUCCESS, "getpropcount failed\n"); + todo_wine ok(count == 10, "prop count incorrect\n"); + + r = MsiSummaryInfoSetProperty( hsuminfo, MSI_PID_TITLE, VT_LPSTR, 0, NULL, "Mike" ); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoSetProperty failed %u\n", r); + + r = MsiSummaryInfoPersist( hsuminfo ); + ok(r == ERROR_FUNCTION_FAILED, "MsiSummaryInfoPersist failed %u\n", r); + + MsiCloseHandle( hsuminfo ); + MsiCloseHandle( hdb ); + + DeleteFile( msifile ); +} + +void main() +{ + test_suminfo(); + test_summary_binary(); +} diff --git a/tests/testsuminfo.ok b/tests/testsuminfo.ok new file mode 100644 index 0000000..19a53ea --- /dev/null +++ b/tests/testsuminfo.ok @@ -0,0 +1,87 @@ +ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_INVALID_HANDLE
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: count == 0
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_UNKNOWN_PROPERTY
+ok: r == ERROR_UNKNOWN_PROPERTY
+ok: r == ERROR_SUCCESS
+ok: type == 0
+ok: r == ERROR_SUCCESS
+ok: type == 0
+ok: val == 1234
+ok: r == ERROR_SUCCESS
+ok: buf[0]=='x'
+ok: sz == 0x10
+ok: type == VT_EMPTY
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_INVALID_PARAMETER
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_MORE_DATA
+ok: sz == 4
+ok: type == VT_LPSTR
+ok: !strcmp(buf,"M")
+ok: r == ERROR_MORE_DATA
+ok: sz == 4
+ok: type == VT_LPSTR
+ok: !strcmp(buf,"Mik")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_DATATYPE_MISMATCH
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_SUCCESS
+ok: r
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: r == S_OK
+ok: INVALID_FILE_ATTRIBUTES != GetFileAttributes(msifile)
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: !lstrcmpA(sval, "") || !lstrcmpA(sval, "7")
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_SUCCESS
+ok: r == ERROR_FUNCTION_FAILED
+ok: r == ERROR_FUNCTION_FAILED
|