From b3c9875cf770aee477c2a4bc92cc0c27b9dee5bc Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sun, 1 Mar 2009 21:41:22 -0800 Subject: [PATCH] msi: Correctly order transposed column values in the INSERT query. --- dlls/msi/database.c | 2 +- dlls/msi/insert.c | 127 ++++++++++++++++++++++++++++++++++++++++++-- dlls/msi/msipriv.h | 2 +- dlls/msi/select.c | 4 +- dlls/msi/storages.c | 13 +++-- dlls/msi/streams.c | 13 +++-- dlls/msi/table.c | 49 +++++++++++++---- dlls/msi/tests/db.c | 110 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 294 insertions(+), 26 deletions(-) diff --git a/dlls/msi/database.c b/dlls/msi/database.c index 83b1f2ccfbb..8a9b0d7f5b4 100644 --- a/dlls/msi/database.c +++ b/dlls/msi/database.c @@ -1349,7 +1349,7 @@ static UINT merge_table(MSIDATABASE *db, MERGETABLE *table) if (r != ERROR_SUCCESS) return r; - r = tv->ops->insert_row(tv, row->data, FALSE); + r = tv->ops->insert_row(tv, row->data, -1, FALSE); tv->ops->delete(tv); if (r != ERROR_SUCCESS) diff --git a/dlls/msi/insert.c b/dlls/msi/insert.c index 0907126500f..85de99a8ea3 100644 --- a/dlls/msi/insert.c +++ b/dlls/msi/insert.c @@ -41,6 +41,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(msidb); typedef struct tagMSIINSERTVIEW { MSIVIEW view; + MSIVIEW *table; MSIDATABASE *db; BOOL bIsTemp; MSIVIEW *sv; @@ -102,10 +103,121 @@ err: return NULL; } +/* checks to see if the column order specified in the INSERT query + * matches the column order of the table + */ +static BOOL msi_columns_in_order(MSIINSERTVIEW *iv, UINT col_count) +{ + LPWSTR a, b = NULL; + UINT i; + int res; + + for (i = 1; i <= col_count; i++) + { + iv->sv->ops->get_column_info(iv->sv, i, &a, NULL); + iv->table->ops->get_column_info(iv->table, i, &b, NULL); + + res = lstrcmpW(a, b); + msi_free(a); + msi_free(b); + + if (res != 0) + return FALSE; + } + + return TRUE; +} + +/* rearranges the data in the record to be inserted based on column order, + * and pads the record for any missing columns in the INSERT query + */ +static UINT msi_arrange_record(MSIINSERTVIEW *iv, MSIRECORD **values) +{ + MSIRECORD *padded; + UINT col_count, val_count; + UINT r, i, colidx; + LPWSTR a, b = NULL; + int res; + + r = iv->table->ops->get_dimensions(iv->table, NULL, &col_count); + if (r != ERROR_SUCCESS) + return r; + + val_count = MSI_RecordGetFieldCount(*values); + + /* check to see if the columns are arranged already + * to avoid unnecessary copying + */ + if (col_count == val_count && msi_columns_in_order(iv, col_count)) + return ERROR_SUCCESS; + + padded = MSI_CreateRecord(col_count); + if (!padded) + return ERROR_OUTOFMEMORY; + + for (colidx = 1; colidx <= val_count; colidx++) + { + r = iv->sv->ops->get_column_info(iv->sv, colidx, &a, NULL); + if (r != ERROR_SUCCESS) + goto err; + + for (i = 1; i <= col_count; i++) + { + r = iv->table->ops->get_column_info(iv->table, i, &b, NULL); + if (r != ERROR_SUCCESS) + goto err; + + res = lstrcmpW(a, b); + msi_free(b); + + if (res == 0) + { + MSI_RecordCopyField(*values, colidx, padded, i); + break; + } + } + + msi_free(a); + } + + msiobj_release(&(*values)->hdr); + *values = padded; + + return ERROR_SUCCESS; + +err: + msiobj_release(&padded->hdr); + return r; +} + +static BOOL row_has_null_primary_keys(MSIINSERTVIEW *iv, MSIRECORD *row) +{ + UINT r, i, col_count, type; + + r = iv->table->ops->get_dimensions( iv->table, NULL, &col_count ); + if (r != ERROR_SUCCESS) + return FALSE; + + for (i = 1; i <= col_count; i++) + { + r = iv->table->ops->get_column_info(iv->table, i, NULL, &type); + if (r != ERROR_SUCCESS) + return FALSE; + + if (!(type & MSITYPE_KEY)) + continue; + + if (MSI_RecordIsNull(row, i)) + return TRUE; + } + + return FALSE; +} + static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) { MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; - UINT r, col_count = 0; + UINT r, row = -1, col_count = 0; MSIVIEW *sv; MSIRECORD *values = NULL; @@ -116,7 +228,7 @@ static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) return ERROR_FUNCTION_FAILED; r = sv->ops->execute( sv, 0 ); - TRACE("tv execute returned %x\n", r); + TRACE("sv execute returned %x\n", r); if( r ) return r; @@ -132,7 +244,15 @@ static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) if( !values ) goto err; - r = sv->ops->insert_row( sv, values, iv->bIsTemp ); + r = msi_arrange_record( iv, &values ); + if( r != ERROR_SUCCESS ) + goto err; + + /* rows with NULL primary keys are inserted at the beginning of the table */ + if( row_has_null_primary_keys( iv, values ) ) + row = 0; + + r = iv->table->ops->insert_row( iv->table, values, row, iv->bIsTemp ); err: if( values ) @@ -282,6 +402,7 @@ UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table, /* fill the structure */ iv->view.ops = &insert_ops; msiobj_addref( &db->hdr ); + iv->table = tv; iv->db = db; iv->vals = values; iv->bIsTemp = temp; diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 6b15fedb401..307c9f97d75 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -199,7 +199,7 @@ typedef struct tagMSIVIEWOPS /* * Inserts a new row into the database from the records contents */ - UINT (*insert_row)( struct tagMSIVIEW *view, MSIRECORD *record, BOOL temporary ); + UINT (*insert_row)( struct tagMSIVIEW *view, MSIRECORD *record, UINT row, BOOL temporary ); /* * Deletes a row from the database diff --git a/dlls/msi/select.c b/dlls/msi/select.c index 30fabce078a..a6ffe7c98e6 100644 --- a/dlls/msi/select.c +++ b/dlls/msi/select.c @@ -136,7 +136,7 @@ static UINT SELECT_set_row( struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, U return r; } -static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record, BOOL temporary ) +static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record, UINT row, BOOL temporary ) { MSISELECTVIEW *sv = (MSISELECTVIEW*)view; UINT i, table_cols, r; @@ -161,7 +161,7 @@ static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record, BOOL goto fail; } - r = sv->table->ops->insert_row( sv->table, outrec, temporary ); + r = sv->table->ops->insert_row( sv->table, outrec, row, temporary ); fail: msiobj_release( &outrec->hdr ); diff --git a/dlls/msi/storages.c b/dlls/msi/storages.c index 302906b8466..9f294ef8516 100644 --- a/dlls/msi/storages.c +++ b/dlls/msi/storages.c @@ -243,14 +243,19 @@ done: return r; } -static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, BOOL temporary) +static UINT STORAGES_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary) { MSISTORAGESVIEW *sv = (MSISTORAGESVIEW *)view; if (!storages_set_table_size(sv, ++sv->num_rows)) return ERROR_FUNCTION_FAILED; - return STORAGES_set_row(view, sv->num_rows - 1, rec, 0); + if (row == -1) + row = sv->num_rows - 1; + + /* FIXME have to readjust rows */ + + return STORAGES_set_row(view, row, rec, 0); } static UINT STORAGES_delete_row(struct tagMSIVIEW *view, UINT row) @@ -361,7 +366,7 @@ static UINT storages_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec) if (r == ERROR_SUCCESS) return storages_modify_update(view, rec); - return STORAGES_insert_row(view, rec, FALSE); + return STORAGES_insert_row(view, rec, -1, FALSE); } static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) @@ -377,7 +382,7 @@ static UINT STORAGES_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIR break; case MSIMODIFY_INSERT: - r = STORAGES_insert_row(view, rec, FALSE); + r = STORAGES_insert_row(view, rec, -1, FALSE); break; case MSIMODIFY_UPDATE: diff --git a/dlls/msi/streams.c b/dlls/msi/streams.c index e263105798e..4e60bd60499 100644 --- a/dlls/msi/streams.c +++ b/dlls/msi/streams.c @@ -209,14 +209,19 @@ done: return r; } -static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, BOOL temporary) +static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary) { MSISTREAMSVIEW *sv = (MSISTREAMSVIEW *)view; if (!streams_set_table_size(sv, ++sv->num_rows)) return ERROR_FUNCTION_FAILED; - return STREAMS_set_row(view, sv->num_rows - 1, rec, 0); + if (row == -1) + row = sv->num_rows - 1; + + /* FIXME have to readjust rows */ + + return STREAMS_set_row(view, row, rec, 0); } static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row) @@ -327,7 +332,7 @@ static UINT streams_modify_assign(struct tagMSIVIEW *view, MSIRECORD *rec) if (r == ERROR_SUCCESS) return streams_modify_update(view, rec); - return STREAMS_insert_row(view, rec, FALSE); + return STREAMS_insert_row(view, rec, -1, FALSE); } static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row) @@ -343,7 +348,7 @@ static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRE break; case MSIMODIFY_INSERT: - r = STREAMS_insert_row(view, rec, FALSE); + r = STREAMS_insert_row(view, rec, -1, FALSE); break; case MSIMODIFY_UPDATE: diff --git a/dlls/msi/table.c b/dlls/msi/table.c index 369dde481a4..1d822e27f08 100644 --- a/dlls/msi/table.c +++ b/dlls/msi/table.c @@ -683,7 +683,7 @@ UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info, if( r ) goto err; - r = tv->ops->insert_row( tv, rec, persistent == MSICONDITION_FALSE ); + r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE ); TRACE("insert_row returned %x\n", r); if( r ) goto err; @@ -733,7 +733,7 @@ UINT msi_create_table( MSIDATABASE *db, LPCWSTR name, column_info *col_info, if( r ) goto err; - r = tv->ops->insert_row( tv, rec, FALSE ); + r = tv->ops->insert_row( tv, rec, -1, FALSE ); if( r ) goto err; @@ -1372,13 +1372,15 @@ static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL tempo { row_count = &tv->table->nonpersistent_row_count; data_ptr = &tv->table->nonpersistent_data; - *num = tv->table->row_count + tv->table->nonpersistent_row_count; + if (*num == -1) + *num = tv->table->row_count + tv->table->nonpersistent_row_count; } else { row_count = &tv->table->row_count; data_ptr = &tv->table->data; - *num = tv->table->row_count; + if (*num == -1) + *num = tv->table->row_count; } sz = (*row_count + 1) * sizeof (BYTE*); @@ -1497,10 +1499,11 @@ static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec ) return ERROR_SUCCESS; } -static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, BOOL temporary ) +static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary ) { MSITABLEVIEW *tv = (MSITABLEVIEW*)view; - UINT r, row = -1; + UINT i, r, idx, size; + BYTE **data; TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" ); @@ -1514,6 +1517,27 @@ static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, BOOL temp if( r != ERROR_SUCCESS ) return r; + idx = row; + if( temporary ) + { + data = tv->table->nonpersistent_data; + size = tv->table->nonpersistent_row_count; + idx -= tv->table->row_count; + } + else + { + data = tv->table->data; + size = tv->table->row_count; + } + + /* shift the rows to make room for the new row */ + if( idx != size - 1 ) + { + for (i = 1; i < size - idx; i++) + memmove(&(data[size - i][0]), + &(data[size - i - 1][0]), tv->row_size); + } + return TABLE_set_row( view, row, rec, (1<num_cols) - 1 ); } @@ -1641,14 +1665,14 @@ static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, r = table_validate_new( tv, rec ); if (r != ERROR_SUCCESS) break; - r = TABLE_insert_row( view, rec, FALSE ); + r = TABLE_insert_row( view, rec, -1, FALSE ); break; case MSIMODIFY_INSERT_TEMPORARY: r = table_validate_new( tv, rec ); if (r != ERROR_SUCCESS) break; - r = TABLE_insert_row( view, rec, TRUE ); + r = TABLE_insert_row( view, rec, -1, TRUE ); break; case MSIMODIFY_REFRESH: @@ -1880,7 +1904,7 @@ static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number MSI_RecordSetStringW(rec, 3, column); MSI_RecordSetInteger(rec, 4, type); - r = TABLE_insert_row(&tv->view, rec, FALSE); + r = TABLE_insert_row(&tv->view, rec, -1, FALSE); if (r != ERROR_SUCCESS) goto done; @@ -2354,7 +2378,10 @@ static UINT* msi_record_to_row( const MSITABLEVIEW *tv, MSIRECORD *rec ) else { data[i] = MSI_RecordGetInteger( rec, i+1 ); - if ((tv->columns[i].type&0xff) == 2) + + if (data[i] == MSI_NULL_INTEGER) + data[i] = 0; + else if ((tv->columns[i].type&0xff) == 2) data[i] += 0x8000; else data[i] += 0x80000000; @@ -2548,7 +2575,7 @@ static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, } } - r = TABLE_insert_row( &tv->view, rec, FALSE ); + r = TABLE_insert_row( &tv->view, rec, -1, FALSE ); if (r != ERROR_SUCCESS) ERR("insert row failed\n"); diff --git a/dlls/msi/tests/db.c b/dlls/msi/tests/db.c index e0694b23d17..b9f8a589b86 100644 --- a/dlls/msi/tests/db.c +++ b/dlls/msi/tests/db.c @@ -6927,6 +6927,115 @@ static void test_dbmerge(void) DeleteFileA("binary.dat"); } +UINT ordervals[6][3] = +{ + { MSI_NULL_INTEGER, 12, 13 }, + { 1, 2, 3 }, + { 6, 4, 5 }, + { 8, 9, 7 }, + { 10, 11, MSI_NULL_INTEGER }, + { 14, MSI_NULL_INTEGER, 15 } +}; + +static void test_insertorder(void) +{ + MSIHANDLE hdb, view, rec; + LPCSTR query; + UINT r; + int i; + + hdb = create_db(); + ok(hdb, "failed to create db\n"); + + query = "CREATE TABLE `T` ( `A` SHORT, `B` SHORT, `C` SHORT PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `A`, `B`, `C` ) VALUES ( 1, 2, 3 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `B`, `C`, `A` ) VALUES ( 4, 5, 6 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `C`, `A`, `B` ) VALUES ( 7, 8, 9 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `A`, `B` ) VALUES ( 10, 11 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` ( `B`, `C` ) VALUES ( 12, 13 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + /* fails because the primary key already + * has an MSI_NULL_INTEGER value set above + */ + query = "INSERT INTO `T` ( `C` ) VALUES ( 14 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + /* replicate the error where primary key is set twice */ + query = "INSERT INTO `T` ( `A`, `C` ) VALUES ( 1, 14 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_FUNCTION_FAILED, + "Expected ERROR_FUNCTION_FAILED, got %d\n", r); + + query = "INSERT INTO `T` ( `A`, `C` ) VALUES ( 14, 15 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `T` VALUES ( 16 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `T` VALUES ( 17, 18 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "INSERT INTO `T` VALUES ( 19, 20, 21 )"; + r = run_query(hdb, 0, query); + ok(r == ERROR_BAD_QUERY_SYNTAX, + "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + + query = "SELECT * FROM `T`"; + r = MsiDatabaseOpenView(hdb, query, &view); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + r = MsiViewExecute(view, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + for (i = 0; i < 6; i++) + { + r = MsiViewFetch(view, &rec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiRecordGetInteger(rec, 1); + ok(r == ordervals[i][0], "Expected %d, got %d\n", ordervals[i][0], r); + + r = MsiRecordGetInteger(rec, 2); + ok(r == ordervals[i][1], "Expected %d, got %d\n", ordervals[i][1], r); + + r = MsiRecordGetInteger(rec, 3); + ok(r == ordervals[i][2], "Expected %d, got %d\n", ordervals[i][2], r); + + MsiCloseHandle(rec); + } + + r = MsiViewFetch(view, &rec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(view); + MsiCloseHandle(view); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + START_TEST(db) { test_msidatabase(); @@ -6968,4 +7077,5 @@ START_TEST(db) test_dbtopackage(); test_droptable(); test_dbmerge(); + test_insertorder(); }