2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002-2005 Mike McCormack for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
37 #include "wine/debug.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(msidb
);
41 #define MSITABLE_HASH_TABLE_SIZE 37
43 typedef struct tagMSICOLUMNHASHENTRY
45 struct tagMSICOLUMNHASHENTRY
*next
;
50 typedef struct tagMSICOLUMNINFO
57 MSICOLUMNHASHENTRY
**hash_table
;
63 BOOL
*data_persistent
;
66 MSICOLUMNINFO
*colinfo
;
68 MSICONDITION persistent
;
73 /* information for default tables */
74 static const MSICOLUMNINFO _Columns_cols
[4] = {
75 { L
"_Columns", 1, L
"Table", MSITYPE_VALID
| MSITYPE_STRING
| MSITYPE_KEY
| 64, 0, NULL
},
76 { L
"_Columns", 2, L
"Number", MSITYPE_VALID
| MSITYPE_KEY
| 2, 2, NULL
},
77 { L
"_Columns", 3, L
"Name", MSITYPE_VALID
| MSITYPE_STRING
| 64, 4, NULL
},
78 { L
"_Columns", 4, L
"Type", MSITYPE_VALID
| 2, 6, NULL
},
81 static const MSICOLUMNINFO _Tables_cols
[1] = {
82 { L
"_Tables", 1, L
"Name", MSITYPE_VALID
| MSITYPE_STRING
| MSITYPE_KEY
| 64, 0, NULL
},
85 #define MAX_STREAM_NAME 0x1f
87 static inline UINT
bytes_per_column( MSIDATABASE
*db
, const MSICOLUMNINFO
*col
, UINT bytes_per_strref
)
89 if( MSITYPE_IS_BINARY(col
->type
) )
92 if( col
->type
& MSITYPE_STRING
)
93 return bytes_per_strref
;
95 if( (col
->type
& 0xff) <= 2)
98 if( (col
->type
& 0xff) != 4 )
99 ERR("Invalid column size %u\n", col
->type
& 0xff);
104 static int utf2mime(int x
)
106 if( (x
>='0') && (x
<='9') )
108 if( (x
>='A') && (x
<='Z') )
110 if( (x
>='a') && (x
<='z') )
119 LPWSTR
encode_streamname(BOOL bTable
, LPCWSTR in
)
121 DWORD count
= MAX_STREAM_NAME
;
126 count
= lstrlenW( in
)+2;
127 if (!(out
= msi_alloc( count
*sizeof(WCHAR
) ))) return NULL
;
143 if( ( ch
< 0x80 ) && ( utf2mime(ch
) >= 0 ) )
145 ch
= utf2mime(ch
) + 0x4800;
147 if( next
&& (next
<0x80) )
149 next
= utf2mime(next
);
160 ERR("Failed to encode stream name (%s)\n",debugstr_w(in
));
165 static int mime2utf(int x
)
172 return x
- 10 - 26 + 'a';
173 if( x
== (10+26+26) )
178 BOOL
decode_streamname(LPCWSTR in
, LPWSTR out
)
183 while ( (ch
= *in
++) )
185 if( (ch
>= 0x3800 ) && (ch
< 0x4840 ) )
188 ch
= mime2utf(ch
-0x4800);
192 *out
++ = mime2utf(ch
&0x3f);
194 ch
= mime2utf((ch
>>6)&0x3f);
204 void enum_stream_names( IStorage
*stg
)
206 IEnumSTATSTG
*stgenum
= NULL
;
212 r
= IStorage_EnumElements( stg
, 0, NULL
, 0, &stgenum
);
220 r
= IEnumSTATSTG_Next( stgenum
, 1, &stat
, &count
);
221 if( FAILED( r
) || !count
)
223 decode_streamname( stat
.pwcsName
, name
);
224 TRACE( "stream %2lu -> %s %s\n", n
, debugstr_w(stat
.pwcsName
), debugstr_w(name
) );
225 CoTaskMemFree( stat
.pwcsName
);
229 IEnumSTATSTG_Release( stgenum
);
232 UINT
read_stream_data( IStorage
*stg
, LPCWSTR stname
, BOOL table
,
233 BYTE
**pdata
, UINT
*psz
)
236 UINT ret
= ERROR_FUNCTION_FAILED
;
243 encname
= encode_streamname(table
, stname
);
245 TRACE("%s -> %s\n",debugstr_w(stname
),debugstr_w(encname
));
247 r
= IStorage_OpenStream(stg
, encname
, NULL
,
248 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
252 WARN( "open stream failed r = %#lx - empty table?\n", r
);
256 r
= IStream_Stat(stm
, &stat
, STATFLAG_NONAME
);
259 WARN( "open stream failed r = %#lx!\n", r
);
263 if( stat
.cbSize
.QuadPart
>> 32 )
269 sz
= stat
.cbSize
.QuadPart
;
270 data
= msi_alloc( sz
);
273 WARN( "couldn't allocate memory r = %#lx!\n", r
);
274 ret
= ERROR_NOT_ENOUGH_MEMORY
;
278 r
= IStream_Read(stm
, data
, sz
, &count
);
279 if( FAILED( r
) || ( count
!= sz
) )
282 WARN("read stream failed r = %#lx!\n", r
);
291 IStream_Release( stm
);
296 UINT
write_stream_data( IStorage
*stg
, LPCWSTR stname
,
297 LPCVOID data
, UINT sz
, BOOL bTable
)
300 UINT ret
= ERROR_FUNCTION_FAILED
;
307 encname
= encode_streamname(bTable
, stname
);
308 r
= IStorage_OpenStream( stg
, encname
, NULL
,
309 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
312 r
= IStorage_CreateStream( stg
, encname
,
313 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stm
);
318 WARN( "open stream failed r = %#lx\n", r
);
323 r
= IStream_SetSize( stm
, size
);
326 WARN("Failed to SetSize\n");
331 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_SET
, NULL
);
334 WARN("Failed to Seek\n");
340 r
= IStream_Write(stm
, data
, sz
, &count
);
341 if( FAILED( r
) || ( count
!= sz
) )
343 WARN("Failed to Write\n");
351 IStream_Release( stm
);
356 static void msi_free_colinfo( MSICOLUMNINFO
*colinfo
, UINT count
)
359 for (i
= 0; i
< count
; i
++) msi_free( colinfo
[i
].hash_table
);
362 static void free_table( MSITABLE
*table
)
365 for( i
=0; i
<table
->row_count
; i
++ )
366 msi_free( table
->data
[i
] );
367 msi_free( table
->data
);
368 msi_free( table
->data_persistent
);
369 msi_free_colinfo( table
->colinfo
, table
->col_count
);
370 msi_free( table
->colinfo
);
374 static UINT
msi_table_get_row_size( MSIDATABASE
*db
, const MSICOLUMNINFO
*cols
, UINT count
, UINT bytes_per_strref
)
376 const MSICOLUMNINFO
*last_col
;
381 if (bytes_per_strref
!= LONG_STR_BYTES
)
384 for (i
= 0; i
< count
; i
++) size
+= bytes_per_column( db
, &cols
[i
], bytes_per_strref
);
387 last_col
= &cols
[count
- 1];
388 return last_col
->offset
+ bytes_per_column( db
, last_col
, bytes_per_strref
);
391 /* add this table to the list of cached tables in the database */
392 static UINT
read_table_from_storage( MSIDATABASE
*db
, MSITABLE
*t
, IStorage
*stg
)
394 BYTE
*rawdata
= NULL
;
395 UINT rawsize
= 0, i
, j
, row_size
, row_size_mem
;
397 TRACE("%s\n",debugstr_w(t
->name
));
399 row_size
= msi_table_get_row_size( db
, t
->colinfo
, t
->col_count
, db
->bytes_per_strref
);
400 row_size_mem
= msi_table_get_row_size( db
, t
->colinfo
, t
->col_count
, LONG_STR_BYTES
);
402 /* if we can't read the table, just assume that it's empty */
403 read_stream_data( stg
, t
->name
, TRUE
, &rawdata
, &rawsize
);
405 return ERROR_SUCCESS
;
407 TRACE("Read %d bytes\n", rawsize
);
409 if( rawsize
% row_size
)
411 WARN("Table size is invalid %d/%d\n", rawsize
, row_size
);
415 if ((t
->row_count
= rawsize
/ row_size
))
417 if (!(t
->data
= msi_alloc_zero( t
->row_count
* sizeof(USHORT
*) ))) goto err
;
418 if (!(t
->data_persistent
= msi_alloc_zero( t
->row_count
* sizeof(BOOL
) ))) goto err
;
421 /* transpose all the data */
422 TRACE("Transposing data from %d rows\n", t
->row_count
);
423 for (i
= 0; i
< t
->row_count
; i
++)
425 UINT ofs
= 0, ofs_mem
= 0;
427 t
->data
[i
] = msi_alloc( row_size_mem
);
430 t
->data_persistent
[i
] = TRUE
;
432 for (j
= 0; j
< t
->col_count
; j
++)
434 UINT m
= bytes_per_column( db
, &t
->colinfo
[j
], LONG_STR_BYTES
);
435 UINT n
= bytes_per_column( db
, &t
->colinfo
[j
], db
->bytes_per_strref
);
438 if ( n
!= 2 && n
!= 3 && n
!= 4 )
440 ERR("oops - unknown column width %d\n", n
);
443 if (t
->colinfo
[j
].type
& MSITYPE_STRING
&& n
< m
)
445 for (k
= 0; k
< m
; k
++)
448 t
->data
[i
][ofs_mem
+ k
] = rawdata
[ofs
* t
->row_count
+ i
* n
+ k
];
450 t
->data
[i
][ofs_mem
+ k
] = 0;
455 for (k
= 0; k
< n
; k
++)
456 t
->data
[i
][ofs_mem
+ k
] = rawdata
[ofs
* t
->row_count
+ i
* n
+ k
];
464 return ERROR_SUCCESS
;
467 return ERROR_FUNCTION_FAILED
;
470 void free_cached_tables( MSIDATABASE
*db
)
472 while( !list_empty( &db
->tables
) )
474 MSITABLE
*t
= LIST_ENTRY( list_head( &db
->tables
), MSITABLE
, entry
);
476 list_remove( &t
->entry
);
481 static MSITABLE
*find_cached_table( MSIDATABASE
*db
, LPCWSTR name
)
485 LIST_FOR_EACH_ENTRY( t
, &db
->tables
, MSITABLE
, entry
)
486 if( !wcscmp( name
, t
->name
) )
492 static void table_calc_column_offsets( MSIDATABASE
*db
, MSICOLUMNINFO
*colinfo
, DWORD count
)
496 for (i
= 0; colinfo
&& i
< count
; i
++)
498 assert( i
+ 1 == colinfo
[i
].number
);
499 if (i
) colinfo
[i
].offset
= colinfo
[i
- 1].offset
+
500 bytes_per_column( db
, &colinfo
[i
- 1], LONG_STR_BYTES
);
501 else colinfo
[i
].offset
= 0;
503 TRACE("column %d is [%s] with type %08x ofs %d\n",
504 colinfo
[i
].number
, debugstr_w(colinfo
[i
].colname
),
505 colinfo
[i
].type
, colinfo
[i
].offset
);
509 static UINT
get_defaulttablecolumns( MSIDATABASE
*db
, LPCWSTR name
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
511 const MSICOLUMNINFO
*p
;
514 TRACE("%s\n", debugstr_w(name
));
516 if (!wcscmp( name
, L
"_Tables" ))
521 else if (!wcscmp( name
, L
"_Columns" ))
526 else return ERROR_FUNCTION_FAILED
;
528 for (i
= 0; i
< n
; i
++)
530 if (colinfo
&& i
< *sz
) colinfo
[i
] = p
[i
];
531 if (colinfo
&& i
>= *sz
) break;
533 table_calc_column_offsets( db
, colinfo
, n
);
535 return ERROR_SUCCESS
;
538 static UINT
get_tablecolumns( MSIDATABASE
*db
, LPCWSTR szTableName
, MSICOLUMNINFO
*colinfo
, UINT
*sz
);
540 static UINT
table_get_column_info( MSIDATABASE
*db
, LPCWSTR name
, MSICOLUMNINFO
**pcols
, UINT
*pcount
)
542 UINT r
, column_count
= 0;
543 MSICOLUMNINFO
*columns
;
545 /* get the number of columns in this table */
547 r
= get_tablecolumns( db
, name
, NULL
, &column_count
);
548 if (r
!= ERROR_SUCCESS
)
551 *pcount
= column_count
;
553 /* if there are no columns, there's no table */
555 return ERROR_INVALID_PARAMETER
;
557 TRACE("table %s found\n", debugstr_w(name
));
559 columns
= msi_alloc( column_count
* sizeof(MSICOLUMNINFO
) );
561 return ERROR_FUNCTION_FAILED
;
563 r
= get_tablecolumns( db
, name
, columns
, &column_count
);
564 if (r
!= ERROR_SUCCESS
)
567 return ERROR_FUNCTION_FAILED
;
573 static UINT
get_table( MSIDATABASE
*db
, LPCWSTR name
, MSITABLE
**table_ret
)
578 /* first, see if the table is cached */
579 table
= find_cached_table( db
, name
);
583 return ERROR_SUCCESS
;
586 /* nonexistent tables should be interpreted as empty tables */
587 table
= msi_alloc( sizeof(MSITABLE
) + lstrlenW( name
) * sizeof(WCHAR
) );
589 return ERROR_FUNCTION_FAILED
;
591 table
->row_count
= 0;
593 table
->data_persistent
= NULL
;
594 table
->colinfo
= NULL
;
595 table
->col_count
= 0;
596 table
->persistent
= MSICONDITION_TRUE
;
597 lstrcpyW( table
->name
, name
);
599 if (!wcscmp( name
, L
"_Tables" ) || !wcscmp( name
, L
"_Columns" ))
600 table
->persistent
= MSICONDITION_NONE
;
602 r
= table_get_column_info( db
, name
, &table
->colinfo
, &table
->col_count
);
603 if (r
!= ERROR_SUCCESS
)
608 r
= read_table_from_storage( db
, table
, db
->storage
);
609 if (r
!= ERROR_SUCCESS
)
614 list_add_head( &db
->tables
, &table
->entry
);
616 return ERROR_SUCCESS
;
619 static UINT
read_table_int( BYTE
*const *data
, UINT row
, UINT col
, UINT bytes
)
623 for (i
= 0; i
< bytes
; i
++)
624 ret
+= data
[row
][col
+ i
] << i
* 8;
629 static UINT
get_tablecolumns( MSIDATABASE
*db
, LPCWSTR szTableName
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
631 UINT r
, i
, n
= 0, table_id
, count
, maxcount
= *sz
;
632 MSITABLE
*table
= NULL
;
634 TRACE("%s\n", debugstr_w(szTableName
));
636 /* first check if there is a default table with that name */
637 r
= get_defaulttablecolumns( db
, szTableName
, colinfo
, sz
);
638 if (r
== ERROR_SUCCESS
&& *sz
)
641 r
= get_table( db
, L
"_Columns", &table
);
642 if (r
!= ERROR_SUCCESS
)
644 ERR("couldn't load _Columns table\n");
645 return ERROR_FUNCTION_FAILED
;
648 /* convert table and column names to IDs from the string table */
649 r
= msi_string2id( db
->strings
, szTableName
, -1, &table_id
);
650 if (r
!= ERROR_SUCCESS
)
652 WARN("Couldn't find id for %s\n", debugstr_w(szTableName
));
655 TRACE("Table id is %d, row count is %d\n", table_id
, table
->row_count
);
657 /* Note: _Columns table doesn't have non-persistent data */
659 /* if maxcount is non-zero, assume it's exactly right for this table */
660 if (colinfo
) memset( colinfo
, 0, maxcount
* sizeof(*colinfo
) );
661 count
= table
->row_count
;
662 for (i
= 0; i
< count
; i
++)
664 if (read_table_int( table
->data
, i
, 0, LONG_STR_BYTES
) != table_id
) continue;
667 UINT id
= read_table_int( table
->data
, i
, table
->colinfo
[2].offset
, LONG_STR_BYTES
);
668 UINT col
= read_table_int( table
->data
, i
, table
->colinfo
[1].offset
, sizeof(USHORT
) ) - (1 << 15);
670 /* check the column number is in range */
671 if (col
< 1 || col
> maxcount
)
673 ERR("column %d out of range (maxcount: %d)\n", col
, maxcount
);
676 /* check if this column was already set */
677 if (colinfo
[col
- 1].number
)
679 ERR("duplicate column %d\n", col
);
682 colinfo
[col
- 1].tablename
= msi_string_lookup( db
->strings
, table_id
, NULL
);
683 colinfo
[col
- 1].number
= col
;
684 colinfo
[col
- 1].colname
= msi_string_lookup( db
->strings
, id
, NULL
);
685 colinfo
[col
- 1].type
= read_table_int( table
->data
, i
, table
->colinfo
[3].offset
,
686 sizeof(USHORT
) ) - (1 << 15);
687 colinfo
[col
- 1].offset
= 0;
688 colinfo
[col
- 1].hash_table
= NULL
;
692 TRACE("%s has %d columns\n", debugstr_w(szTableName
), n
);
694 if (colinfo
&& n
!= maxcount
)
696 ERR("missing column in table %s\n", debugstr_w(szTableName
));
697 msi_free_colinfo( colinfo
, maxcount
);
698 return ERROR_FUNCTION_FAILED
;
700 table_calc_column_offsets( db
, colinfo
, n
);
702 return ERROR_SUCCESS
;
705 UINT
msi_create_table( MSIDATABASE
*db
, LPCWSTR name
, column_info
*col_info
,
706 MSICONDITION persistent
, BOOL hold
)
710 MSIRECORD
*rec
= NULL
;
715 /* only add tables that don't exist already */
716 if( TABLE_Exists(db
, name
) )
718 WARN("table %s exists\n", debugstr_w(name
));
719 return ERROR_BAD_QUERY_SYNTAX
;
722 table
= msi_alloc( sizeof (MSITABLE
) + lstrlenW(name
)*sizeof (WCHAR
) );
724 return ERROR_FUNCTION_FAILED
;
726 table
->ref_count
= 0;
727 table
->row_count
= 0;
729 table
->data_persistent
= NULL
;
730 table
->colinfo
= NULL
;
731 table
->col_count
= 0;
732 table
->persistent
= persistent
;
733 lstrcpyW( table
->name
, name
);
738 for( col
= col_info
; col
; col
= col
->next
)
741 table
->colinfo
= msi_alloc( table
->col_count
* sizeof(MSICOLUMNINFO
) );
745 return ERROR_FUNCTION_FAILED
;
748 for( i
= 0, col
= col_info
; col
; i
++, col
= col
->next
)
750 UINT table_id
= msi_add_string( db
->strings
, col
->table
, -1, persistent
);
751 UINT col_id
= msi_add_string( db
->strings
, col
->column
, -1, persistent
);
753 table
->colinfo
[ i
].tablename
= msi_string_lookup( db
->strings
, table_id
, NULL
);
754 table
->colinfo
[ i
].number
= i
+ 1;
755 table
->colinfo
[ i
].colname
= msi_string_lookup( db
->strings
, col_id
, NULL
);
756 table
->colinfo
[ i
].type
= col
->type
;
757 table
->colinfo
[ i
].offset
= 0;
758 table
->colinfo
[ i
].hash_table
= NULL
;
760 table_calc_column_offsets( db
, table
->colinfo
, table
->col_count
);
762 r
= TABLE_CreateView( db
, L
"_Tables", &tv
);
763 TRACE("CreateView returned %x\n", r
);
770 r
= tv
->ops
->execute( tv
, 0 );
771 TRACE("tv execute returned %x\n", r
);
775 rec
= MSI_CreateRecord( 1 );
779 r
= MSI_RecordSetStringW( rec
, 1, name
);
783 r
= tv
->ops
->insert_row( tv
, rec
, -1, persistent
== MSICONDITION_FALSE
);
784 TRACE("insert_row returned %x\n", r
);
788 tv
->ops
->delete( tv
);
791 msiobj_release( &rec
->hdr
);
794 if( persistent
!= MSICONDITION_FALSE
)
796 /* add each column to the _Columns table */
797 r
= TABLE_CreateView( db
, L
"_Columns", &tv
);
801 r
= tv
->ops
->execute( tv
, 0 );
802 TRACE("tv execute returned %x\n", r
);
806 rec
= MSI_CreateRecord( 4 );
810 r
= MSI_RecordSetStringW( rec
, 1, name
);
815 * need to set the table, column number, col name and type
816 * for each column we enter in the table
819 for( col
= col_info
; col
; col
= col
->next
)
821 r
= MSI_RecordSetInteger( rec
, 2, nField
);
825 r
= MSI_RecordSetStringW( rec
, 3, col
->column
);
829 r
= MSI_RecordSetInteger( rec
, 4, col
->type
);
833 r
= tv
->ops
->insert_row( tv
, rec
, -1, FALSE
);
845 msiobj_release( &rec
->hdr
);
846 /* FIXME: remove values from the string table on error */
848 tv
->ops
->delete( tv
);
850 if (r
== ERROR_SUCCESS
)
851 list_add_head( &db
->tables
, &table
->entry
);
858 static UINT
save_table( MSIDATABASE
*db
, const MSITABLE
*t
, UINT bytes_per_strref
)
860 BYTE
*rawdata
= NULL
;
861 UINT rawsize
, i
, j
, row_size
, row_count
;
862 UINT r
= ERROR_FUNCTION_FAILED
;
864 /* Nothing to do for non-persistent tables */
865 if( t
->persistent
== MSICONDITION_FALSE
)
866 return ERROR_SUCCESS
;
868 TRACE("Saving %s\n", debugstr_w( t
->name
) );
870 row_size
= msi_table_get_row_size( db
, t
->colinfo
, t
->col_count
, bytes_per_strref
);
871 row_count
= t
->row_count
;
872 for (i
= 0; i
< t
->row_count
; i
++)
874 if (!t
->data_persistent
[i
])
876 row_count
= 1; /* yes, this is bizarre */
880 rawsize
= row_count
* row_size
;
881 rawdata
= msi_alloc_zero( rawsize
);
884 r
= ERROR_NOT_ENOUGH_MEMORY
;
889 for (i
= 0; i
< row_count
; i
++)
891 UINT ofs
= 0, ofs_mem
= 0;
893 if (!t
->data_persistent
[i
]) break;
895 for (j
= 0; j
< t
->col_count
; j
++)
897 UINT m
= bytes_per_column( db
, &t
->colinfo
[j
], LONG_STR_BYTES
);
898 UINT n
= bytes_per_column( db
, &t
->colinfo
[j
], bytes_per_strref
);
901 if (n
!= 2 && n
!= 3 && n
!= 4)
903 ERR("oops - unknown column width %d\n", n
);
906 if (t
->colinfo
[j
].type
& MSITYPE_STRING
&& n
< m
)
908 UINT id
= read_table_int( t
->data
, i
, ofs_mem
, LONG_STR_BYTES
);
909 if (id
> 1 << bytes_per_strref
* 8)
911 ERR("string id %u out of range\n", id
);
915 for (k
= 0; k
< n
; k
++)
917 rawdata
[ofs
* row_count
+ i
* n
+ k
] = t
->data
[i
][ofs_mem
+ k
];
925 TRACE("writing %d bytes\n", rawsize
);
926 r
= write_stream_data( db
->storage
, t
->name
, rawdata
, rawsize
, TRUE
);
933 static void msi_update_table_columns( MSIDATABASE
*db
, LPCWSTR name
)
936 UINT size
, offset
, old_count
;
939 if (!(table
= find_cached_table( db
, name
))) return;
940 old_count
= table
->col_count
;
941 msi_free_colinfo( table
->colinfo
, table
->col_count
);
942 msi_free( table
->colinfo
);
943 table
->colinfo
= NULL
;
945 table_get_column_info( db
, name
, &table
->colinfo
, &table
->col_count
);
946 if (!table
->col_count
) return;
948 size
= msi_table_get_row_size( db
, table
->colinfo
, table
->col_count
, LONG_STR_BYTES
);
949 offset
= table
->colinfo
[table
->col_count
- 1].offset
;
951 for ( n
= 0; n
< table
->row_count
; n
++ )
953 table
->data
[n
] = msi_realloc( table
->data
[n
], size
);
954 if (old_count
< table
->col_count
)
955 memset( &table
->data
[n
][offset
], 0, size
- offset
);
959 /* try to find the table name in the _Tables table */
960 BOOL
TABLE_Exists( MSIDATABASE
*db
, LPCWSTR name
)
965 if( !wcscmp( name
, L
"_Tables" ) || !wcscmp( name
, L
"_Columns" ) ||
966 !wcscmp( name
, L
"_Streams" ) || !wcscmp( name
, L
"_Storages" ) )
969 r
= msi_string2id( db
->strings
, name
, -1, &table_id
);
970 if( r
!= ERROR_SUCCESS
)
972 TRACE("Couldn't find id for %s\n", debugstr_w(name
));
976 r
= get_table( db
, L
"_Tables", &table
);
977 if( r
!= ERROR_SUCCESS
)
979 ERR("table _Tables not available\n");
983 for( i
= 0; i
< table
->row_count
; i
++ )
985 if( read_table_int( table
->data
, i
, 0, LONG_STR_BYTES
) == table_id
)
992 /* below is the query interface to a table */
994 typedef struct tagMSITABLEVIEW
999 MSICOLUMNINFO
*columns
;
1005 static UINT
TABLE_fetch_int( struct tagMSIVIEW
*view
, UINT row
, UINT col
, UINT
*val
)
1007 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1011 return ERROR_INVALID_PARAMETER
;
1013 if( (col
==0) || (col
>tv
->num_cols
) )
1014 return ERROR_INVALID_PARAMETER
;
1016 /* how many rows are there ? */
1017 if( row
>= tv
->table
->row_count
)
1018 return ERROR_NO_MORE_ITEMS
;
1020 if( tv
->columns
[col
-1].offset
>= tv
->row_size
)
1022 ERR("Stuffed up %d >= %d\n", tv
->columns
[col
-1].offset
, tv
->row_size
);
1023 ERR("%p %p\n", tv
, tv
->columns
);
1024 return ERROR_FUNCTION_FAILED
;
1027 n
= bytes_per_column( tv
->db
, &tv
->columns
[col
- 1], LONG_STR_BYTES
);
1028 if (n
!= 2 && n
!= 3 && n
!= 4)
1030 ERR("oops! what is %d bytes per column?\n", n
);
1031 return ERROR_FUNCTION_FAILED
;
1034 offset
= tv
->columns
[col
-1].offset
;
1035 *val
= read_table_int(tv
->table
->data
, row
, offset
, n
);
1037 /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1039 return ERROR_SUCCESS
;
1042 static UINT
get_stream_name( const MSITABLEVIEW
*tv
, UINT row
, WCHAR
**pstname
)
1044 LPWSTR p
, stname
= NULL
;
1045 UINT i
, r
, type
, ival
;
1048 MSIVIEW
*view
= (MSIVIEW
*) tv
;
1050 TRACE("%p %d\n", tv
, row
);
1052 len
= lstrlenW( tv
->name
) + 1;
1053 stname
= msi_alloc( len
*sizeof(WCHAR
) );
1056 r
= ERROR_OUTOFMEMORY
;
1060 lstrcpyW( stname
, tv
->name
);
1062 for ( i
= 0; i
< tv
->num_cols
; i
++ )
1064 type
= tv
->columns
[i
].type
;
1065 if ( type
& MSITYPE_KEY
)
1069 r
= TABLE_fetch_int( view
, row
, i
+1, &ival
);
1070 if ( r
!= ERROR_SUCCESS
)
1073 if ( tv
->columns
[i
].type
& MSITYPE_STRING
)
1075 sval
= msi_string_lookup( tv
->db
->strings
, ival
, NULL
);
1078 r
= ERROR_INVALID_PARAMETER
;
1084 UINT n
= bytes_per_column( tv
->db
, &tv
->columns
[i
], LONG_STR_BYTES
);
1089 swprintf( number
, ARRAY_SIZE(number
), L
"%d", ival
-0x8000 );
1092 swprintf( number
, ARRAY_SIZE(number
), L
"%d", ival
^0x80000000 );
1095 ERR( "oops - unknown column width %d\n", n
);
1096 r
= ERROR_FUNCTION_FAILED
;
1102 len
+= lstrlenW( L
"." ) + lstrlenW( sval
);
1103 p
= msi_realloc ( stname
, len
*sizeof(WCHAR
) );
1106 r
= ERROR_OUTOFMEMORY
;
1111 lstrcatW( stname
, L
"." );
1112 lstrcatW( stname
, sval
);
1119 return ERROR_SUCCESS
;
1128 * We need a special case for streams, as we need to reference column with
1129 * the name of the stream in the same table, and the table name
1130 * which may not be available at higher levels of the query
1132 static UINT
TABLE_fetch_stream( struct tagMSIVIEW
*view
, UINT row
, UINT col
, IStream
**stm
)
1134 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1138 if( !view
->ops
->fetch_int
)
1139 return ERROR_INVALID_PARAMETER
;
1141 r
= get_stream_name( tv
, row
, &name
);
1142 if (r
!= ERROR_SUCCESS
)
1144 ERR("fetching stream, error = %u\n", r
);
1148 r
= msi_get_stream( tv
->db
, name
, stm
);
1149 if (r
!= ERROR_SUCCESS
)
1150 ERR("fetching stream %s, error = %u\n", debugstr_w(name
), r
);
1156 /* Set a table value, i.e. preadjusted integer or string ID. */
1157 static UINT
table_set_bytes( MSITABLEVIEW
*tv
, UINT row
, UINT col
, UINT val
)
1162 return ERROR_INVALID_PARAMETER
;
1164 if( (col
==0) || (col
>tv
->num_cols
) )
1165 return ERROR_INVALID_PARAMETER
;
1167 if( row
>= tv
->table
->row_count
)
1168 return ERROR_INVALID_PARAMETER
;
1170 if( tv
->columns
[col
-1].offset
>= tv
->row_size
)
1172 ERR("Stuffed up %d >= %d\n", tv
->columns
[col
-1].offset
, tv
->row_size
);
1173 ERR("%p %p\n", tv
, tv
->columns
);
1174 return ERROR_FUNCTION_FAILED
;
1177 msi_free( tv
->columns
[col
-1].hash_table
);
1178 tv
->columns
[col
-1].hash_table
= NULL
;
1180 n
= bytes_per_column( tv
->db
, &tv
->columns
[col
- 1], LONG_STR_BYTES
);
1181 if ( n
!= 2 && n
!= 3 && n
!= 4 )
1183 ERR("oops! what is %d bytes per column?\n", n
);
1184 return ERROR_FUNCTION_FAILED
;
1187 offset
= tv
->columns
[col
-1].offset
;
1188 for ( i
= 0; i
< n
; i
++ )
1189 tv
->table
->data
[row
][offset
+ i
] = (val
>> i
* 8) & 0xff;
1191 return ERROR_SUCCESS
;
1194 static UINT
int_to_table_storage( const MSITABLEVIEW
*tv
, UINT col
, int val
, UINT
*ret
)
1196 if ((tv
->columns
[col
-1].type
& MSI_DATASIZEMASK
) == 2)
1198 if (val
== MSI_NULL_INTEGER
)
1200 else if ((val
+ 0x8000) & 0xffff0000)
1202 ERR("value %d out of range\n", val
);
1203 return ERROR_FUNCTION_FAILED
;
1206 *ret
= val
+ 0x8000;
1209 *ret
= val
^ 0x80000000;
1211 return ERROR_SUCCESS
;
1214 static UINT
TABLE_set_int( MSIVIEW
*view
, UINT row
, UINT col
, int val
)
1216 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1219 TRACE("row %u, col %u, val %d.\n", row
, col
, val
);
1221 if ((r
= int_to_table_storage( tv
, col
, val
, &table_int
)))
1224 if (tv
->columns
[col
-1].type
& MSITYPE_KEY
)
1228 if ((r
= TABLE_fetch_int( view
, row
, col
, &key
)))
1230 if (key
!= table_int
)
1232 ERR("Cannot modify primary key %s.%s.\n",
1233 debugstr_w(tv
->table
->name
), debugstr_w(tv
->columns
[col
-1].colname
));
1234 return ERROR_FUNCTION_FAILED
;
1238 return table_set_bytes( tv
, row
, col
, table_int
);
1241 static UINT
TABLE_set_string( MSIVIEW
*view
, UINT row
, UINT col
, const WCHAR
*val
, int len
)
1243 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1247 TRACE("row %u, col %u, val %s.\n", row
, col
, debugstr_wn(val
, len
));
1249 persistent
= (tv
->table
->persistent
!= MSICONDITION_FALSE
)
1250 && tv
->table
->data_persistent
[row
];
1254 r
= msi_string2id( tv
->db
->strings
, val
, len
, &id
);
1255 if (r
!= ERROR_SUCCESS
)
1256 id
= msi_add_string( tv
->db
->strings
, val
, len
, persistent
);
1261 if (tv
->columns
[col
-1].type
& MSITYPE_KEY
)
1265 if ((r
= TABLE_fetch_int( view
, row
, col
, &key
)))
1269 ERR("Cannot modify primary key %s.%s.\n",
1270 debugstr_w(tv
->table
->name
), debugstr_w(tv
->columns
[col
-1].colname
));
1271 return ERROR_FUNCTION_FAILED
;
1275 return table_set_bytes( tv
, row
, col
, id
);
1278 static UINT
TABLE_get_row( struct tagMSIVIEW
*view
, UINT row
, MSIRECORD
**rec
)
1280 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1283 return ERROR_INVALID_PARAMETER
;
1285 return msi_view_get_row(tv
->db
, view
, row
, rec
);
1288 static UINT
add_stream( MSIDATABASE
*db
, const WCHAR
*name
, IStream
*data
)
1294 TRACE("%p %s %p\n", db
, debugstr_w(name
), data
);
1296 if (!(rec
= MSI_CreateRecord( 2 )))
1297 return ERROR_OUTOFMEMORY
;
1299 r
= MSI_RecordSetStringW( rec
, 1, name
);
1300 if (r
!= ERROR_SUCCESS
)
1303 r
= MSI_RecordSetIStream( rec
, 2, data
);
1304 if (r
!= ERROR_SUCCESS
)
1307 r
= MSI_DatabaseOpenViewW( db
, L
"INSERT INTO `_Streams` (`Name`,`Data`) VALUES (?,?)", &query
);
1308 if (r
!= ERROR_SUCCESS
)
1311 r
= MSI_ViewExecute( query
, rec
);
1312 msiobj_release( &query
->hdr
);
1313 if (r
== ERROR_SUCCESS
)
1316 msiobj_release( &rec
->hdr
);
1317 if (!(rec
= MSI_CreateRecord( 2 )))
1318 return ERROR_OUTOFMEMORY
;
1320 r
= MSI_RecordSetIStream( rec
, 1, data
);
1321 if (r
!= ERROR_SUCCESS
)
1324 r
= MSI_RecordSetStringW( rec
, 2, name
);
1325 if (r
!= ERROR_SUCCESS
)
1328 r
= MSI_DatabaseOpenViewW( db
, L
"UPDATE `_Streams` SET `Data` = ? WHERE `Name` = ?", &query
);
1329 if (r
!= ERROR_SUCCESS
)
1332 r
= MSI_ViewExecute( query
, rec
);
1333 msiobj_release( &query
->hdr
);
1336 msiobj_release( &rec
->hdr
);
1340 static UINT
TABLE_set_stream( MSIVIEW
*view
, UINT row
, UINT col
, IStream
*stream
)
1342 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1346 TRACE("row %u, col %u, stream %p.\n", row
, col
, stream
);
1348 if ((r
= get_stream_name( tv
, row
- 1, &name
)))
1351 r
= add_stream( tv
->db
, name
, stream
);
1356 static UINT
get_table_value_from_record( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT iField
, UINT
*pvalue
)
1358 MSICOLUMNINFO columninfo
;
1361 if (!iField
|| iField
> tv
->num_cols
|| MSI_RecordIsNull( rec
, iField
))
1362 return ERROR_FUNCTION_FAILED
;
1364 columninfo
= tv
->columns
[ iField
- 1 ];
1366 if ( MSITYPE_IS_BINARY(columninfo
.type
) )
1368 *pvalue
= 1; /* refers to the first key column */
1370 else if ( columninfo
.type
& MSITYPE_STRING
)
1373 const WCHAR
*sval
= msi_record_get_string( rec
, iField
, &len
);
1376 r
= msi_string2id( tv
->db
->strings
, sval
, len
, pvalue
);
1377 if (r
!= ERROR_SUCCESS
)
1378 return ERROR_NOT_FOUND
;
1383 return int_to_table_storage( tv
, iField
, MSI_RecordGetInteger( rec
, iField
), pvalue
);
1385 return ERROR_SUCCESS
;
1388 static UINT
TABLE_set_row( struct tagMSIVIEW
*view
, UINT row
, MSIRECORD
*rec
, UINT mask
)
1390 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1391 UINT i
, val
, r
= ERROR_SUCCESS
;
1394 return ERROR_INVALID_PARAMETER
;
1396 /* test if any of the mask bits are invalid */
1397 if ( mask
>= (1<<tv
->num_cols
) )
1398 return ERROR_INVALID_PARAMETER
;
1400 for ( i
= 0; i
< tv
->num_cols
; i
++ )
1404 /* only update the fields specified in the mask */
1405 if ( !(mask
&(1<<i
)) )
1408 persistent
= (tv
->table
->persistent
!= MSICONDITION_FALSE
) &&
1409 (tv
->table
->data_persistent
[row
]);
1410 /* FIXME: should we allow updating keys? */
1413 if ( !MSI_RecordIsNull( rec
, i
+ 1 ) )
1415 r
= get_table_value_from_record (tv
, rec
, i
+ 1, &val
);
1416 if ( MSITYPE_IS_BINARY(tv
->columns
[ i
].type
) )
1421 if ( r
!= ERROR_SUCCESS
)
1422 return ERROR_FUNCTION_FAILED
;
1424 r
= MSI_RecordGetIStream( rec
, i
+ 1, &stm
);
1425 if ( r
!= ERROR_SUCCESS
)
1428 r
= get_stream_name( tv
, row
, &stname
);
1429 if ( r
!= ERROR_SUCCESS
)
1431 IStream_Release( stm
);
1435 r
= add_stream( tv
->db
, stname
, stm
);
1436 IStream_Release( stm
);
1437 msi_free ( stname
);
1439 if ( r
!= ERROR_SUCCESS
)
1442 else if ( tv
->columns
[i
].type
& MSITYPE_STRING
)
1446 if ( r
!= ERROR_SUCCESS
)
1449 const WCHAR
*sval
= msi_record_get_string( rec
, i
+ 1, &len
);
1450 val
= msi_add_string( tv
->db
->strings
, sval
, len
, persistent
);
1454 TABLE_fetch_int(&tv
->view
, row
, i
+ 1, &x
);
1461 if ( r
!= ERROR_SUCCESS
)
1462 return ERROR_FUNCTION_FAILED
;
1466 r
= table_set_bytes( tv
, row
, i
+1, val
);
1467 if ( r
!= ERROR_SUCCESS
)
1473 static UINT
table_create_new_row( struct tagMSIVIEW
*view
, UINT
*num
, BOOL temporary
)
1475 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1480 BOOL
**data_persist_ptr
;
1483 TRACE("%p %s\n", view
, temporary
? "TRUE" : "FALSE");
1486 return ERROR_INVALID_PARAMETER
;
1488 row
= msi_alloc_zero( tv
->row_size
);
1490 return ERROR_NOT_ENOUGH_MEMORY
;
1492 row_count
= &tv
->table
->row_count
;
1493 data_ptr
= &tv
->table
->data
;
1494 data_persist_ptr
= &tv
->table
->data_persistent
;
1496 *num
= tv
->table
->row_count
;
1498 sz
= (*row_count
+ 1) * sizeof (BYTE
*);
1500 p
= msi_realloc( *data_ptr
, sz
);
1502 p
= msi_alloc( sz
);
1506 return ERROR_NOT_ENOUGH_MEMORY
;
1509 sz
= (*row_count
+ 1) * sizeof (BOOL
);
1510 if( *data_persist_ptr
)
1511 b
= msi_realloc( *data_persist_ptr
, sz
);
1513 b
= msi_alloc( sz
);
1518 return ERROR_NOT_ENOUGH_MEMORY
;
1522 (*data_ptr
)[*row_count
] = row
;
1524 *data_persist_ptr
= b
;
1525 (*data_persist_ptr
)[*row_count
] = !temporary
;
1529 return ERROR_SUCCESS
;
1532 static UINT
TABLE_execute( struct tagMSIVIEW
*view
, MSIRECORD
*record
)
1534 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1536 TRACE("%p %p\n", tv
, record
);
1538 TRACE("There are %d columns\n", tv
->num_cols
);
1540 return ERROR_SUCCESS
;
1543 static UINT
TABLE_close( struct tagMSIVIEW
*view
)
1545 TRACE("%p\n", view
);
1547 return ERROR_SUCCESS
;
1550 static UINT
TABLE_get_dimensions( struct tagMSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
1552 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1554 TRACE("%p %p %p\n", view
, rows
, cols
);
1557 *cols
= tv
->num_cols
;
1561 return ERROR_INVALID_PARAMETER
;
1562 *rows
= tv
->table
->row_count
;
1565 return ERROR_SUCCESS
;
1568 static UINT
TABLE_get_column_info( struct tagMSIVIEW
*view
,
1569 UINT n
, LPCWSTR
*name
, UINT
*type
, BOOL
*temporary
,
1570 LPCWSTR
*table_name
)
1572 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1574 TRACE("%p %d %p %p\n", tv
, n
, name
, type
);
1576 if( ( n
== 0 ) || ( n
> tv
->num_cols
) )
1577 return ERROR_INVALID_PARAMETER
;
1581 *name
= tv
->columns
[n
-1].colname
;
1583 return ERROR_FUNCTION_FAILED
;
1588 *table_name
= tv
->columns
[n
-1].tablename
;
1590 return ERROR_FUNCTION_FAILED
;
1594 *type
= tv
->columns
[n
-1].type
;
1597 *temporary
= (tv
->columns
[n
-1].type
& MSITYPE_TEMPORARY
) != 0;
1599 return ERROR_SUCCESS
;
1602 static UINT
msi_table_find_row( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT
*row
, UINT
*column
);
1604 static UINT
table_validate_new( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT
*column
)
1608 /* check there are no null values where they're not allowed */
1609 for( i
= 0; i
< tv
->num_cols
; i
++ )
1611 if ( tv
->columns
[i
].type
& MSITYPE_NULLABLE
)
1614 if ( MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
1615 TRACE("skipping binary column\n");
1616 else if ( tv
->columns
[i
].type
& MSITYPE_STRING
)
1619 const WCHAR
*str
= msi_record_get_string( rec
, i
+1, &len
);
1621 if (!str
|| (!str
[0] && !len
))
1623 if (column
) *column
= i
;
1624 return ERROR_INVALID_DATA
;
1631 n
= MSI_RecordGetInteger( rec
, i
+1 );
1632 if (n
== MSI_NULL_INTEGER
)
1634 if (column
) *column
= i
;
1635 return ERROR_INVALID_DATA
;
1640 /* check there are no duplicate keys */
1641 r
= msi_table_find_row( tv
, rec
, &row
, column
);
1642 if (r
== ERROR_SUCCESS
)
1643 return ERROR_FUNCTION_FAILED
;
1645 return ERROR_SUCCESS
;
1648 static int compare_record( MSITABLEVIEW
*tv
, UINT row
, MSIRECORD
*rec
)
1650 UINT r
, i
, ivalue
, x
;
1652 for (i
= 0; i
< tv
->num_cols
; i
++ )
1654 if (!(tv
->columns
[i
].type
& MSITYPE_KEY
)) continue;
1656 r
= get_table_value_from_record( tv
, rec
, i
+ 1, &ivalue
);
1657 if (r
!= ERROR_SUCCESS
)
1660 r
= TABLE_fetch_int( &tv
->view
, row
, i
+ 1, &x
);
1661 if (r
!= ERROR_SUCCESS
)
1663 WARN("TABLE_fetch_int should not fail here %u\n", r
);
1670 else if (ivalue
== x
)
1672 if (i
< tv
->num_cols
- 1) continue;
1681 static int find_insert_index( MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
1683 int idx
, c
, low
= 0, high
= tv
->table
->row_count
- 1;
1685 TRACE("%p %p\n", tv
, rec
);
1689 idx
= (low
+ high
) / 2;
1690 c
= compare_record( tv
, idx
, rec
);
1698 TRACE("found %u\n", idx
);
1702 TRACE("found %u\n", high
+ 1);
1706 static UINT
TABLE_insert_row( struct tagMSIVIEW
*view
, MSIRECORD
*rec
, UINT row
, BOOL temporary
)
1708 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1711 TRACE("%p %p %s\n", tv
, rec
, temporary
? "TRUE" : "FALSE" );
1713 /* check that the key is unique - can we find a matching row? */
1714 r
= table_validate_new( tv
, rec
, NULL
);
1715 if( r
!= ERROR_SUCCESS
)
1716 return ERROR_FUNCTION_FAILED
;
1719 row
= find_insert_index( tv
, rec
);
1721 r
= table_create_new_row( view
, &row
, temporary
);
1722 TRACE("insert_row returned %08x\n", r
);
1723 if( r
!= ERROR_SUCCESS
)
1726 /* shift the rows to make room for the new row */
1727 for (i
= tv
->table
->row_count
- 1; i
> row
; i
--)
1729 memmove(&(tv
->table
->data
[i
][0]),
1730 &(tv
->table
->data
[i
- 1][0]), tv
->row_size
);
1731 tv
->table
->data_persistent
[i
] = tv
->table
->data_persistent
[i
- 1];
1734 /* Re-set the persistence flag */
1735 tv
->table
->data_persistent
[row
] = !temporary
;
1736 return TABLE_set_row( view
, row
, rec
, (1<<tv
->num_cols
) - 1 );
1739 static UINT
TABLE_delete_row( struct tagMSIVIEW
*view
, UINT row
)
1741 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1742 UINT r
, num_rows
, num_cols
, i
;
1744 TRACE("%p %d\n", tv
, row
);
1747 return ERROR_INVALID_PARAMETER
;
1749 r
= TABLE_get_dimensions( view
, &num_rows
, &num_cols
);
1750 if ( r
!= ERROR_SUCCESS
)
1753 if ( row
>= num_rows
)
1754 return ERROR_FUNCTION_FAILED
;
1756 num_rows
= tv
->table
->row_count
;
1757 tv
->table
->row_count
--;
1759 /* reset the hash tables */
1760 for (i
= 0; i
< tv
->num_cols
; i
++)
1762 msi_free( tv
->columns
[i
].hash_table
);
1763 tv
->columns
[i
].hash_table
= NULL
;
1766 for (i
= row
+ 1; i
< num_rows
; i
++)
1768 memcpy(tv
->table
->data
[i
- 1], tv
->table
->data
[i
], tv
->row_size
);
1769 tv
->table
->data_persistent
[i
- 1] = tv
->table
->data_persistent
[i
];
1772 msi_free(tv
->table
->data
[num_rows
- 1]);
1774 return ERROR_SUCCESS
;
1777 static UINT
msi_table_update(struct tagMSIVIEW
*view
, MSIRECORD
*rec
, UINT row
)
1779 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1782 /* FIXME: MsiViewFetch should set rec index 0 to some ID that
1783 * sets the fetched record apart from other records
1787 return ERROR_INVALID_PARAMETER
;
1789 r
= msi_table_find_row(tv
, rec
, &new_row
, NULL
);
1790 if (r
!= ERROR_SUCCESS
)
1792 ERR("can't find row to modify\n");
1793 return ERROR_FUNCTION_FAILED
;
1796 /* the row cannot be changed */
1798 return ERROR_FUNCTION_FAILED
;
1800 return TABLE_set_row(view
, new_row
, rec
, (1 << tv
->num_cols
) - 1);
1803 static UINT
msi_table_assign(struct tagMSIVIEW
*view
, MSIRECORD
*rec
)
1805 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1809 return ERROR_INVALID_PARAMETER
;
1811 r
= msi_table_find_row(tv
, rec
, &row
, NULL
);
1812 if (r
== ERROR_SUCCESS
)
1813 return TABLE_set_row(view
, row
, rec
, (1 << tv
->num_cols
) - 1);
1815 return TABLE_insert_row( view
, rec
, -1, FALSE
);
1818 static UINT
msi_refresh_record( struct tagMSIVIEW
*view
, MSIRECORD
*rec
, UINT row
)
1823 r
= TABLE_get_row(view
, row
, &curr
);
1824 if (r
!= ERROR_SUCCESS
)
1827 /* Close the original record */
1828 MSI_CloseRecord(&rec
->hdr
);
1830 count
= MSI_RecordGetFieldCount(rec
);
1831 for (i
= 0; i
< count
; i
++)
1832 MSI_RecordCopyField(curr
, i
+ 1, rec
, i
+ 1);
1834 msiobj_release(&curr
->hdr
);
1835 return ERROR_SUCCESS
;
1838 static UINT
TABLE_modify( struct tagMSIVIEW
*view
, MSIMODIFY eModifyMode
,
1839 MSIRECORD
*rec
, UINT row
)
1841 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1842 UINT r
, frow
, column
;
1844 TRACE("%p %d %p\n", view
, eModifyMode
, rec
);
1846 switch (eModifyMode
)
1848 case MSIMODIFY_DELETE
:
1849 r
= TABLE_delete_row( view
, row
);
1851 case MSIMODIFY_VALIDATE_NEW
:
1852 r
= table_validate_new( tv
, rec
, &column
);
1853 if (r
!= ERROR_SUCCESS
)
1855 tv
->view
.error
= MSIDBERROR_DUPLICATEKEY
;
1856 tv
->view
.error_column
= tv
->columns
[column
].colname
;
1857 r
= ERROR_INVALID_DATA
;
1861 case MSIMODIFY_INSERT
:
1862 r
= table_validate_new( tv
, rec
, NULL
);
1863 if (r
!= ERROR_SUCCESS
)
1865 r
= TABLE_insert_row( view
, rec
, -1, FALSE
);
1868 case MSIMODIFY_INSERT_TEMPORARY
:
1869 r
= table_validate_new( tv
, rec
, NULL
);
1870 if (r
!= ERROR_SUCCESS
)
1872 r
= TABLE_insert_row( view
, rec
, -1, TRUE
);
1875 case MSIMODIFY_REFRESH
:
1876 r
= msi_refresh_record( view
, rec
, row
);
1879 case MSIMODIFY_UPDATE
:
1880 r
= msi_table_update( view
, rec
, row
);
1883 case MSIMODIFY_ASSIGN
:
1884 r
= msi_table_assign( view
, rec
);
1887 case MSIMODIFY_MERGE
:
1888 /* check row that matches this record */
1889 r
= msi_table_find_row( tv
, rec
, &frow
, &column
);
1890 if (r
!= ERROR_SUCCESS
)
1892 r
= table_validate_new( tv
, rec
, NULL
);
1893 if (r
== ERROR_SUCCESS
)
1894 r
= TABLE_insert_row( view
, rec
, -1, FALSE
);
1898 case MSIMODIFY_REPLACE
:
1899 case MSIMODIFY_VALIDATE
:
1900 case MSIMODIFY_VALIDATE_FIELD
:
1901 case MSIMODIFY_VALIDATE_DELETE
:
1902 FIXME("%p %d %p - mode not implemented\n", view
, eModifyMode
, rec
);
1903 r
= ERROR_CALL_NOT_IMPLEMENTED
;
1907 r
= ERROR_INVALID_DATA
;
1913 static UINT
TABLE_delete( struct tagMSIVIEW
*view
)
1915 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1917 TRACE("%p\n", view
);
1924 return ERROR_SUCCESS
;
1927 static UINT
TABLE_add_ref(struct tagMSIVIEW
*view
)
1929 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1931 TRACE( "%p, %ld\n", view
, tv
->table
->ref_count
);
1932 return InterlockedIncrement(&tv
->table
->ref_count
);
1935 static UINT
TABLE_remove_column(struct tagMSIVIEW
*view
, UINT number
)
1937 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1938 MSIRECORD
*rec
= NULL
;
1939 MSIVIEW
*columns
= NULL
;
1942 if (tv
->table
->col_count
!= number
)
1943 return ERROR_BAD_QUERY_SYNTAX
;
1945 if (tv
->table
->colinfo
[number
-1].type
& MSITYPE_TEMPORARY
)
1947 UINT size
= tv
->table
->colinfo
[number
-1].offset
;
1948 tv
->table
->col_count
--;
1949 tv
->table
->colinfo
= msi_realloc( tv
->table
->colinfo
, sizeof(*tv
->table
->colinfo
) * tv
->table
->col_count
);
1951 for (row
= 0; row
< tv
->table
->row_count
; row
++)
1952 tv
->table
->data
[row
] = msi_realloc( tv
->table
->data
[row
], size
);
1953 return ERROR_SUCCESS
;
1956 rec
= MSI_CreateRecord(2);
1958 return ERROR_OUTOFMEMORY
;
1960 MSI_RecordSetStringW(rec
, 1, tv
->name
);
1961 MSI_RecordSetInteger(rec
, 2, number
);
1963 r
= TABLE_CreateView(tv
->db
, L
"_Columns", &columns
);
1964 if (r
!= ERROR_SUCCESS
)
1966 msiobj_release(&rec
->hdr
);
1970 r
= msi_table_find_row((MSITABLEVIEW
*)columns
, rec
, &row
, NULL
);
1971 if (r
!= ERROR_SUCCESS
)
1974 r
= TABLE_delete_row(columns
, row
);
1975 if (r
!= ERROR_SUCCESS
)
1978 msi_update_table_columns(tv
->db
, tv
->name
);
1981 msiobj_release(&rec
->hdr
);
1982 columns
->ops
->delete(columns
);
1986 static UINT
TABLE_release(struct tagMSIVIEW
*view
)
1988 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1989 INT ref
= tv
->table
->ref_count
;
1993 TRACE("%p %d\n", view
, ref
);
1995 ref
= InterlockedDecrement(&tv
->table
->ref_count
);
1998 for (i
= tv
->table
->col_count
- 1; i
>= 0; i
--)
2000 if (tv
->table
->colinfo
[i
].type
& MSITYPE_TEMPORARY
)
2002 r
= TABLE_remove_column(view
, tv
->table
->colinfo
[i
].number
);
2003 if (r
!= ERROR_SUCCESS
)
2012 if (!tv
->table
->col_count
)
2014 list_remove(&tv
->table
->entry
);
2015 free_table(tv
->table
);
2023 static UINT
TABLE_add_column(struct tagMSIVIEW
*view
, LPCWSTR column
,
2024 INT type
, BOOL hold
)
2026 UINT i
, r
, table_id
, col_id
, size
, offset
;
2027 BOOL temporary
= type
& MSITYPE_TEMPORARY
;
2028 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2029 MSICOLUMNINFO
*colinfo
;
2031 if (temporary
&& !hold
&& !tv
->table
->ref_count
)
2032 return ERROR_SUCCESS
;
2034 if (!temporary
&& tv
->table
->col_count
&&
2035 tv
->table
->colinfo
[tv
->table
->col_count
-1].type
& MSITYPE_TEMPORARY
)
2036 return ERROR_BAD_QUERY_SYNTAX
;
2038 for (i
= 0; i
< tv
->table
->col_count
; i
++)
2040 if (!wcscmp(tv
->table
->colinfo
[i
].colname
, column
))
2041 return ERROR_BAD_QUERY_SYNTAX
;
2044 colinfo
= msi_realloc(tv
->table
->colinfo
, sizeof(*tv
->table
->colinfo
) * (tv
->table
->col_count
+ 1));
2046 return ERROR_OUTOFMEMORY
;
2047 tv
->table
->colinfo
= colinfo
;
2049 r
= msi_string2id( tv
->db
->strings
, tv
->name
, -1, &table_id
);
2050 if (r
!= ERROR_SUCCESS
)
2052 col_id
= msi_add_string( tv
->db
->strings
, column
, -1, !temporary
);
2054 colinfo
[tv
->table
->col_count
].tablename
= msi_string_lookup( tv
->db
->strings
, table_id
, NULL
);
2055 colinfo
[tv
->table
->col_count
].number
= tv
->table
->col_count
+ 1;
2056 colinfo
[tv
->table
->col_count
].colname
= msi_string_lookup( tv
->db
->strings
, col_id
, NULL
);
2057 colinfo
[tv
->table
->col_count
].type
= type
;
2058 colinfo
[tv
->table
->col_count
].offset
= 0;
2059 colinfo
[tv
->table
->col_count
].hash_table
= NULL
;
2060 tv
->table
->col_count
++;
2062 table_calc_column_offsets( tv
->db
, tv
->table
->colinfo
, tv
->table
->col_count
);
2064 size
= msi_table_get_row_size( tv
->db
, tv
->table
->colinfo
, tv
->table
->col_count
, LONG_STR_BYTES
);
2065 offset
= tv
->table
->colinfo
[tv
->table
->col_count
- 1].offset
;
2066 for (i
= 0; i
< tv
->table
->row_count
; i
++)
2068 BYTE
*data
= msi_realloc( tv
->table
->data
[i
], size
);
2071 tv
->table
->col_count
--;
2072 return ERROR_OUTOFMEMORY
;
2075 tv
->table
->data
[i
] = data
;
2076 memset(data
+ offset
, 0, size
- offset
);
2084 rec
= MSI_CreateRecord(4);
2087 tv
->table
->col_count
--;
2088 return ERROR_OUTOFMEMORY
;
2091 MSI_RecordSetStringW(rec
, 1, tv
->name
);
2092 MSI_RecordSetInteger(rec
, 2, tv
->table
->col_count
);
2093 MSI_RecordSetStringW(rec
, 3, column
);
2094 MSI_RecordSetInteger(rec
, 4, type
);
2096 r
= TABLE_CreateView(tv
->db
, L
"_Columns", &columns
);
2097 if (r
!= ERROR_SUCCESS
)
2099 tv
->table
->col_count
--;
2100 msiobj_release(&rec
->hdr
);
2104 r
= TABLE_insert_row(columns
, rec
, -1, FALSE
);
2105 columns
->ops
->delete(columns
);
2106 msiobj_release(&rec
->hdr
);
2107 if (r
!= ERROR_SUCCESS
)
2109 tv
->table
->col_count
--;
2114 TABLE_add_ref(view
);
2115 return ERROR_SUCCESS
;
2118 static UINT
TABLE_drop(struct tagMSIVIEW
*view
)
2120 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2121 MSIVIEW
*tables
= NULL
;
2122 MSIRECORD
*rec
= NULL
;
2126 TRACE("dropping table %s\n", debugstr_w(tv
->name
));
2128 for (i
= tv
->table
->col_count
- 1; i
>= 0; i
--)
2130 r
= TABLE_remove_column(view
, tv
->table
->colinfo
[i
].number
);
2131 if (r
!= ERROR_SUCCESS
)
2135 rec
= MSI_CreateRecord(1);
2137 return ERROR_OUTOFMEMORY
;
2139 MSI_RecordSetStringW(rec
, 1, tv
->name
);
2141 r
= TABLE_CreateView(tv
->db
, L
"_Tables", &tables
);
2142 if (r
!= ERROR_SUCCESS
)
2144 msiobj_release(&rec
->hdr
);
2148 r
= msi_table_find_row((MSITABLEVIEW
*)tables
, rec
, &row
, NULL
);
2149 if (r
!= ERROR_SUCCESS
)
2152 r
= TABLE_delete_row(tables
, row
);
2153 if (r
!= ERROR_SUCCESS
)
2156 list_remove(&tv
->table
->entry
);
2157 free_table(tv
->table
);
2160 msiobj_release(&rec
->hdr
);
2161 tables
->ops
->delete(tables
);
2166 static const MSIVIEWOPS table_ops
=
2178 TABLE_get_dimensions
,
2179 TABLE_get_column_info
,
2189 UINT
TABLE_CreateView( MSIDATABASE
*db
, LPCWSTR name
, MSIVIEW
**view
)
2194 TRACE("%p %s %p\n", db
, debugstr_w(name
), view
);
2196 if ( !wcscmp( name
, L
"_Streams" ) )
2197 return STREAMS_CreateView( db
, view
);
2198 else if ( !wcscmp( name
, L
"_Storages" ) )
2199 return STORAGES_CreateView( db
, view
);
2201 sz
= FIELD_OFFSET( MSITABLEVIEW
, name
[lstrlenW( name
) + 1] );
2202 tv
= msi_alloc_zero( sz
);
2204 return ERROR_FUNCTION_FAILED
;
2206 r
= get_table( db
, name
, &tv
->table
);
2207 if( r
!= ERROR_SUCCESS
)
2210 WARN("table not found\n");
2214 TRACE("table %p found with %d columns\n", tv
->table
, tv
->table
->col_count
);
2216 /* fill the structure */
2217 tv
->view
.ops
= &table_ops
;
2219 tv
->columns
= tv
->table
->colinfo
;
2220 tv
->num_cols
= tv
->table
->col_count
;
2221 tv
->row_size
= msi_table_get_row_size( db
, tv
->table
->colinfo
, tv
->table
->col_count
, LONG_STR_BYTES
);
2223 TRACE("%s one row is %d bytes\n", debugstr_w(name
), tv
->row_size
);
2225 *view
= (MSIVIEW
*) tv
;
2226 lstrcpyW( tv
->name
, name
);
2228 return ERROR_SUCCESS
;
2231 static WCHAR
* create_key_string(MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
2233 DWORD i
, p
, len
, key_len
= 0;
2236 for (i
= 0; i
< tv
->num_cols
; i
++)
2238 if (!(tv
->columns
[i
].type
& MSITYPE_KEY
))
2240 if (MSI_RecordGetStringW( rec
, i
+1, NULL
, &len
) == ERROR_SUCCESS
)
2245 key
= msi_alloc( key_len
* sizeof(WCHAR
) );
2250 for (i
= 0; i
< tv
->num_cols
; i
++)
2252 if (!(tv
->columns
[i
].type
& MSITYPE_KEY
))
2257 if (MSI_RecordGetStringW( rec
, i
+1, key
+ p
, &len
) == ERROR_SUCCESS
)
2263 static UINT
msi_record_stream_name( const MSITABLEVIEW
*tv
, MSIRECORD
*rec
, LPWSTR name
, DWORD
*len
)
2268 l
= wcslen( tv
->name
);
2269 if (name
&& *len
> l
)
2270 memcpy(name
, tv
->name
, l
* sizeof(WCHAR
));
2273 for ( i
= 0; i
< tv
->num_cols
; i
++ )
2275 if (!(tv
->columns
[i
].type
& MSITYPE_KEY
))
2278 if (name
&& *len
> p
+ 1)
2282 l
= (*len
> p
? *len
- p
: 0);
2283 r
= MSI_RecordGetStringW( rec
, i
+ 1, name
? name
+ p
: NULL
, &l
);
2284 if (r
!= ERROR_SUCCESS
)
2289 if (name
&& *len
> p
)
2293 return ERROR_SUCCESS
;
2296 static UINT
TransformView_fetch_int( MSIVIEW
*view
, UINT row
, UINT col
, UINT
*val
)
2298 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2300 if (!tv
->table
|| col
> tv
->table
->col_count
)
2303 return ERROR_SUCCESS
;
2305 return TABLE_fetch_int( view
, row
, col
, val
);
2308 static UINT
TransformView_fetch_stream( MSIVIEW
*view
, UINT row
, UINT col
, IStream
**stm
)
2310 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2312 if (!tv
->table
|| col
> tv
->table
->col_count
)
2315 return ERROR_SUCCESS
;
2317 return TABLE_fetch_stream( view
, row
, col
, stm
);
2320 static UINT
TransformView_set_row( MSIVIEW
*view
, UINT row
, MSIRECORD
*rec
, UINT mask
)
2322 static const WCHAR query_pfx
[] =
2323 L
"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`, `Data`, `Current`) VALUES (1, '";
2325 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2326 WCHAR buf
[256], *query
;
2333 if (!wcscmp( tv
->name
, L
"_Columns" ))
2335 ERR( "trying to modify existing column\n" );
2336 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2339 if (!wcscmp( tv
->name
, L
"_Tables" ))
2341 ERR( "trying to modify existing table\n" );
2342 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2345 key
= create_key_string( tv
, rec
);
2347 return ERROR_OUTOFMEMORY
;
2349 r
= msi_view_get_row( tv
->db
, view
, row
, &old_rec
);
2350 if (r
!= ERROR_SUCCESS
)
2353 for (i
= 0; i
< tv
->num_cols
; i
++)
2355 if (!(mask
& (1 << i
)))
2357 if (tv
->columns
[i
].type
& MSITYPE_KEY
)
2360 qlen
= p
= ARRAY_SIZE( query_pfx
) - 1;
2361 qlen
+= wcslen( tv
->name
) + 3; /* strlen("','") */
2362 qlen
+= wcslen( tv
->columns
[i
].colname
) + 3;
2363 qlen
+= wcslen( key
) + 3;
2364 if (MSITYPE_IS_BINARY( tv
->columns
[i
].type
))
2365 r
= msi_record_stream_name( tv
, rec
, NULL
, &len
);
2367 r
= MSI_RecordGetStringW( rec
, i
+ 1, NULL
, &len
);
2368 if (r
!= ERROR_SUCCESS
)
2371 msiobj_release( &old_rec
->hdr
);
2376 if (old_rec
&& (r
= MSI_RecordGetStringW( old_rec
, i
+1, NULL
, &len
)))
2378 msiobj_release( &old_rec
->hdr
);
2382 qlen
+= len
+ 3; /* strlen("')") + 1 */
2384 if (qlen
> ARRAY_SIZE(buf
))
2386 query
= msi_alloc( qlen
* sizeof(WCHAR
) );
2390 msiobj_release( &old_rec
->hdr
);
2392 return ERROR_OUTOFMEMORY
;
2400 memcpy( query
, query_pfx
, p
* sizeof(WCHAR
) );
2401 len
= wcslen( tv
->name
);
2402 memcpy( query
+ p
, tv
->name
, len
* sizeof(WCHAR
) );
2407 len
= wcslen( tv
->columns
[i
].colname
);
2408 memcpy( query
+ p
, tv
->columns
[i
].colname
, len
* sizeof(WCHAR
) );
2413 len
= wcslen( key
);
2414 memcpy( query
+ p
, key
, len
* sizeof(WCHAR
) );
2420 if (MSITYPE_IS_BINARY( tv
->columns
[i
].type
))
2421 msi_record_stream_name( tv
, rec
, query
+ p
, &len
);
2423 MSI_RecordGetStringW( rec
, i
+ 1, query
+ p
, &len
);
2431 MSI_RecordGetStringW( old_rec
, i
+ 1, query
+ p
, &len
);
2438 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2441 if (r
!= ERROR_SUCCESS
)
2444 msiobj_release( &old_rec
->hdr
);
2449 r
= MSI_ViewExecute( q
, NULL
);
2450 msiobj_release( &q
->hdr
);
2451 if (r
!= ERROR_SUCCESS
)
2454 msiobj_release( &old_rec
->hdr
);
2461 msiobj_release( &old_rec
->hdr
);
2463 return ERROR_SUCCESS
;
2466 static UINT
TransformView_create_table( MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
2468 static const WCHAR query_fmt
[] =
2469 L
"INSERT INTO `_TransformView` (`Table`, `Column`, `new`) VALUES ('%s', 'CREATE', 1)";
2471 WCHAR buf
[256], *query
= buf
;
2477 name
= msi_record_get_string( rec
, 1, &len
);
2479 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2481 len
= _snwprintf( NULL
, 0, query_fmt
, name
) + 1;
2482 if (len
> ARRAY_SIZE(buf
))
2484 query
= msi_alloc( len
* sizeof(WCHAR
) );
2486 return ERROR_OUTOFMEMORY
;
2488 swprintf( query
, len
, query_fmt
, name
);
2490 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2493 if (r
!= ERROR_SUCCESS
)
2496 r
= MSI_ViewExecute( q
, NULL
);
2497 msiobj_release( &q
->hdr
);
2501 static UINT
TransformView_add_column( MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
2503 static const WCHAR query_pfx
[] =
2504 L
"INSERT INTO `_TransformView` (`new`, `Table`, `Current`, `Column`, `Data`) VALUES (1, '";
2506 WCHAR buf
[256], *query
= buf
;
2511 qlen
= p
= wcslen( query_pfx
);
2512 for (i
= 1; i
<= 4; i
++)
2514 r
= MSI_RecordGetStringW( rec
, i
, NULL
, &len
);
2515 if (r
!= ERROR_SUCCESS
)
2517 qlen
+= len
+ 3; /* strlen( "','" ) */
2520 if (qlen
> ARRAY_SIZE(buf
))
2522 query
= msi_alloc( len
* sizeof(WCHAR
) );
2525 return ERROR_OUTOFMEMORY
;
2528 memcpy( query
, query_pfx
, p
* sizeof(WCHAR
) );
2529 for (i
= 1; i
<= 4; i
++)
2532 MSI_RecordGetStringW( rec
, i
, query
+ p
, &len
);
2544 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2547 if (r
!= ERROR_SUCCESS
)
2550 r
= MSI_ViewExecute( q
, NULL
);
2551 msiobj_release( &q
->hdr
);
2555 static UINT
TransformView_insert_row( MSIVIEW
*view
, MSIRECORD
*rec
, UINT row
, BOOL temporary
)
2557 static const WCHAR query_fmt
[] =
2558 L
"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`) VALUES (1, '%s', 'INSERT', '%s')";
2560 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2561 WCHAR buf
[256], *query
= buf
;
2567 if (!wcscmp(tv
->name
, L
"_Tables"))
2568 return TransformView_create_table( tv
, rec
);
2570 if (!wcscmp(tv
->name
, L
"_Columns"))
2571 return TransformView_add_column( tv
, rec
);
2573 key
= create_key_string( tv
, rec
);
2575 return ERROR_OUTOFMEMORY
;
2577 len
= _snwprintf( NULL
, 0, query_fmt
, tv
->name
, key
) + 1;
2578 if (len
> ARRAY_SIZE(buf
))
2580 query
= msi_alloc( len
* sizeof(WCHAR
) );
2584 return ERROR_OUTOFMEMORY
;
2587 swprintf( query
, len
, query_fmt
, tv
->name
, key
);
2590 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2593 if (r
!= ERROR_SUCCESS
)
2596 r
= MSI_ViewExecute( q
, NULL
);
2597 msiobj_release( &q
->hdr
);
2598 if (r
!= ERROR_SUCCESS
)
2601 return TransformView_set_row( view
, row
, rec
, ~0 );
2604 static UINT
TransformView_drop_table( MSITABLEVIEW
*tv
, UINT row
)
2606 static const WCHAR query_pfx
[] = L
"INSERT INTO `_TransformView` ( `new`, `Table`, `Column` ) VALUES ( 1, '";
2607 static const WCHAR query_sfx
[] = L
"', 'DROP' )";
2609 WCHAR buf
[256], *query
= buf
;
2610 UINT r
, table_id
, len
;
2615 r
= TABLE_fetch_int( &tv
->view
, row
, 1, &table_id
);
2616 if (r
!= ERROR_SUCCESS
)
2619 table
= msi_string_lookup( tv
->db
->strings
, table_id
, &table_len
);
2621 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2623 len
= ARRAY_SIZE(query_pfx
) - 1 + table_len
+ ARRAY_SIZE(query_sfx
);
2624 if (len
> ARRAY_SIZE(buf
))
2626 query
= msi_alloc( len
* sizeof(WCHAR
) );
2628 return ERROR_OUTOFMEMORY
;
2631 memcpy( query
, query_pfx
, ARRAY_SIZE(query_pfx
) * sizeof(WCHAR
) );
2632 len
= ARRAY_SIZE(query_pfx
) - 1;
2633 memcpy( query
+ len
, table
, table_len
* sizeof(WCHAR
) );
2635 memcpy( query
+ len
, query_sfx
, ARRAY_SIZE(query_sfx
) * sizeof(WCHAR
) );
2637 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2640 if (r
!= ERROR_SUCCESS
)
2643 r
= MSI_ViewExecute( q
, NULL
);
2644 msiobj_release( &q
->hdr
);
2648 static UINT
TransformView_delete_row( MSIVIEW
*view
, UINT row
)
2650 static const WCHAR query_pfx
[] = L
"INSERT INTO `_TransformView` ( `new`, `Table`, `Column`, `Row`) VALUES ( 1, '";
2651 static const WCHAR query_column
[] = L
"', 'DELETE', '";
2652 static const WCHAR query_sfx
[] = L
"')";
2654 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2655 WCHAR
*key
, buf
[256], *query
= buf
;
2656 UINT r
, len
, name_len
, key_len
;
2660 if (!wcscmp( tv
->name
, L
"_Columns" ))
2662 ERR("trying to remove column\n");
2663 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2666 if (!wcscmp( tv
->name
, L
"_Tables" ))
2667 return TransformView_drop_table( tv
, row
);
2669 r
= msi_view_get_row( tv
->db
, view
, row
, &rec
);
2670 if (r
!= ERROR_SUCCESS
)
2673 key
= create_key_string( tv
, rec
);
2674 msiobj_release( &rec
->hdr
);
2676 return ERROR_OUTOFMEMORY
;
2678 name_len
= wcslen( tv
->name
);
2679 key_len
= wcslen( key
);
2680 len
= ARRAY_SIZE(query_pfx
) + name_len
+ ARRAY_SIZE(query_column
) + key_len
+ ARRAY_SIZE(query_sfx
) - 2;
2681 if (len
> ARRAY_SIZE(buf
))
2683 query
= msi_alloc( len
* sizeof(WCHAR
) );
2688 return ERROR_OUTOFMEMORY
;
2692 memcpy( query
, query_pfx
, ARRAY_SIZE(query_pfx
) * sizeof(WCHAR
) );
2693 len
= ARRAY_SIZE(query_pfx
) - 1;
2694 memcpy( query
+ len
, tv
->name
, name_len
* sizeof(WCHAR
) );
2696 memcpy( query
+ len
, query_column
, ARRAY_SIZE(query_column
) * sizeof(WCHAR
) );
2697 len
+= ARRAY_SIZE(query_column
) - 1;
2698 memcpy( query
+ len
, key
, key_len
* sizeof(WCHAR
) );
2700 memcpy( query
+ len
, query_sfx
, ARRAY_SIZE(query_sfx
) * sizeof(WCHAR
) );
2703 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2706 if (r
!= ERROR_SUCCESS
)
2709 r
= MSI_ViewExecute( q
, NULL
);
2710 msiobj_release( &q
->hdr
);
2714 static UINT
TransformView_execute( MSIVIEW
*view
, MSIRECORD
*record
)
2716 return ERROR_SUCCESS
;
2719 static UINT
TransformView_close( MSIVIEW
*view
)
2721 return ERROR_SUCCESS
;
2724 static UINT
TransformView_get_dimensions( MSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
2726 return TABLE_get_dimensions( view
, rows
, cols
);
2729 static UINT
TransformView_get_column_info( MSIVIEW
*view
, UINT n
, LPCWSTR
*name
, UINT
*type
,
2730 BOOL
*temporary
, LPCWSTR
*table_name
)
2732 return TABLE_get_column_info( view
, n
, name
, type
, temporary
, table_name
);
2735 static UINT
TransformView_delete( MSIVIEW
*view
)
2737 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2738 if (!tv
->table
|| tv
->columns
!= tv
->table
->colinfo
)
2739 msi_free( tv
->columns
);
2740 return TABLE_delete( view
);
2743 static const MSIVIEWOPS transform_view_ops
=
2745 TransformView_fetch_int
,
2746 TransformView_fetch_stream
,
2750 TransformView_set_row
,
2751 TransformView_insert_row
,
2752 TransformView_delete_row
,
2753 TransformView_execute
,
2754 TransformView_close
,
2755 TransformView_get_dimensions
,
2756 TransformView_get_column_info
,
2758 TransformView_delete
,
2766 UINT
TransformView_Create( MSIDATABASE
*db
, string_table
*st
, LPCWSTR name
, MSIVIEW
**view
)
2768 static const WCHAR query_pfx
[] = L
"SELECT `Column`, `Data`, `Current` FROM `_TransformView` WHERE `Table`='";
2769 static const WCHAR query_sfx
[] = L
"' AND `Row` IS NULL AND `Current` IS NOT NULL AND `new` = 1";
2771 WCHAR buf
[256], *query
= buf
;
2772 UINT r
, len
, name_len
, size
, add_col
;
2773 MSICOLUMNINFO
*colinfo
;
2778 name_len
= wcslen( name
);
2780 r
= TABLE_CreateView( db
, name
, view
);
2781 if (r
== ERROR_INVALID_PARAMETER
)
2783 /* table does not exist */
2784 size
= FIELD_OFFSET( MSITABLEVIEW
, name
[name_len
+ 1] );
2785 tv
= msi_alloc_zero( size
);
2787 return ERROR_OUTOFMEMORY
;
2790 memcpy( tv
->name
, name
, name_len
* sizeof(WCHAR
) );
2791 *view
= (MSIVIEW
*)tv
;
2793 else if (r
!= ERROR_SUCCESS
)
2799 tv
= (MSITABLEVIEW
*)*view
;
2802 tv
->view
.ops
= &transform_view_ops
;
2804 len
= ARRAY_SIZE(query_pfx
) + name_len
+ ARRAY_SIZE(query_sfx
) - 1;
2805 if (len
> ARRAY_SIZE(buf
))
2807 query
= msi_alloc( len
* sizeof(WCHAR
) );
2811 return ERROR_OUTOFMEMORY
;
2814 memcpy( query
, query_pfx
, ARRAY_SIZE(query_pfx
) * sizeof(WCHAR
) );
2815 len
= ARRAY_SIZE(query_pfx
) - 1;
2816 memcpy( query
+ len
, name
, name_len
* sizeof(WCHAR
) );
2818 memcpy( query
+ len
, query_sfx
, ARRAY_SIZE(query_sfx
) * sizeof(WCHAR
) );
2820 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2823 if (r
!= ERROR_SUCCESS
)
2829 r
= MSI_ViewExecute( q
, NULL
);
2830 if (r
!= ERROR_SUCCESS
)
2836 r
= q
->view
->ops
->get_dimensions( q
->view
, &add_col
, NULL
);
2837 if (r
!= ERROR_SUCCESS
)
2840 msiobj_release( &q
->hdr
);
2847 msiobj_release( &q
->hdr
);
2848 return ERROR_SUCCESS
;
2851 colinfo
= msi_alloc_zero( (add_col
+ tv
->num_cols
) * sizeof(*colinfo
) );
2855 msiobj_release( &q
->hdr
);
2860 while (MSI_ViewFetch( q
, &rec
) == ERROR_SUCCESS
)
2863 const WCHAR
*name
= msi_record_get_string( rec
, 1, &name_len
);
2864 const WCHAR
*type
= msi_record_get_string( rec
, 2, NULL
);
2867 idx
= _wtoi( msi_record_get_string(rec
, 3, NULL
) );
2868 colinfo
[idx
- 1].number
= idx
;
2869 colinfo
[idx
- 1].type
= _wtoi( type
);
2871 r
= msi_string2id( st
, name
, name_len
, &name_id
);
2872 if (r
== ERROR_SUCCESS
)
2873 colinfo
[idx
- 1].colname
= msi_string_lookup( st
, name_id
, NULL
);
2875 ERR( "column name %s is not defined in strings table\n", wine_dbgstr_w(name
) );
2876 msiobj_release( &rec
->hdr
);
2879 msiobj_release( &q
->hdr
);
2881 memcpy( colinfo
, tv
->columns
, tv
->num_cols
* sizeof(*colinfo
) );
2882 tv
->columns
= colinfo
;
2883 tv
->num_cols
+= add_col
;
2884 return ERROR_SUCCESS
;
2887 UINT
MSI_CommitTables( MSIDATABASE
*db
)
2889 UINT r
, bytes_per_strref
;
2891 MSITABLE
*table
= NULL
;
2895 r
= msi_save_string_table( db
->strings
, db
->storage
, &bytes_per_strref
);
2896 if( r
!= ERROR_SUCCESS
)
2898 WARN("failed to save string table r=%08x\n",r
);
2902 LIST_FOR_EACH_ENTRY( table
, &db
->tables
, MSITABLE
, entry
)
2904 r
= save_table( db
, table
, bytes_per_strref
);
2905 if( r
!= ERROR_SUCCESS
)
2907 WARN("failed to save table %s (r=%08x)\n",
2908 debugstr_w(table
->name
), r
);
2913 hr
= IStorage_Commit( db
->storage
, 0 );
2916 WARN( "failed to commit changes %#lx\n", hr
);
2917 r
= ERROR_FUNCTION_FAILED
;
2922 MSICONDITION
MSI_DatabaseIsTablePersistent( MSIDATABASE
*db
, LPCWSTR table
)
2927 TRACE("%p %s\n", db
, debugstr_w(table
));
2930 return MSICONDITION_ERROR
;
2932 r
= get_table( db
, table
, &t
);
2933 if (r
!= ERROR_SUCCESS
)
2934 return MSICONDITION_NONE
;
2936 return t
->persistent
;
2939 static UINT
read_raw_int(const BYTE
*data
, UINT col
, UINT bytes
)
2943 for (i
= 0; i
< bytes
; i
++)
2944 ret
+= (data
[col
+ i
] << i
* 8);
2949 static UINT
msi_record_encoded_stream_name( const MSITABLEVIEW
*tv
, MSIRECORD
*rec
, LPWSTR
*pstname
)
2955 TRACE("%p %p\n", tv
, rec
);
2957 r
= msi_record_stream_name( tv
, rec
, NULL
, &len
);
2958 if (r
!= ERROR_SUCCESS
)
2962 name
= msi_alloc( len
* sizeof(WCHAR
) );
2964 return ERROR_OUTOFMEMORY
;
2966 r
= msi_record_stream_name( tv
, rec
, name
, &len
);
2967 if (r
!= ERROR_SUCCESS
)
2973 *pstname
= encode_streamname( FALSE
, name
);
2975 return ERROR_SUCCESS
;
2978 static MSIRECORD
*msi_get_transform_record( const MSITABLEVIEW
*tv
, const string_table
*st
,
2979 IStorage
*stg
, const BYTE
*rawdata
, UINT bytes_per_strref
)
2981 UINT i
, val
, ofs
= 0;
2983 MSICOLUMNINFO
*columns
= tv
->columns
;
2986 mask
= rawdata
[0] | (rawdata
[1] << 8);
2989 rec
= MSI_CreateRecord( tv
->num_cols
);
2994 for( i
=0; i
<tv
->num_cols
; i
++ )
2996 if ( (mask
&1) && (i
>=(mask
>>8)) )
2998 /* all keys must be present */
2999 if ( (~mask
&1) && (~columns
[i
].type
& MSITYPE_KEY
) && ((1<<i
) & ~mask
) )
3002 if( MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
3005 IStream
*stm
= NULL
;
3008 ofs
+= bytes_per_column( tv
->db
, &columns
[i
], bytes_per_strref
);
3010 r
= msi_record_encoded_stream_name( tv
, rec
, &encname
);
3011 if ( r
!= ERROR_SUCCESS
)
3013 msiobj_release( &rec
->hdr
);
3016 r
= IStorage_OpenStream( stg
, encname
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
3017 if ( r
!= ERROR_SUCCESS
)
3019 msiobj_release( &rec
->hdr
);
3020 msi_free( encname
);
3024 MSI_RecordSetStream( rec
, i
+1, stm
);
3025 TRACE(" field %d [%s]\n", i
+1, debugstr_w(encname
));
3026 msi_free( encname
);
3028 else if( columns
[i
].type
& MSITYPE_STRING
)
3033 val
= read_raw_int(rawdata
, ofs
, bytes_per_strref
);
3034 sval
= msi_string_lookup( st
, val
, &len
);
3035 msi_record_set_string( rec
, i
+1, sval
, len
);
3036 TRACE(" field %d [%s]\n", i
+1, debugstr_wn(sval
, len
));
3037 ofs
+= bytes_per_strref
;
3041 UINT n
= bytes_per_column( tv
->db
, &columns
[i
], bytes_per_strref
);
3045 val
= read_raw_int(rawdata
, ofs
, n
);
3047 MSI_RecordSetInteger( rec
, i
+1, val
-0x8000 );
3048 TRACE(" field %d [0x%04x]\n", i
+1, val
);
3051 val
= read_raw_int(rawdata
, ofs
, n
);
3053 MSI_RecordSetInteger( rec
, i
+1, val
^0x80000000 );
3054 TRACE(" field %d [0x%08x]\n", i
+1, val
);
3057 ERR("oops - unknown column width %d\n", n
);
3066 static void dump_table( const string_table
*st
, const USHORT
*rawdata
, UINT rawsize
)
3069 for (i
= 0; i
< rawsize
/ 2; i
++)
3072 const WCHAR
*sval
= msi_string_lookup( st
, rawdata
[i
], &len
);
3073 MESSAGE(" %04x %s\n", rawdata
[i
], debugstr_wn(sval
, len
) );
3077 static UINT
* msi_record_to_row( const MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
3081 data
= msi_alloc( tv
->num_cols
*sizeof (UINT
) );
3082 for( i
=0; i
<tv
->num_cols
; i
++ )
3086 if ( ~tv
->columns
[i
].type
& MSITYPE_KEY
)
3089 /* turn the transform column value into a row value */
3090 if ( ( tv
->columns
[i
].type
& MSITYPE_STRING
) &&
3091 ! MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
3094 const WCHAR
*str
= msi_record_get_string( rec
, i
+1, &len
);
3097 r
= msi_string2id( tv
->db
->strings
, str
, len
, &data
[i
] );
3099 /* if there's no matching string in the string table,
3100 these keys can't match any record, so fail now. */
3101 if (r
!= ERROR_SUCCESS
)
3111 if (int_to_table_storage( tv
, i
+ 1, MSI_RecordGetInteger( rec
, i
+ 1 ), &data
[i
] ))
3121 static UINT
msi_row_matches( MSITABLEVIEW
*tv
, UINT row
, const UINT
*data
, UINT
*column
)
3123 UINT i
, r
, x
, ret
= ERROR_FUNCTION_FAILED
;
3125 for( i
=0; i
<tv
->num_cols
; i
++ )
3127 if ( ~tv
->columns
[i
].type
& MSITYPE_KEY
)
3130 /* turn the transform column value into a row value */
3131 r
= TABLE_fetch_int( &tv
->view
, row
, i
+1, &x
);
3132 if ( r
!= ERROR_SUCCESS
)
3134 ERR("TABLE_fetch_int shouldn't fail here\n");
3138 /* if this key matches, move to the next column */
3141 ret
= ERROR_FUNCTION_FAILED
;
3144 if (column
) *column
= i
;
3145 ret
= ERROR_SUCCESS
;
3150 static UINT
msi_table_find_row( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT
*row
, UINT
*column
)
3152 UINT i
, r
= ERROR_FUNCTION_FAILED
, *data
;
3154 data
= msi_record_to_row( tv
, rec
);
3157 for( i
= 0; i
< tv
->table
->row_count
; i
++ )
3159 r
= msi_row_matches( tv
, i
, data
, column
);
3160 if( r
== ERROR_SUCCESS
)
3176 static UINT
msi_table_load_transform( MSIDATABASE
*db
, IStorage
*stg
,
3177 string_table
*st
, TRANSFORMDATA
*transform
,
3178 UINT bytes_per_strref
, int err_cond
)
3180 BYTE
*rawdata
= NULL
;
3181 MSITABLEVIEW
*tv
= NULL
;
3182 UINT r
, n
, sz
, i
, mask
, num_cols
, colcol
= 0, rawsize
= 0;
3183 MSIRECORD
*rec
= NULL
;
3188 return ERROR_SUCCESS
;
3190 name
= transform
->name
;
3193 TRACE("%p %p %p %s\n", db
, stg
, st
, debugstr_w(name
) );
3195 /* read the transform data */
3196 read_stream_data( stg
, name
, TRUE
, &rawdata
, &rawsize
);
3199 TRACE("table %s empty\n", debugstr_w(name
) );
3200 return ERROR_INVALID_TABLE
;
3203 /* create a table view */
3204 if ( err_cond
& MSITRANSFORM_ERROR_VIEWTRANSFORM
)
3205 r
= TransformView_Create( db
, st
, name
, (MSIVIEW
**) &tv
);
3207 r
= TABLE_CreateView( db
, name
, (MSIVIEW
**) &tv
);
3208 if( r
!= ERROR_SUCCESS
)
3211 r
= tv
->view
.ops
->execute( &tv
->view
, NULL
);
3212 if( r
!= ERROR_SUCCESS
)
3215 TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
3216 debugstr_w(name
), tv
->num_cols
, tv
->row_size
, rawsize
);
3218 /* interpret the data */
3219 for (n
= 0; n
< rawsize
;)
3221 mask
= rawdata
[n
] | (rawdata
[n
+ 1] << 8);
3225 * if the low bit is set, columns are continuous and
3226 * the number of columns is specified in the high byte
3229 num_cols
= mask
>> 8;
3230 if (num_cols
> tv
->num_cols
)
3232 ERR("excess columns in transform: %u > %u\n", num_cols
, tv
->num_cols
);
3236 for (i
= 0; i
< num_cols
; i
++)
3238 if( (tv
->columns
[i
].type
& MSITYPE_STRING
) &&
3239 ! MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
3240 sz
+= bytes_per_strref
;
3242 sz
+= bytes_per_column( tv
->db
, &tv
->columns
[i
], bytes_per_strref
);
3248 * If the low bit is not set, mask is a bitmask.
3249 * Excepting for key fields, which are always present,
3250 * each bit indicates that a field is present in the transform record.
3252 * mask == 0 is a special case ... only the keys will be present
3253 * and it means that this row should be deleted.
3256 num_cols
= tv
->num_cols
;
3257 for (i
= 0; i
< num_cols
; i
++)
3259 if ((tv
->columns
[i
].type
& MSITYPE_KEY
) || ((1 << i
) & mask
))
3261 if ((tv
->columns
[i
].type
& MSITYPE_STRING
) &&
3262 !MSITYPE_IS_BINARY(tv
->columns
[i
].type
))
3263 sz
+= bytes_per_strref
;
3265 sz
+= bytes_per_column( tv
->db
, &tv
->columns
[i
], bytes_per_strref
);
3270 /* check we didn't run of the end of the table */
3271 if (n
+ sz
> rawsize
)
3274 dump_table( st
, (USHORT
*)rawdata
, rawsize
);
3278 rec
= msi_get_transform_record( tv
, st
, stg
, &rawdata
[n
], bytes_per_strref
);
3283 UINT number
= MSI_NULL_INTEGER
;
3286 if (!wcscmp( name
, L
"_Columns" ))
3288 MSI_RecordGetStringW( rec
, 1, table
, &sz
);
3289 number
= MSI_RecordGetInteger( rec
, 2 );
3292 * Native msi seems writes nul into the Number (2nd) column of
3293 * the _Columns table when there are new columns
3295 if ( number
== MSI_NULL_INTEGER
)
3297 /* reset the column number on a new table */
3298 if (wcscmp( coltable
, table
))
3301 lstrcpyW( coltable
, table
);
3304 /* fix nul column numbers */
3305 MSI_RecordSetInteger( rec
, 2, ++colcol
);
3309 if (TRACE_ON(msidb
)) dump_record( rec
);
3312 r
= msi_table_find_row( tv
, rec
, &row
, NULL
);
3314 r
= ERROR_FUNCTION_FAILED
;
3315 if (r
== ERROR_SUCCESS
)
3319 TRACE("deleting row [%d]:\n", row
);
3320 r
= tv
->view
.ops
->delete_row( &tv
->view
, row
);
3321 if (r
!= ERROR_SUCCESS
)
3322 WARN("failed to delete row %u\n", r
);
3326 TRACE("modifying full row [%d]:\n", row
);
3327 r
= tv
->view
.ops
->set_row( &tv
->view
, row
, rec
, (1 << tv
->num_cols
) - 1 );
3328 if (r
!= ERROR_SUCCESS
)
3329 WARN("failed to modify row %u\n", r
);
3333 TRACE("modifying masked row [%d]:\n", row
);
3334 r
= tv
->view
.ops
->set_row( &tv
->view
, row
, rec
, mask
);
3335 if (r
!= ERROR_SUCCESS
)
3336 WARN("failed to modify row %u\n", r
);
3341 TRACE("inserting row\n");
3342 r
= tv
->view
.ops
->insert_row( &tv
->view
, rec
, -1, FALSE
);
3343 if (r
!= ERROR_SUCCESS
)
3344 WARN("failed to insert row %u\n", r
);
3347 if (!(err_cond
& MSITRANSFORM_ERROR_VIEWTRANSFORM
) &&
3348 !wcscmp( name
, L
"_Columns" ))
3349 msi_update_table_columns( db
, table
);
3351 msiobj_release( &rec
->hdr
);
3358 /* no need to free the table, it's associated with the database */
3359 msi_free( rawdata
);
3361 tv
->view
.ops
->delete( &tv
->view
);
3363 return ERROR_SUCCESS
;
3367 * msi_table_apply_transform
3369 * Enumerate the table transforms in a transform storage and apply each one.
3371 UINT
msi_table_apply_transform( MSIDATABASE
*db
, IStorage
*stg
, int err_cond
)
3373 struct list transforms
;
3374 IEnumSTATSTG
*stgenum
= NULL
;
3375 TRANSFORMDATA
*transform
;
3376 TRANSFORMDATA
*tables
= NULL
, *columns
= NULL
;
3379 string_table
*strings
;
3380 UINT ret
= ERROR_FUNCTION_FAILED
;
3381 UINT bytes_per_strref
;
3382 BOOL property_update
= FALSE
;
3383 MSIVIEW
*transform_view
= NULL
;
3385 TRACE("%p %p\n", db
, stg
);
3387 strings
= msi_load_string_table( stg
, &bytes_per_strref
);
3391 hr
= IStorage_EnumElements( stg
, 0, NULL
, 0, &stgenum
);
3395 list_init(&transforms
);
3399 MSITABLEVIEW
*tv
= NULL
;
3403 hr
= IEnumSTATSTG_Next( stgenum
, 1, &stat
, &count
);
3404 if (FAILED( hr
) || !count
)
3407 decode_streamname( stat
.pwcsName
, name
);
3408 CoTaskMemFree( stat
.pwcsName
);
3409 if ( name
[0] != 0x4840 )
3412 if ( !wcscmp( name
+1, L
"_StringPool" ) ||
3413 !wcscmp( name
+1, L
"_StringData" ) )
3416 transform
= msi_alloc_zero( sizeof(TRANSFORMDATA
) );
3420 list_add_tail( &transforms
, &transform
->entry
);
3422 transform
->name
= strdupW( name
+ 1 );
3424 if ( !wcscmp( transform
->name
, L
"_Tables" ) )
3426 else if (!wcscmp( transform
->name
, L
"_Columns" ) )
3427 columns
= transform
;
3428 else if (!wcscmp( transform
->name
, L
"Property" ))
3429 property_update
= TRUE
;
3431 TRACE("transform contains stream %s\n", debugstr_w(name
));
3433 /* load the table */
3434 if (TABLE_CreateView( db
, transform
->name
, (MSIVIEW
**) &tv
) != ERROR_SUCCESS
)
3437 if (tv
->view
.ops
->execute( &tv
->view
, NULL
) != ERROR_SUCCESS
)
3439 tv
->view
.ops
->delete( &tv
->view
);
3443 tv
->view
.ops
->delete( &tv
->view
);
3446 if (err_cond
& MSITRANSFORM_ERROR_VIEWTRANSFORM
)
3448 static const WCHAR create_query
[] = L
"CREATE TABLE `_TransformView` ( "
3449 L
"`Table` CHAR(0) NOT NULL TEMPORARY, `Column` CHAR(0) NOT NULL TEMPORARY, "
3450 L
"`Row` CHAR(0) TEMPORARY, `Data` CHAR(0) TEMPORARY, `Current` CHAR(0) TEMPORARY "
3451 L
"PRIMARY KEY `Table`, `Column`, `Row` ) HOLD";
3456 r
= MSI_DatabaseOpenViewW( db
, create_query
, &query
);
3457 if (r
!= ERROR_SUCCESS
)
3460 r
= MSI_ViewExecute( query
, NULL
);
3461 if (r
== ERROR_SUCCESS
)
3462 MSI_ViewClose( query
);
3463 msiobj_release( &query
->hdr
);
3464 if (r
!= ERROR_BAD_QUERY_SYNTAX
&& r
!= ERROR_SUCCESS
)
3467 if (TABLE_CreateView(db
, L
"_TransformView", &transform_view
) != ERROR_SUCCESS
)
3470 if (r
== ERROR_BAD_QUERY_SYNTAX
)
3471 transform_view
->ops
->add_ref( transform_view
);
3473 r
= transform_view
->ops
->add_column( transform_view
, L
"new",
3474 MSITYPE_TEMPORARY
| MSITYPE_NULLABLE
| 0x402 /* INT */, FALSE
);
3475 if (r
!= ERROR_SUCCESS
)
3480 * Apply _Tables and _Columns transforms first so that
3481 * the table metadata is correct, and empty tables exist.
3483 ret
= msi_table_load_transform( db
, stg
, strings
, tables
, bytes_per_strref
, err_cond
);
3484 if (ret
!= ERROR_SUCCESS
&& ret
!= ERROR_INVALID_TABLE
)
3487 ret
= msi_table_load_transform( db
, stg
, strings
, columns
, bytes_per_strref
, err_cond
);
3488 if (ret
!= ERROR_SUCCESS
&& ret
!= ERROR_INVALID_TABLE
)
3491 ret
= ERROR_SUCCESS
;
3493 while ( !list_empty( &transforms
) )
3495 transform
= LIST_ENTRY( list_head( &transforms
), TRANSFORMDATA
, entry
);
3497 if ( wcscmp( transform
->name
, L
"_Columns" ) &&
3498 wcscmp( transform
->name
, L
"_Tables" ) &&
3499 ret
== ERROR_SUCCESS
)
3501 ret
= msi_table_load_transform( db
, stg
, strings
, transform
, bytes_per_strref
, err_cond
);
3504 list_remove( &transform
->entry
);
3505 msi_free( transform
->name
);
3506 msi_free( transform
);
3509 if ( ret
== ERROR_SUCCESS
)
3511 append_storage_to_db( db
, stg
);
3512 if (property_update
) msi_clone_properties( db
);
3517 IEnumSTATSTG_Release( stgenum
);
3519 msi_destroy_stringtable( strings
);
3522 struct tagMSITABLE
*table
= ((MSITABLEVIEW
*)transform_view
)->table
;
3524 if (ret
!= ERROR_SUCCESS
)
3525 transform_view
->ops
->release( transform_view
);
3527 if (!wcscmp(table
->colinfo
[table
->col_count
- 1].colname
, L
"new"))
3528 TABLE_remove_column( transform_view
, table
->colinfo
[table
->col_count
- 1].number
);
3529 transform_view
->ops
->delete( transform_view
);