diff --git a/dlls/msi/alter.c b/dlls/msi/alter.c index 2815f115adb..3e8ffbd245e 100644 --- a/dlls/msi/alter.c +++ b/dlls/msi/alter.c @@ -39,6 +39,7 @@ typedef struct tagMSIALTERVIEW MSIVIEW view; MSIDATABASE *db; MSIVIEW *table; + column_info *colinfo; INT hold; } MSIALTERVIEW; @@ -60,6 +61,78 @@ static UINT ALTER_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, ISt return ERROR_FUNCTION_FAILED; } +static UINT ITERATE_columns(MSIRECORD *row, LPVOID param) +{ + (*(UINT *)param)++; + return ERROR_SUCCESS; +} + +static BOOL check_column_exists(MSIDATABASE *db, MSIVIEW *columns, LPCWSTR table, LPCWSTR column) +{ + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','_','C','o','l','u','m','n','s','`',' ','W','H','E','R','E',' ', + '`','T','a','b','l','e','`','=','\'','%','s','\'',' ','A','N','D',' ', + '`','N','a','m','e','`','=','\'','%','s','\'',0 + }; + + r = MSI_OpenQuery(db, &view, query, table, column); + if (r != ERROR_SUCCESS) + return FALSE; + + r = MSI_ViewExecute(view, NULL); + if (r != ERROR_SUCCESS) + goto done; + + r = MSI_ViewFetch(view, &rec); + if (r == ERROR_SUCCESS) + msiobj_release(&rec->hdr); + +done: + msiobj_release(&view->hdr); + return (r == ERROR_SUCCESS); +} + +static UINT alter_add_column(MSIALTERVIEW *av) +{ + UINT r, colnum = 1; + MSIQUERY *view; + MSIVIEW *columns; + + static const WCHAR szColumns[] = {'_','C','o','l','u','m','n','s',0}; + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','_','C','o','l','u','m','n','s','`',' ','W','H','E','R','E',' ', + '`','T','a','b','l','e','`','=','\'','%','s','\'',' ','O','R','D','E','R',' ', + 'B','Y',' ','`','N','u','m','b','e','r','`',0 + }; + + r = TABLE_CreateView(av->db, szColumns, &columns); + if (r != ERROR_SUCCESS) + return r; + + if (check_column_exists(av->db, columns, av->colinfo->table, av->colinfo->column)) + return ERROR_BAD_QUERY_SYNTAX; + + r = MSI_OpenQuery(av->db, &view, query, av->colinfo->table, av->colinfo->column); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords(view, NULL, ITERATE_columns, &colnum); + msiobj_release(&view->hdr); + } + + r = columns->ops->add_column(columns, av->colinfo->table, + colnum, av->colinfo->column, + av->colinfo->type); + + msiobj_release(&columns->hdr); + return r; +} + static UINT ALTER_execute( struct tagMSIVIEW *view, MSIRECORD *record ) { MSIALTERVIEW *av = (MSIALTERVIEW*)view; @@ -70,6 +143,8 @@ static UINT ALTER_execute( struct tagMSIVIEW *view, MSIRECORD *record ) av->table->ops->add_ref(av->table); else if (av->hold == -1) av->table->ops->release(av->table); + else + return alter_add_column(av); return ERROR_SUCCESS; } @@ -147,9 +222,10 @@ static const MSIVIEWOPS alter_ops = ALTER_find_matching_rows, NULL, NULL, + NULL, }; -UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, int hold ) +UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, column_info *colinfo, int hold ) { MSIALTERVIEW *av; UINT r; @@ -164,10 +240,14 @@ UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, int hold ) if (r != ERROR_SUCCESS || !av->table) return r; + if (colinfo) + colinfo->table = name; + /* fill the structure */ av->view.ops = &alter_ops; av->db = db; av->hold = hold; + av->colinfo = colinfo; *view = &av->view; diff --git a/dlls/msi/create.c b/dlls/msi/create.c index 65a066ff086..f632ef63d91 100644 --- a/dlls/msi/create.c +++ b/dlls/msi/create.c @@ -132,6 +132,7 @@ static const MSIVIEWOPS create_ops = CREATE_delete, NULL, NULL, + NULL, }; static UINT check_columns( column_info *col_info ) diff --git a/dlls/msi/delete.c b/dlls/msi/delete.c index 8d668c4575f..81d8a450c74 100644 --- a/dlls/msi/delete.c +++ b/dlls/msi/delete.c @@ -195,6 +195,7 @@ static const MSIVIEWOPS delete_ops = DELETE_find_matching_rows, NULL, NULL, + NULL, }; UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) diff --git a/dlls/msi/distinct.c b/dlls/msi/distinct.c index 6d737d9d56b..9d8b79041b9 100644 --- a/dlls/msi/distinct.c +++ b/dlls/msi/distinct.c @@ -284,6 +284,7 @@ static const MSIVIEWOPS distinct_ops = DISTINCT_find_matching_rows, NULL, NULL, + NULL, }; UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) diff --git a/dlls/msi/insert.c b/dlls/msi/insert.c index 207f1538325..e7f3ce19879 100644 --- a/dlls/msi/insert.c +++ b/dlls/msi/insert.c @@ -235,6 +235,7 @@ static const MSIVIEWOPS insert_ops = INSERT_find_matching_rows, NULL, NULL, + NULL, }; static UINT count_column_info( const column_info *ci ) diff --git a/dlls/msi/join.c b/dlls/msi/join.c index 7ff2b91e03e..492d0d887ff 100644 --- a/dlls/msi/join.c +++ b/dlls/msi/join.c @@ -255,6 +255,7 @@ static const MSIVIEWOPS join_ops = JOIN_find_matching_rows, NULL, NULL, + NULL, }; UINT JOIN_CreateView( MSIDATABASE *db, MSIVIEW **view, diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 39719d5eade..0dceb753f75 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -231,6 +231,11 @@ typedef struct tagMSIVIEWOPS * release - decreases the reference count of the table */ UINT (*release)( struct tagMSIVIEW *view ); + + /* + * add_column - adds a column to the table + */ + UINT (*add_column)( struct tagMSIVIEW *view, LPCWSTR table, UINT number, LPCWSTR column, UINT type ); } MSIVIEWOPS; struct tagMSIVIEW diff --git a/dlls/msi/order.c b/dlls/msi/order.c index feb9a31238e..a2a3984d31f 100644 --- a/dlls/msi/order.c +++ b/dlls/msi/order.c @@ -284,6 +284,7 @@ static const MSIVIEWOPS order_ops = ORDER_find_matching_rows, NULL, NULL, + NULL, }; static UINT ORDER_AddColumn( MSIORDERVIEW *ov, LPCWSTR name ) diff --git a/dlls/msi/query.h b/dlls/msi/query.h index ef2e8a6814b..268989d2c34 100644 --- a/dlls/msi/query.h +++ b/dlls/msi/query.h @@ -122,7 +122,7 @@ UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); UINT JOIN_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR left, LPCWSTR right ); -UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, int hold ); +UINT ALTER_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR name, column_info *colinfo, int hold ); UINT STREAMS_CreateView( MSIDATABASE *db, MSIVIEW **view ); diff --git a/dlls/msi/select.c b/dlls/msi/select.c index fc2f7be3f86..6eaffaa31a9 100644 --- a/dlls/msi/select.c +++ b/dlls/msi/select.c @@ -277,6 +277,7 @@ static const MSIVIEWOPS select_ops = SELECT_find_matching_rows, NULL, NULL, + NULL, }; static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name ) diff --git a/dlls/msi/sql.y b/dlls/msi/sql.y index 902b08b97b5..96801f5d67d 100644 --- a/dlls/msi/sql.y +++ b/dlls/msi/sql.y @@ -83,7 +83,7 @@ static struct expr * EXPR_wildcard( void *info ); } %token TK_ALTER TK_AND TK_BY TK_CHAR TK_COMMA TK_CREATE TK_DELETE -%token TK_DISTINCT TK_DOT TK_EQ TK_FREE TK_FROM TK_GE TK_GT TK_HOLD +%token TK_DISTINCT TK_DOT TK_EQ TK_FREE TK_FROM TK_GE TK_GT TK_HOLD TK_ADD %token TK_ID %token TK_ILLEGAL TK_INSERT TK_INT %token TK_INTEGER @@ -231,11 +231,21 @@ onealter: SQL_input* sql = (SQL_input*) info; MSIVIEW *alter = NULL; - ALTER_CreateView( sql->db, &alter, $3, $4 ); + ALTER_CreateView( sql->db, &alter, $3, NULL, $4 ); if( !alter ) YYABORT; $$ = alter; } + | TK_ALTER TK_TABLE table TK_ADD column_and_type + { + SQL_input *sql = (SQL_input *)info; + MSIVIEW *alter = NULL; + + ALTER_CreateView( sql->db, &alter, $3, $5, 0 ); + if (!alter) + YYABORT; + $$ = alter; + } ; alterop: diff --git a/dlls/msi/streams.c b/dlls/msi/streams.c index 6828277b33f..f17fb98044f 100644 --- a/dlls/msi/streams.c +++ b/dlls/msi/streams.c @@ -339,6 +339,7 @@ static const MSIVIEWOPS streams_ops = STREAMS_find_matching_rows, NULL, NULL, + NULL, }; static UINT add_streams_to_table(MSISTREAMSVIEW *sv) diff --git a/dlls/msi/table.c b/dlls/msi/table.c index 33d6aa50561..1dba764b4aa 100644 --- a/dlls/msi/table.c +++ b/dlls/msi/table.c @@ -97,8 +97,8 @@ static const WCHAR szType[] = { 'T','y','p','e',0 }; * Do not mark them const. */ static MSICOLUMNINFO _Columns_cols[4] = { - { szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | 64, 0 }, - { szColumns, 2, szNumber, MSITYPE_VALID | 2, 2 }, + { szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0 }, + { szColumns, 2, szNumber, MSITYPE_VALID | MSITYPE_KEY | 2, 2 }, { szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4 }, { szColumns, 4, szType, MSITYPE_VALID | 2, 6 }, }; @@ -1035,6 +1035,26 @@ static UINT get_tablecolumns( MSIDATABASE *db, return ERROR_SUCCESS; } +static void msi_update_table_columns( MSIDATABASE *db, LPCWSTR name ) +{ + MSITABLE *table; + UINT size, offset; + int n; + + table = find_cached_table( db, name ); + msi_free( table->colinfo ); + table_get_column_info( db, name, &table->colinfo, &table->col_count ); + + size = msi_table_get_row_size( table->colinfo, table->col_count ); + offset = table->colinfo[table->col_count - 1].offset; + + for ( n = 0; n < table->row_count; n++ ) + { + table->data[n] = msi_realloc( table->data[n], size ); + table->data[n][offset] = (BYTE)MSI_NULL_INTEGER; + } +} + /* try to find the table name in the _Tables table */ BOOL TABLE_Exists( MSIDATABASE *db, LPCWSTR name ) { @@ -1674,6 +1694,32 @@ static UINT TABLE_release(struct tagMSIVIEW *view) return ref; } +static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR table, UINT number, LPCWSTR column, UINT type) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + MSIRECORD *rec; + UINT r; + + rec = MSI_CreateRecord(4); + if (!rec) + return ERROR_OUTOFMEMORY; + + MSI_RecordSetStringW(rec, 1, table); + MSI_RecordSetInteger(rec, 2, number); + MSI_RecordSetStringW(rec, 3, column); + MSI_RecordSetInteger(rec, 4, type); + + r = TABLE_insert_row(&tv->view, rec, FALSE); + if (r != ERROR_SUCCESS) + goto done; + + msi_update_table_columns(tv->db, table); + +done: + msiobj_release(&rec->hdr); + return r; +} + static const MSIVIEWOPS table_ops = { TABLE_fetch_int, @@ -1690,6 +1736,7 @@ static const MSIVIEWOPS table_ops = TABLE_find_matching_rows, TABLE_add_ref, TABLE_release, + TABLE_add_column, }; UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) @@ -1977,26 +2024,6 @@ static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row ) return r; } -static void msi_update_table_columns( MSIDATABASE *db, LPWSTR name ) -{ - MSITABLE *table; - UINT size, offset; - int n; - - table = find_cached_table( db, name ); - msi_free( table->colinfo ); - table_get_column_info( db, name, &table->colinfo, &table->col_count ); - - size = msi_table_get_row_size( table->colinfo, table->col_count ); - offset = table->colinfo[table->col_count - 1].offset; - - for ( n = 0; n < table->row_count; n++ ) - { - table->data[n] = msi_realloc( table->data[n], size ); - table->data[n][offset] = (BYTE)MSI_NULL_INTEGER; - } -} - typedef struct { struct list entry; diff --git a/dlls/msi/tests/db.c b/dlls/msi/tests/db.c index 4b54e8a9179..317425aaae8 100644 --- a/dlls/msi/tests/db.c +++ b/dlls/msi/tests/db.c @@ -2962,10 +2962,7 @@ static void test_alter(void) query = "ALTER TABLE `U` ADD `C` INTEGER"; 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); /* add column C again */ query = "ALTER TABLE `U` ADD `C` INTEGER"; @@ -2974,17 +2971,11 @@ static void test_alter(void) query = "ALTER TABLE `U` ADD `D` INTEGER TEMPORARY"; 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 = "INSERT INTO `U` ( `A`, `B`, `C`, `D` ) VALUES ( 1, 2, 3, 4 )"; 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 = "ALTER TABLE `U` ADD `D` INTEGER TEMPORARY HOLD"; r = run_query(hdb, 0, query); @@ -2992,17 +2983,11 @@ static void test_alter(void) query = "INSERT INTO `U` ( `A`, `B`, `C`, `D` ) VALUES ( 5, 6, 7, 8 )"; 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 = "SELECT * FROM `U` WHERE `D` = 8"; 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 = "ALTER TABLE `U` ADD `D` INTEGER TEMPORARY FREE"; r = run_query(hdb, 0, query); @@ -3025,11 +3010,17 @@ static void test_alter(void) /* column D is removed */ query = "SELECT * FROM `U` WHERE `D` = 8"; r = run_query(hdb, 0, query); - ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + todo_wine + { + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + } query = "INSERT INTO `U` ( `A`, `B`, `C`, `D` ) VALUES ( 9, 10, 11, 12 )"; r = run_query(hdb, 0, query); - ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + todo_wine + { + ok(r == ERROR_BAD_QUERY_SYNTAX, "Expected ERROR_BAD_QUERY_SYNTAX, got %d\n", r); + } /* add the column again */ query = "ALTER TABLE `U` ADD `E` INTEGER TEMPORARY HOLD"; diff --git a/dlls/msi/tests/package.c b/dlls/msi/tests/package.c index e30a0db22e7..78bf3b69bdd 100644 --- a/dlls/msi/tests/package.c +++ b/dlls/msi/tests/package.c @@ -1701,7 +1701,7 @@ static void test_property_table(void) query = "ALTER TABLE `_Property` ADD `extra` INTEGER"; r = run_query(hdb, query); - todo_wine ok(r == ERROR_SUCCESS, "failed to add column\n"); + ok(r == ERROR_SUCCESS, "failed to add column\n"); hpkg = package_from_db(hdb); todo_wine diff --git a/dlls/msi/tokenize.c b/dlls/msi/tokenize.c index 14bcaf2e49f..d05d244731a 100644 --- a/dlls/msi/tokenize.c +++ b/dlls/msi/tokenize.c @@ -38,6 +38,7 @@ struct Keyword { #define MAX_TOKEN_LEN 11 +static const WCHAR ADD_W[] = { 'A','D','D',0 }; static const WCHAR ALTER_W[] = { 'A','L','T','E','R',0 }; static const WCHAR AND_W[] = { 'A','N','D',0 }; static const WCHAR BY_W[] = { 'B','Y',0 }; @@ -78,6 +79,7 @@ static const WCHAR WHERE_W[] = { 'W','H','E','R','E',0 }; ** These are the keywords */ static const Keyword aKeywordTable[] = { + { ADD_W, TK_ADD }, { ALTER_W, TK_ALTER }, { AND_W, TK_AND }, { BY_W, TK_BY }, diff --git a/dlls/msi/update.c b/dlls/msi/update.c index 037c282bd2e..feb12a997c5 100644 --- a/dlls/msi/update.c +++ b/dlls/msi/update.c @@ -187,6 +187,7 @@ static const MSIVIEWOPS update_ops = UPDATE_find_matching_rows, NULL, NULL, + NULL, }; UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPCWSTR table, diff --git a/dlls/msi/where.c b/dlls/msi/where.c index 5b5d8678b37..fee7a303c6a 100644 --- a/dlls/msi/where.c +++ b/dlls/msi/where.c @@ -449,6 +449,7 @@ static const MSIVIEWOPS where_ops = WHERE_find_matching_rows, NULL, NULL, + NULL, }; static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,