msi: Add initial implementation of MsiDatabaseMerge, with tests.
This commit is contained in:
parent
c9ec69db02
commit
f03889ae82
|
@ -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 )
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue