msi: Add initial implementation of MsiDatabaseMerge, with tests.

This commit is contained in:
James Hawkins 2008-10-08 22:42:33 -05:00 committed by Alexandre Julliard
parent c9ec69db02
commit f03889ae82
4 changed files with 1014 additions and 5 deletions

View File

@ -35,6 +35,7 @@
#include "objidl.h"
#include "objbase.h"
#include "msiserver.h"
#include "query.h"
#include "initguid.h"
@ -999,13 +1000,505 @@ UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
return r;
}
typedef struct _tagMERGETABLE
{
struct list entry;
struct list rows;
LPWSTR name;
DWORD numconflicts;
} MERGETABLE;
typedef struct _tagMERGEROW
{
struct list entry;
MSIRECORD *data;
} MERGEROW;
typedef struct _tagMERGEDATA
{
MSIDATABASE *db;
MSIDATABASE *merge;
MERGETABLE *curtable;
MSIQUERY *curview;
struct list *tabledata;
} MERGEDATA;
static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
{
MSIRECORD *dbrec, *mergerec;
UINT r, i, count;
r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
if (r != ERROR_SUCCESS)
return r;
r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
if (r != ERROR_SUCCESS)
return r;
count = MSI_RecordGetFieldCount(dbrec);
for (i = 1; i <= count; i++)
{
if (!MSI_RecordGetString(mergerec, i))
break;
if (lstrcmpW(MSI_RecordGetString(dbrec, i),
MSI_RecordGetString(mergerec, i)))
{
r = ERROR_DATATYPE_MISMATCH;
goto done;
}
}
msiobj_release(&dbrec->hdr);
msiobj_release(&mergerec->hdr);
dbrec = mergerec = NULL;
r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
if (r != ERROR_SUCCESS)
return r;
r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
if (r != ERROR_SUCCESS)
return r;
count = MSI_RecordGetFieldCount(dbrec);
for (i = 1; i <= count; i++)
{
if (!MSI_RecordGetString(mergerec, i))
break;
if (lstrcmpW(MSI_RecordGetString(dbrec, i),
MSI_RecordGetString(mergerec, i)))
{
r = ERROR_DATATYPE_MISMATCH;
break;
}
}
done:
msiobj_release(&dbrec->hdr);
msiobj_release(&mergerec->hdr);
return r;
}
static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
LPCWSTR table)
{
MSIRECORD *dbrec, *mergerec = NULL;
UINT r, i, count;
r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
if (r != ERROR_SUCCESS)
return r;
r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
if (r != ERROR_SUCCESS)
goto done;
count = MSI_RecordGetFieldCount(dbrec);
if (count != MSI_RecordGetFieldCount(mergerec))
{
r = ERROR_DATATYPE_MISMATCH;
goto done;
}
for (i = 1; i <= count; i++)
{
if (lstrcmpW(MSI_RecordGetString(dbrec, i),
MSI_RecordGetString(mergerec, i)))
{
r = ERROR_DATATYPE_MISMATCH;
goto done;
}
}
done:
msiobj_release(&dbrec->hdr);
msiobj_release(&mergerec->hdr);
return r;
}
static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
{
MSIRECORD *colnames;
LPWSTR str;
UINT r, i = 0;
int cmp;
r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
if (r != ERROR_SUCCESS)
return NULL;
do
{
str = msi_dup_record_field(colnames, ++i);
cmp = lstrcmpW(key, str);
msi_free(str);
} while (cmp);
msiobj_release(&colnames->hdr);
return msi_dup_record_field(rec, i);
}
static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
LPWSTR table, MSIRECORD *rec)
{
LPWSTR query = NULL, clause = NULL;
LPWSTR ptr = NULL, val;
LPCWSTR setptr;
DWORD size = 1, oldsize;
LPCWSTR key;
MSIRECORD *keys;
UINT r, i, count;
static const WCHAR keyset[] = {
'`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
static const WCHAR lastkeyset[] = {
'`','%','s','`',' ','=',' ','%','s',' ',0};
static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
'F','R','O','M',' ','`','%','s','`',' ',
'W','H','E','R','E',' ','%','s',0};
r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
if (r != ERROR_SUCCESS)
return NULL;
clause = msi_alloc_zero(size * sizeof(WCHAR));
if (!clause)
goto done;
ptr = clause;
count = MSI_RecordGetFieldCount(keys);
for (i = 1; i <= count; i++)
{
key = MSI_RecordGetString(keys, i);
val = get_key_value(view, key, rec);
if (i == count)
setptr = lastkeyset;
else
setptr = keyset;
oldsize = size;
size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
clause = msi_realloc(clause, size * sizeof (WCHAR));
if (!clause)
{
msi_free(val);
goto done;
}
ptr = clause + oldsize - 1;
sprintfW(ptr, setptr, key, val);
msi_free(val);
}
size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
query = msi_alloc(size * sizeof(WCHAR));
if (!query)
goto done;
sprintfW(query, fmt, table, clause);
done:
msi_free(clause);
msiobj_release(&keys->hdr);
return query;
}
static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
{
MERGEDATA *data = (MERGEDATA *)param;
MERGETABLE *table = data->curtable;
MERGEROW *mergerow;
MSIQUERY *dbview;
MSIRECORD *row;
LPWSTR query;
UINT r;
query = create_diff_row_query(data->merge, data->curview, table->name, rec);
if (!query)
return ERROR_OUTOFMEMORY;
r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
if (r != ERROR_SUCCESS)
goto done;
r = MSI_ViewExecute(dbview, NULL);
if (r != ERROR_SUCCESS)
goto done;
r = MSI_ViewFetch(dbview, &row);
if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
{
table->numconflicts++;
goto done;
}
else if (r != ERROR_NO_MORE_ITEMS)
goto done;
mergerow = msi_alloc(sizeof(MERGEROW));
if (!mergerow)
{
r = ERROR_OUTOFMEMORY;
goto done;
}
mergerow->data = MSI_CloneRecord(rec);
if (!mergerow->data)
{
r = ERROR_OUTOFMEMORY;
msi_free(mergerow);
goto done;
}
list_add_tail(&table->rows, &mergerow->entry);
done:
msi_free(query);
msiobj_release(&row->hdr);
msiobj_release(&dbview->hdr);
return r;
}
static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
{
MERGEDATA *data = (MERGEDATA *)param;
MERGETABLE *table;
MSIQUERY *dbview = NULL;
MSIQUERY *mergeview = NULL;
LPCWSTR name;
LPWSTR query;
DWORD size;
UINT r;
static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
'F','R','O','M',' ','`','%','s','`',0};
name = MSI_RecordGetString(rec, 1);
size = lstrlenW(fmt) + lstrlenW(name) - 1;
query = msi_alloc(size * sizeof(WCHAR));
if (!query)
return ERROR_OUTOFMEMORY;
sprintfW(query, fmt, name);
r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
if (r != ERROR_SUCCESS)
goto done;
r = MSI_DatabaseOpenViewW(data->merge, query, &mergeview);
if (r != ERROR_SUCCESS)
goto done;
r = merge_verify_colnames(dbview, mergeview);
if (r != ERROR_SUCCESS)
goto done;
r = merge_verify_primary_keys(data->db, data->merge, name);
if (r != ERROR_SUCCESS)
goto done;
table = msi_alloc(sizeof(MERGETABLE));
if (!table)
{
r = ERROR_OUTOFMEMORY;
goto done;
}
list_init(&table->rows);
table->name = strdupW(name);
table->numconflicts = 0;
data->curtable = table;
data->curview = mergeview;
r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
if (r != ERROR_SUCCESS)
{
msi_free(table->name);
msi_free(table);
goto done;
}
list_add_tail(data->tabledata, &table->entry);
done:
msi_free(query);
msiobj_release(&dbview->hdr);
msiobj_release(&mergeview->hdr);
return r;
}
static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
struct list *tabledata)
{
UINT r;
MSIQUERY *view;
MERGEDATA data;
static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
r = MSI_DatabaseOpenViewW(merge, query, &view);
if (r != ERROR_SUCCESS)
return r;
data.db = db;
data.merge = merge;
data.tabledata = tabledata;
r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
msiobj_release(&view->hdr);
return r;
}
static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
{
UINT r;
MERGEROW *row;
MSIVIEW *tv;
LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
{
r = TABLE_CreateView(db, table->name, &tv);
if (r != ERROR_SUCCESS)
return r;
r = tv->ops->insert_row(tv, row->data, FALSE);
tv->ops->delete(tv);
if (r != ERROR_SUCCESS)
return r;
}
return ERROR_SUCCESS;
}
static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
LPWSTR table, DWORD numconflicts)
{
UINT r;
MSIQUERY *view;
static const WCHAR create[] = {
'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
'`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
static const WCHAR insert[] = {
'I','N','S','E','R','T',' ','I','N','T','O',' ',
'`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
'`','N','u','m','R','o','w','M','e','r','g','e',
'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
' ','(','\'','%','s','\'',',',' ','%','d',')',0};
if (!TABLE_Exists(db, error))
{
r = MSI_OpenQuery(db, &view, create, error);
if (r != ERROR_SUCCESS)
return r;
r = MSI_ViewExecute(view, NULL);
msiobj_release(&view->hdr);
if (r != ERROR_SUCCESS)
return r;
}
r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
if (r != ERROR_SUCCESS)
return r;
r = MSI_ViewExecute(view, NULL);
msiobj_release(&view->hdr);
return r;
}
static void merge_free_rows(MERGETABLE *table)
{
struct list *item, *cursor;
LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
{
MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
list_remove(&row->entry);
merge_free_rows(table);
msiobj_release(&row->data->hdr);
msi_free(row);
}
}
UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
LPCWSTR szTableName)
{
FIXME("(%ld, %ld, %s): stub!\n", hDatabase, hDatabaseMerge,
struct list tabledata = LIST_INIT(tabledata);
struct list *item, *cursor;
MSIDATABASE *db, *merge;
MERGETABLE *table;
BOOL conflicts;
UINT r;
TRACE("(%ld, %ld, %s)\n", hDatabase, hDatabaseMerge,
debugstr_w(szTableName));
return ERROR_CALL_NOT_IMPLEMENTED;
if (szTableName && !*szTableName)
return ERROR_INVALID_TABLE;
db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
if (!db || !merge)
{
r = ERROR_INVALID_HANDLE;
goto done;
}
r = gather_merge_data(db, merge, &tabledata);
if (r != ERROR_SUCCESS)
goto done;
conflicts = FALSE;
LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
{
if (table->numconflicts)
{
conflicts = TRUE;
r = update_merge_errors(db, szTableName, table->name,
table->numconflicts);
if (r != ERROR_SUCCESS)
break;
}
else
{
r = merge_table(db, table);
if (r != ERROR_SUCCESS)
break;
}
}
LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
{
MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
list_remove(&table->entry);
merge_free_rows(table);
msi_free(table->name);
msi_free(table);
}
if (conflicts)
r = ERROR_FUNCTION_FAILED;
done:
msiobj_release(&db->hdr);
msiobj_release(&merge->hdr);
return r;
}
MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )

View File

@ -703,6 +703,8 @@ extern UINT MSI_RecordSetStream( MSIRECORD *, UINT, IStream * );
extern UINT MSI_RecordDataSize( MSIRECORD *, UINT );
extern UINT MSI_RecordStreamToFile( MSIRECORD *, UINT, LPCWSTR );
extern UINT MSI_RecordCopyField( MSIRECORD *, UINT, MSIRECORD *, UINT );
extern MSIRECORD *MSI_CloneRecord( MSIRECORD * );
extern BOOL MSI_RecordsAreEqual( MSIRECORD *, MSIRECORD * );
/* stream internals */
extern UINT get_raw_stream( MSIHANDLE hdb, LPCWSTR stname, IStream **stm );

View File

@ -903,3 +903,74 @@ UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
return r;
}
MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
{
MSIRECORD *clone;
UINT r, i, count;
count = MSI_RecordGetFieldCount(rec);
clone = MSI_CreateRecord(count);
if (!clone)
return NULL;
for (i = 0; i <= count; i++)
{
if (rec->fields[i].type == MSIFIELD_STREAM)
{
if (FAILED(IStream_Clone(rec->fields[i].u.stream,
&clone->fields[i].u.stream)))
{
msiobj_release(&clone->hdr);
return NULL;
}
}
else
{
r = MSI_RecordCopyField(rec, i, clone, i);
if (r != ERROR_SUCCESS)
{
msiobj_release(&clone->hdr);
return NULL;
}
}
}
return clone;
}
BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
{
UINT i;
if (a->count != b->count)
return FALSE;
for (i = 0; i <= a->count; i++)
{
if (a->fields[i].type != b->fields[i].type)
return FALSE;
switch (a->fields[i].type)
{
case MSIFIELD_NULL:
break;
case MSIFIELD_INT:
if (a->fields[i].u.iVal != b->fields[i].u.iVal)
return FALSE;
break;
case MSIFIELD_WSTR:
if (lstrcmpW(a->fields[i].u.szwVal, b->fields[i].u.szwVal))
return FALSE;
break;
case MSIFIELD_STREAM:
default:
return FALSE;
}
}
return TRUE;
}

View File

@ -144,6 +144,9 @@ 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)
@ -1382,18 +1385,29 @@ static void test_longstrings(void)
DeleteFile(msifile);
}
static void create_file(const CHAR *name)
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);
ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
WriteFile(file, name, strlen(name), &written, 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;
@ -6532,6 +6546,434 @@ static void test_droptable(void)
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` 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(!lstrcmpA(buf, "Table"), "Expected \"Table\", got \"%s\"\n", buf);
size = MAX_PATH;
r = MsiRecordGetString(hrec, 2, buf, &size);
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(!lstrcmpA(buf, "s255"), "Expected \"s255\", got \"%s\"\n", buf);
size = MAX_PATH;
r = MsiRecordGetString(hrec, 2, buf, &size);
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);
create_file_data("codepage.idt", "\r\n\r\n28603\t_ForceCodepage\r\n", 0);
GetCurrentDirectoryA(MAX_PATH, buf);
r = MsiDatabaseImportA(hdb, buf, "codepage.idt");
todo_wine 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);
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);
}
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);
MsiCloseHandle(hdb);
MsiCloseHandle(href);
DeleteFileA(msifile);
DeleteFileA("refdb.msi");
DeleteFileA("codepage.idt");
}
START_TEST(db)
{
test_msidatabase();
@ -6572,4 +7014,5 @@ START_TEST(db)
test_storages_table();
test_dbtopackage();
test_droptable();
test_dbmerge();
}