msi: Add support for merging tables that are not in target database.
This commit is contained in:
parent
97200b0b31
commit
ff5b29e5eb
|
@ -1056,6 +1056,12 @@ typedef struct _tagMERGETABLE
|
||||||
struct list rows;
|
struct list rows;
|
||||||
LPWSTR name;
|
LPWSTR name;
|
||||||
DWORD numconflicts;
|
DWORD numconflicts;
|
||||||
|
LPWSTR *columns;
|
||||||
|
DWORD numcolumns;
|
||||||
|
LPWSTR *types;
|
||||||
|
DWORD numtypes;
|
||||||
|
LPWSTR *labels;
|
||||||
|
DWORD numlabels;
|
||||||
} MERGETABLE;
|
} MERGETABLE;
|
||||||
|
|
||||||
typedef struct _tagMERGEROW
|
typedef struct _tagMERGEROW
|
||||||
|
@ -1302,29 +1308,32 @@ static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
|
||||||
MERGEROW *mergerow;
|
MERGEROW *mergerow;
|
||||||
MSIQUERY *dbview = NULL;
|
MSIQUERY *dbview = NULL;
|
||||||
MSIRECORD *row = NULL;
|
MSIRECORD *row = NULL;
|
||||||
LPWSTR query;
|
LPWSTR query = NULL;
|
||||||
UINT r;
|
UINT r = ERROR_SUCCESS;
|
||||||
|
|
||||||
query = create_diff_row_query(data->merge, data->curview, table->name, rec);
|
if (TABLE_Exists(data->db, table->name))
|
||||||
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++;
|
query = create_diff_row_query(data->merge, data->curview, table->name, rec);
|
||||||
goto done;
|
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;
|
||||||
}
|
}
|
||||||
else if (r != ERROR_NO_MORE_ITEMS)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
mergerow = msi_alloc(sizeof(MERGEROW));
|
mergerow = msi_alloc(sizeof(MERGEROW));
|
||||||
if (!mergerow)
|
if (!mergerow)
|
||||||
|
@ -1350,11 +1359,188 @@ done:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
|
||||||
|
{
|
||||||
|
UINT r, i, count;
|
||||||
|
MSIRECORD *prec = NULL;
|
||||||
|
|
||||||
|
r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
count = MSI_RecordGetFieldCount(prec);
|
||||||
|
*numlabels = count + 1;
|
||||||
|
*labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
|
||||||
|
if (!labels)
|
||||||
|
{
|
||||||
|
r = ERROR_OUTOFMEMORY;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*labels)[0] = strdupW(table);
|
||||||
|
for (i=1; i<=count; i++ )
|
||||||
|
{
|
||||||
|
(*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
msiobj_release( &prec->hdr );
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
|
||||||
|
{
|
||||||
|
UINT r, i, count;
|
||||||
|
MSIRECORD *prec = NULL;
|
||||||
|
|
||||||
|
r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
count = MSI_RecordGetFieldCount(prec);
|
||||||
|
*columns = msi_alloc(count*sizeof(LPWSTR));
|
||||||
|
if (!columns)
|
||||||
|
{
|
||||||
|
r = ERROR_OUTOFMEMORY;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=1; i<=count; i++ )
|
||||||
|
{
|
||||||
|
(*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
*numcolumns = count;
|
||||||
|
|
||||||
|
end:
|
||||||
|
msiobj_release( &prec->hdr );
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
|
||||||
|
{
|
||||||
|
UINT r, i, count;
|
||||||
|
MSIRECORD *prec = NULL;
|
||||||
|
|
||||||
|
r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
count = MSI_RecordGetFieldCount(prec);
|
||||||
|
*types = msi_alloc(count*sizeof(LPWSTR));
|
||||||
|
if (!types)
|
||||||
|
{
|
||||||
|
r = ERROR_OUTOFMEMORY;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=1; i<=count; i++ )
|
||||||
|
{
|
||||||
|
(*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
msiobj_release( &prec->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);
|
||||||
|
msiobj_release(&row->data->hdr);
|
||||||
|
msi_free(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_merge_table(MERGETABLE *table)
|
||||||
|
{
|
||||||
|
UINT i;
|
||||||
|
|
||||||
|
if (table->labels != NULL)
|
||||||
|
{
|
||||||
|
for (i = 0; i < table->numlabels; i++)
|
||||||
|
msi_free(table->labels[i]);
|
||||||
|
msi_free(table->labels);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table->columns != NULL)
|
||||||
|
{
|
||||||
|
for (i = 0; i < table->numcolumns; i++)
|
||||||
|
msi_free(table->columns[i]);
|
||||||
|
msi_free(table->columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table->types != NULL)
|
||||||
|
{
|
||||||
|
for (i = 0; i < table->numtypes; i++)
|
||||||
|
msi_free(table->types[i]);
|
||||||
|
msi_free(table->types);
|
||||||
|
}
|
||||||
|
msi_free(table->name);
|
||||||
|
merge_free_rows(table);
|
||||||
|
|
||||||
|
msi_free(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
|
||||||
|
{
|
||||||
|
UINT r;
|
||||||
|
MERGETABLE *table;
|
||||||
|
MSIQUERY *mergeview = NULL;
|
||||||
|
|
||||||
|
static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
|
||||||
|
'F','R','O','M',' ','`','%','s','`',0};
|
||||||
|
|
||||||
|
table = msi_alloc_zero(sizeof(MERGETABLE));
|
||||||
|
if (!table)
|
||||||
|
{
|
||||||
|
*ptable = NULL;
|
||||||
|
return ERROR_OUTOFMEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
r = MSI_OpenQuery(db, &mergeview, query, name);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
list_init(&table->rows);
|
||||||
|
|
||||||
|
table->name = strdupW(name);
|
||||||
|
table->numconflicts = 0;
|
||||||
|
|
||||||
|
msiobj_release(&mergeview->hdr);
|
||||||
|
*ptable = table;
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
|
||||||
|
err:
|
||||||
|
msiobj_release(&mergeview->hdr);
|
||||||
|
free_merge_table(table);
|
||||||
|
*ptable = NULL;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
|
static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
|
||||||
{
|
{
|
||||||
MERGEDATA *data = param;
|
MERGEDATA *data = param;
|
||||||
MERGETABLE *table;
|
MERGETABLE *table;
|
||||||
MSIQUERY *dbview;
|
MSIQUERY *dbview = NULL;
|
||||||
MSIQUERY *mergeview = NULL;
|
MSIQUERY *mergeview = NULL;
|
||||||
LPCWSTR name;
|
LPCWSTR name;
|
||||||
UINT r;
|
UINT r;
|
||||||
|
@ -1364,40 +1550,35 @@ static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
|
||||||
|
|
||||||
name = MSI_RecordGetString(rec, 1);
|
name = MSI_RecordGetString(rec, 1);
|
||||||
|
|
||||||
r = MSI_OpenQuery(data->db, &dbview, query, name);
|
|
||||||
if (r != ERROR_SUCCESS)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
r = MSI_OpenQuery(data->merge, &mergeview, query, name);
|
r = MSI_OpenQuery(data->merge, &mergeview, query, name);
|
||||||
if (r != ERROR_SUCCESS)
|
if (r != ERROR_SUCCESS)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
r = merge_verify_colnames(dbview, mergeview);
|
if (TABLE_Exists(data->db, name))
|
||||||
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;
|
r = MSI_OpenQuery(data->db, &dbview, query, name);
|
||||||
goto done;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_init(&table->rows);
|
r = msi_get_merge_table(data->merge, name, &table);
|
||||||
table->name = strdupW(name);
|
if (r != ERROR_SUCCESS)
|
||||||
table->numconflicts = 0;
|
goto done;
|
||||||
|
|
||||||
data->curtable = table;
|
data->curtable = table;
|
||||||
data->curview = mergeview;
|
data->curview = mergeview;
|
||||||
|
|
||||||
r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
|
r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
|
||||||
if (r != ERROR_SUCCESS)
|
if (r != ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
msi_free(table->name);
|
free_merge_table(table);
|
||||||
msi_free(table);
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1438,6 +1619,14 @@ static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
|
||||||
MERGEROW *row;
|
MERGEROW *row;
|
||||||
MSIVIEW *tv;
|
MSIVIEW *tv;
|
||||||
|
|
||||||
|
if (!TABLE_Exists(db, table->name))
|
||||||
|
{
|
||||||
|
r = msi_add_table_to_db(db, table->columns, table->types,
|
||||||
|
table->labels, table->numlabels, table->numcolumns);
|
||||||
|
if (r != ERROR_SUCCESS)
|
||||||
|
return ERROR_FUNCTION_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
|
LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
|
||||||
{
|
{
|
||||||
r = TABLE_CreateView(db, table->name, &tv);
|
r = TABLE_CreateView(db, table->name, &tv);
|
||||||
|
@ -1496,21 +1685,6 @@ static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
|
||||||
return r;
|
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,
|
UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
|
||||||
LPCWSTR szTableName)
|
LPCWSTR szTableName)
|
||||||
{
|
{
|
||||||
|
@ -1564,9 +1738,7 @@ UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
|
||||||
MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
|
MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
|
||||||
|
|
||||||
list_remove(&table->entry);
|
list_remove(&table->entry);
|
||||||
merge_free_rows(table);
|
free_merge_table(table);
|
||||||
msi_free(table->name);
|
|
||||||
msi_free(table);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conflicts)
|
if (conflicts)
|
||||||
|
|
|
@ -6962,19 +6962,19 @@ static void test_dbmerge(void)
|
||||||
|
|
||||||
/* table from merged database is not in target database */
|
/* table from merged database is not in target database */
|
||||||
r = MsiDatabaseMergeA(hdb, href, "MergeErrors");
|
r = MsiDatabaseMergeA(hdb, href, "MergeErrors");
|
||||||
todo_wine ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
||||||
|
|
||||||
query = "SELECT * FROM `One`";
|
query = "SELECT * FROM `One`";
|
||||||
r = do_query(hdb, query, &hrec);
|
r = do_query(hdb, query, &hrec);
|
||||||
todo_wine ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
||||||
|
|
||||||
r = MsiRecordGetInteger(hrec, 1);
|
r = MsiRecordGetInteger(hrec, 1);
|
||||||
todo_wine ok(r == 1, "Expected 1, got %d\n", r);
|
ok(r == 1, "Expected 1, got %d\n", r);
|
||||||
|
|
||||||
size = MAX_PATH;
|
size = MAX_PATH;
|
||||||
r = MsiRecordGetStringA(hrec, 2, buf, &size);
|
r = MsiRecordGetStringA(hrec, 2, buf, &size);
|
||||||
todo_wine ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
||||||
todo_wine ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf);
|
ok(!lstrcmpA(buf, "hi"), "Expected \"hi\", got \"%s\"\n", buf);
|
||||||
|
|
||||||
MsiCloseHandle(hrec);
|
MsiCloseHandle(hrec);
|
||||||
|
|
||||||
|
@ -6986,7 +6986,7 @@ static void test_dbmerge(void)
|
||||||
|
|
||||||
query = "DROP TABLE `One`";
|
query = "DROP TABLE `One`";
|
||||||
r = run_query(hdb, 0, query);
|
r = run_query(hdb, 0, query);
|
||||||
todo_wine ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r);
|
||||||
|
|
||||||
query = "DROP TABLE `One`";
|
query = "DROP TABLE `One`";
|
||||||
r = run_query(href, 0, query);
|
r = run_query(href, 0, query);
|
||||||
|
|
Loading…
Reference in New Issue