Commit b3c9875c authored by James Hawkins's avatar James Hawkins Committed by Alexandre Julliard

msi: Correctly order transposed column values in the INSERT query.

parent 4063a32f
......@@ -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)
......
......@@ -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;
......
......@@ -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
......
......@@ -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 );
......
......@@ -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:
......
......@@ -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:
......
......@@ -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<<tv->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");
......
......@@ -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();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment