[MSI_WINETEST]
[reactos.git] / rostests / winetests / msi / db.c
index 41f65f1..8df17e8 100644 (file)
 
 #include <windows.h>
 #include <msi.h>
+#include <msidefs.h>
 #include <msiquery.h>
 
 #include <objidl.h>
 
 #include "wine/test.h"
 
-static const char *msifile = "winetest.msi";
-static const char *msifile2 = "winetst2.msi";
-static const char *mstfile = "winetst.mst";
+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)
 {
@@ -681,6 +683,30 @@ static void test_msibadqueries(void)
     r = try_query( hdb, "select * from 'c'");
     ok(r == ERROR_BAD_QUERY_SYNTAX, "query failed\n");
 
+    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");
 
@@ -920,6 +946,8 @@ static void test_viewmodify(void)
     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);
@@ -1383,7 +1411,7 @@ static void create_file_data(LPCSTR name, LPCSTR data, DWORD size)
  
 static void test_streamtable(void)
 {
-    MSIHANDLE hdb = 0, rec, view;
+    MSIHANDLE hdb = 0, rec, view, hsi;
     char file[MAX_PATH];
     char buf[MAX_PATH];
     DWORD size;
@@ -1428,6 +1456,46 @@ static void test_streamtable(void)
 
     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, 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" );
 
@@ -1450,8 +1518,30 @@ static void test_streamtable(void)
     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`", &view );
+            "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 );
@@ -1472,16 +1562,260 @@ static void test_streamtable(void)
     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_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+    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;
@@ -1581,6 +1915,30 @@ static void test_where(void)
     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);
+    todo_wine 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, "");
 
@@ -1628,6 +1986,24 @@ static const CHAR endlines2[] = "A\tB\tC\tD\tE\tF\r"
                                 "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\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;
@@ -1650,6 +2026,128 @@ static UINT add_table_to_db(MSIHANDLE hdb, LPCSTR table_data)
     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, 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, 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, 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, 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, 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, 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, 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"),
+       "Expected \"Intel;1033\", got %s\n", str_value);
+
+    size = sizeof(str_value);
+    r = MsiSummaryInfoGetPropertyA(hsi, 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, 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, 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, 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, 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, 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, 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;
@@ -1856,6 +2354,63 @@ static void test_msiimport(void)
     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;
@@ -3242,13 +3797,70 @@ static void test_join(void)
     ok( r == ERROR_BAD_QUERY_SYNTAX,
         "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r );
 
-    MsiCloseHandle(hdb);
-    DeleteFile(msifile);
-}
-
-static void test_temporary_table(void)
-{
-    MSICONDITION cond;
+    /* 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);
+    todo_wine 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;
@@ -4014,163 +4626,453 @@ static void test_special_tables(void)
     ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
 }
 
-static void test_select_markers(void)
+static void test_tables_order(void)
 {
-    MSIHANDLE hdb = 0, rec, view, res;
-    LPCSTR query;
+    const char *query;
+    MSIHANDLE hdb = 0, hview = 0, hrec = 0;
     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);
+    char buffer[100];
+    DWORD sz;
 
-    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 = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
+    ok(r == ERROR_SUCCESS, "MsiOpenDatabase failed\n");
 
-    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);
+    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");
 
-    rec = MsiCreateRecord(2);
-    MsiRecordSetString(rec, 1, "apple");
-    MsiRecordSetString(rec, 2, "two");
+    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 = "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);
+    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");
 
-    r = MsiViewExecute(view, rec);
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    /* 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(view, &res);
+    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");
 
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 1, buf, &size);
+    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(buf, "apple"), "Expected apple, got %s\n", buf);
+    ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer);
+    r = MsiCloseHandle(hrec);
+    ok(r == ERROR_SUCCESS, "failed to close record\n");
 
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 2, buf, &size);
+    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(buf, "two"), "Expected two, got %s\n", buf);
-
-    r = MsiRecordGetInteger(res, 3);
-    ok(r == 1, "Expected 1, 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");
 
-    MsiCloseHandle(res);
+    r = MsiViewClose(hview);
+    ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+    r = MsiCloseHandle(hview);
+    ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
 
-    r = MsiViewFetch(view, &res);
-    ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
+    /* 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");
 
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 1, buf, &size);
+    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(buf, "apple"), "Expected apple, got %s\n", buf);
-
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 2, buf, &size);
+    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(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);
+    ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer);
+    r = MsiCloseHandle(hrec);
+    ok(r == ERROR_SUCCESS, "failed to close record\n");
 
-    query = "SELECT * FROM `Table` WHERE `Two`<>? AND `Three`>? ORDER BY `Three`";
-    r = MsiDatabaseOpenView(hdb, query, &view);
+    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);
-    r = MsiViewExecute(view, rec);
+    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(view, &res);
+    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);
-
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 1, buf, &size);
+    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(buf, "apple"), "Expected apple, got %s\n", buf);
+    ok(!lstrcmp(buffer, "baz"), "Expected baz, got %s\n", buffer);
+    r = MsiCloseHandle(hrec);
+    ok(r == ERROR_SUCCESS, "failed to close record\n");
 
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 2, buf, &size);
+    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(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(!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");
 
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 1, buf, &size);
+    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(buf, "banana"), "Expected banana, got %s\n", buf);
-
-    size = MAX_PATH;
-    r = MsiRecordGetString(res, 2, buf, &size);
+    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(buf, "three"), "Expected three, got %s\n", buf);
-
-    r = MsiRecordGetInteger(res, 3);
-    ok(r == 3, "Expected 3, 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");
 
-    MsiCloseHandle(res);
+    r = MsiViewClose(hview);
+    ok(r == ERROR_SUCCESS, "MsiViewClose failed\n");
+    r = MsiCloseHandle(hview);
+    ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
 
-    r = MsiViewFetch(view, &res);
-    ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r);
+    r = MsiCloseHandle(hdb);
+    ok(r == ERROR_SUCCESS, "MsiCloseHandle failed\n");
 
-    MsiCloseHandle(rec);
-    MsiViewClose(view);
-    MsiCloseHandle(view);
-    MsiCloseHandle(hdb);
     DeleteFile(msifile);
 }
 
-static void test_viewmodify_update(void)
+static void test_rows_order(void)
 {
-    MSIHANDLE hdb = 0, hview = 0, hrec = 0;
     const char *query;
+    MSIHANDLE hdb = 0, hview = 0, hrec = 0;
     UINT r;
-
-    DeleteFile(msifile);
+    char buffer[100];
+    DWORD sz;
 
     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 = "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");
 
-    query = "INSERT INTO `table` (`A`, `B`) VALUES (1, 2)";
+    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_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;
+    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");
 
@@ -4328,6 +5230,127 @@ static void test_viewmodify_update(void)
     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,
 };
@@ -4752,6 +5775,8 @@ static void enum_stream_names(IStorage *stg)
         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);
@@ -4782,8 +5807,6 @@ static void test_defaultdatabase(void)
     MSIHANDLE hdb;
     IStorage *stg = NULL;
 
-    static const WCHAR msifileW[] = {'w','i','n','e','t','e','s','t','.','m','s','i',0};
-
     DeleteFile(msifile);
 
     r = MsiOpenDatabase(msifile, MSIDBOPEN_CREATE, &hdb);
@@ -6514,30 +7537,75 @@ static void test_dbmerge(void)
     ok(r == ERROR_INVALID_HANDLE,
        "Expected ERROR_INVALID_HANDLE, got %d\n", r);
 
-    /* szTableName is NULL */
-    r = MsiDatabaseMergeA(hdb, href, NULL);
+    /* 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);
 
-    /* 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");
+    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 PRIMARY KEY `A` )";
+    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(72) PRIMARY KEY `A` )";
+    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 types don't match */
+    /* column sting types don't match exactly */
     r = MsiDatabaseMergeA(hdb, href, "MergeErrors");
-    ok(r == ERROR_DATATYPE_MISMATCH,
-       "Expected ERROR_DATATYPE_MISMATCH, got %d\n", r);
+    ok(r == ERROR_SUCCESS,
+       "Expected ERROR_SUCCESS, got %d\n", r);
 
     /* nothing in MergeErrors */
     query = "SELECT * FROM `MergeErrors`";
@@ -6804,6 +7872,92 @@ static void test_dbmerge(void)
     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);
@@ -6899,12 +8053,9 @@ static void test_dbmerge(void)
     size = MAX_PATH;
     ZeroMemory(buf, MAX_PATH);
     r = MsiRecordReadStream(hrec, 2, buf, &size);
-    todo_wine
-    {
-        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);
-    }
+    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);
 
@@ -6914,6 +8065,70 @@ static void test_dbmerge(void)
     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);
@@ -6922,6 +8137,80 @@ static void test_dbmerge(void)
     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);
+}
+
 UINT ordervals[6][3] =
 {
     { MSI_NULL_INTEGER, 12, 13 },
@@ -7025,6 +8314,43 @@ static void test_insertorder(void)
     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);
@@ -7528,8 +8854,11 @@ START_TEST(db)
     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();
@@ -7539,8 +8868,11 @@ START_TEST(db)
     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();
@@ -7557,6 +8889,8 @@ START_TEST(db)
     test_dbtopackage();
     test_droptable();
     test_dbmerge();
+    test_select_with_tablenames();
     test_insertorder();
     test_columnorder();
+    test_suminfo_import();
 }