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 %2d -> %s %s\n", n
,
225 debugstr_w(stat
.pwcsName
), debugstr_w(name
) );
226 CoTaskMemFree( stat
.pwcsName
);
230 IEnumSTATSTG_Release( stgenum
);
233 UINT
read_stream_data( IStorage
*stg
, LPCWSTR stname
, BOOL table
,
234 BYTE
**pdata
, UINT
*psz
)
237 UINT ret
= ERROR_FUNCTION_FAILED
;
244 encname
= encode_streamname(table
, stname
);
246 TRACE("%s -> %s\n",debugstr_w(stname
),debugstr_w(encname
));
248 r
= IStorage_OpenStream(stg
, encname
, NULL
,
249 STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
253 WARN("open stream failed r = %08x - empty table?\n", r
);
257 r
= IStream_Stat(stm
, &stat
, STATFLAG_NONAME
);
260 WARN("open stream failed r = %08x!\n", r
);
264 if( stat
.cbSize
.QuadPart
>> 32 )
270 sz
= stat
.cbSize
.QuadPart
;
271 data
= msi_alloc( sz
);
274 WARN("couldn't allocate memory r=%08x!\n", r
);
275 ret
= ERROR_NOT_ENOUGH_MEMORY
;
279 r
= IStream_Read(stm
, data
, sz
, &count
);
280 if( FAILED( r
) || ( count
!= sz
) )
283 WARN("read stream failed r = %08x!\n", r
);
292 IStream_Release( stm
);
297 UINT
write_stream_data( IStorage
*stg
, LPCWSTR stname
,
298 LPCVOID data
, UINT sz
, BOOL bTable
)
301 UINT ret
= ERROR_FUNCTION_FAILED
;
308 encname
= encode_streamname(bTable
, stname
);
309 r
= IStorage_OpenStream( stg
, encname
, NULL
,
310 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
313 r
= IStorage_CreateStream( stg
, encname
,
314 STGM_WRITE
| STGM_SHARE_EXCLUSIVE
, 0, 0, &stm
);
319 WARN("open stream failed r = %08x\n", r
);
324 r
= IStream_SetSize( stm
, size
);
327 WARN("Failed to SetSize\n");
332 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_SET
, NULL
);
335 WARN("Failed to Seek\n");
341 r
= IStream_Write(stm
, data
, sz
, &count
);
342 if( FAILED( r
) || ( count
!= sz
) )
344 WARN("Failed to Write\n");
352 IStream_Release( stm
);
357 static void msi_free_colinfo( MSICOLUMNINFO
*colinfo
, UINT count
)
360 for (i
= 0; i
< count
; i
++) msi_free( colinfo
[i
].hash_table
);
363 static void free_table( MSITABLE
*table
)
366 for( i
=0; i
<table
->row_count
; i
++ )
367 msi_free( table
->data
[i
] );
368 msi_free( table
->data
);
369 msi_free( table
->data_persistent
);
370 msi_free_colinfo( table
->colinfo
, table
->col_count
);
371 msi_free( table
->colinfo
);
375 static UINT
msi_table_get_row_size( MSIDATABASE
*db
, const MSICOLUMNINFO
*cols
, UINT count
, UINT bytes_per_strref
)
377 const MSICOLUMNINFO
*last_col
;
382 if (bytes_per_strref
!= LONG_STR_BYTES
)
385 for (i
= 0; i
< count
; i
++) size
+= bytes_per_column( db
, &cols
[i
], bytes_per_strref
);
388 last_col
= &cols
[count
- 1];
389 return last_col
->offset
+ bytes_per_column( db
, last_col
, bytes_per_strref
);
392 /* add this table to the list of cached tables in the database */
393 static UINT
read_table_from_storage( MSIDATABASE
*db
, MSITABLE
*t
, IStorage
*stg
)
395 BYTE
*rawdata
= NULL
;
396 UINT rawsize
= 0, i
, j
, row_size
, row_size_mem
;
398 TRACE("%s\n",debugstr_w(t
->name
));
400 row_size
= msi_table_get_row_size( db
, t
->colinfo
, t
->col_count
, db
->bytes_per_strref
);
401 row_size_mem
= msi_table_get_row_size( db
, t
->colinfo
, t
->col_count
, LONG_STR_BYTES
);
403 /* if we can't read the table, just assume that it's empty */
404 read_stream_data( stg
, t
->name
, TRUE
, &rawdata
, &rawsize
);
406 return ERROR_SUCCESS
;
408 TRACE("Read %d bytes\n", rawsize
);
410 if( rawsize
% row_size
)
412 WARN("Table size is invalid %d/%d\n", rawsize
, row_size
);
416 if ((t
->row_count
= rawsize
/ row_size
))
418 if (!(t
->data
= msi_alloc_zero( t
->row_count
* sizeof(USHORT
*) ))) goto err
;
419 if (!(t
->data_persistent
= msi_alloc_zero( t
->row_count
* sizeof(BOOL
) ))) goto err
;
422 /* transpose all the data */
423 TRACE("Transposing data from %d rows\n", t
->row_count
);
424 for (i
= 0; i
< t
->row_count
; i
++)
426 UINT ofs
= 0, ofs_mem
= 0;
428 t
->data
[i
] = msi_alloc( row_size_mem
);
431 t
->data_persistent
[i
] = TRUE
;
433 for (j
= 0; j
< t
->col_count
; j
++)
435 UINT m
= bytes_per_column( db
, &t
->colinfo
[j
], LONG_STR_BYTES
);
436 UINT n
= bytes_per_column( db
, &t
->colinfo
[j
], db
->bytes_per_strref
);
439 if ( n
!= 2 && n
!= 3 && n
!= 4 )
441 ERR("oops - unknown column width %d\n", n
);
444 if (t
->colinfo
[j
].type
& MSITYPE_STRING
&& n
< m
)
446 for (k
= 0; k
< m
; k
++)
449 t
->data
[i
][ofs_mem
+ k
] = rawdata
[ofs
* t
->row_count
+ i
* n
+ k
];
451 t
->data
[i
][ofs_mem
+ k
] = 0;
456 for (k
= 0; k
< n
; k
++)
457 t
->data
[i
][ofs_mem
+ k
] = rawdata
[ofs
* t
->row_count
+ i
* n
+ k
];
465 return ERROR_SUCCESS
;
468 return ERROR_FUNCTION_FAILED
;
471 void free_cached_tables( MSIDATABASE
*db
)
473 while( !list_empty( &db
->tables
) )
475 MSITABLE
*t
= LIST_ENTRY( list_head( &db
->tables
), MSITABLE
, entry
);
477 list_remove( &t
->entry
);
482 static MSITABLE
*find_cached_table( MSIDATABASE
*db
, LPCWSTR name
)
486 LIST_FOR_EACH_ENTRY( t
, &db
->tables
, MSITABLE
, entry
)
487 if( !wcscmp( name
, t
->name
) )
493 static void table_calc_column_offsets( MSIDATABASE
*db
, MSICOLUMNINFO
*colinfo
, DWORD count
)
497 for (i
= 0; colinfo
&& i
< count
; i
++)
499 assert( i
+ 1 == colinfo
[i
].number
);
500 if (i
) colinfo
[i
].offset
= colinfo
[i
- 1].offset
+
501 bytes_per_column( db
, &colinfo
[i
- 1], LONG_STR_BYTES
);
502 else colinfo
[i
].offset
= 0;
504 TRACE("column %d is [%s] with type %08x ofs %d\n",
505 colinfo
[i
].number
, debugstr_w(colinfo
[i
].colname
),
506 colinfo
[i
].type
, colinfo
[i
].offset
);
510 static UINT
get_defaulttablecolumns( MSIDATABASE
*db
, LPCWSTR name
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
512 const MSICOLUMNINFO
*p
;
515 TRACE("%s\n", debugstr_w(name
));
517 if (!wcscmp( name
, L
"_Tables" ))
522 else if (!wcscmp( name
, L
"_Columns" ))
527 else return ERROR_FUNCTION_FAILED
;
529 for (i
= 0; i
< n
; i
++)
531 if (colinfo
&& i
< *sz
) colinfo
[i
] = p
[i
];
532 if (colinfo
&& i
>= *sz
) break;
534 table_calc_column_offsets( db
, colinfo
, n
);
536 return ERROR_SUCCESS
;
539 static UINT
get_tablecolumns( MSIDATABASE
*db
, LPCWSTR szTableName
, MSICOLUMNINFO
*colinfo
, UINT
*sz
);
541 static UINT
table_get_column_info( MSIDATABASE
*db
, LPCWSTR name
, MSICOLUMNINFO
**pcols
, UINT
*pcount
)
543 UINT r
, column_count
= 0;
544 MSICOLUMNINFO
*columns
;
546 /* get the number of columns in this table */
548 r
= get_tablecolumns( db
, name
, NULL
, &column_count
);
549 if (r
!= ERROR_SUCCESS
)
552 *pcount
= column_count
;
554 /* if there are no columns, there's no table */
556 return ERROR_INVALID_PARAMETER
;
558 TRACE("table %s found\n", debugstr_w(name
));
560 columns
= msi_alloc( column_count
* sizeof(MSICOLUMNINFO
) );
562 return ERROR_FUNCTION_FAILED
;
564 r
= get_tablecolumns( db
, name
, columns
, &column_count
);
565 if (r
!= ERROR_SUCCESS
)
568 return ERROR_FUNCTION_FAILED
;
574 static UINT
get_table( MSIDATABASE
*db
, LPCWSTR name
, MSITABLE
**table_ret
)
579 /* first, see if the table is cached */
580 table
= find_cached_table( db
, name
);
584 return ERROR_SUCCESS
;
587 /* nonexistent tables should be interpreted as empty tables */
588 table
= msi_alloc( sizeof(MSITABLE
) + lstrlenW( name
) * sizeof(WCHAR
) );
590 return ERROR_FUNCTION_FAILED
;
592 table
->row_count
= 0;
594 table
->data_persistent
= NULL
;
595 table
->colinfo
= NULL
;
596 table
->col_count
= 0;
597 table
->persistent
= MSICONDITION_TRUE
;
598 lstrcpyW( table
->name
, name
);
600 if (!wcscmp( name
, L
"_Tables" ) || !wcscmp( name
, L
"_Columns" ))
601 table
->persistent
= MSICONDITION_NONE
;
603 r
= table_get_column_info( db
, name
, &table
->colinfo
, &table
->col_count
);
604 if (r
!= ERROR_SUCCESS
)
609 r
= read_table_from_storage( db
, table
, db
->storage
);
610 if (r
!= ERROR_SUCCESS
)
615 list_add_head( &db
->tables
, &table
->entry
);
617 return ERROR_SUCCESS
;
620 static UINT
read_table_int( BYTE
*const *data
, UINT row
, UINT col
, UINT bytes
)
624 for (i
= 0; i
< bytes
; i
++)
625 ret
+= data
[row
][col
+ i
] << i
* 8;
630 static UINT
get_tablecolumns( MSIDATABASE
*db
, LPCWSTR szTableName
, MSICOLUMNINFO
*colinfo
, UINT
*sz
)
632 UINT r
, i
, n
= 0, table_id
, count
, maxcount
= *sz
;
633 MSITABLE
*table
= NULL
;
635 TRACE("%s\n", debugstr_w(szTableName
));
637 /* first check if there is a default table with that name */
638 r
= get_defaulttablecolumns( db
, szTableName
, colinfo
, sz
);
639 if (r
== ERROR_SUCCESS
&& *sz
)
642 r
= get_table( db
, L
"_Columns", &table
);
643 if (r
!= ERROR_SUCCESS
)
645 ERR("couldn't load _Columns table\n");
646 return ERROR_FUNCTION_FAILED
;
649 /* convert table and column names to IDs from the string table */
650 r
= msi_string2id( db
->strings
, szTableName
, -1, &table_id
);
651 if (r
!= ERROR_SUCCESS
)
653 WARN("Couldn't find id for %s\n", debugstr_w(szTableName
));
656 TRACE("Table id is %d, row count is %d\n", table_id
, table
->row_count
);
658 /* Note: _Columns table doesn't have non-persistent data */
660 /* if maxcount is non-zero, assume it's exactly right for this table */
661 if (colinfo
) memset( colinfo
, 0, maxcount
* sizeof(*colinfo
) );
662 count
= table
->row_count
;
663 for (i
= 0; i
< count
; i
++)
665 if (read_table_int( table
->data
, i
, 0, LONG_STR_BYTES
) != table_id
) continue;
668 UINT id
= read_table_int( table
->data
, i
, table
->colinfo
[2].offset
, LONG_STR_BYTES
);
669 UINT col
= read_table_int( table
->data
, i
, table
->colinfo
[1].offset
, sizeof(USHORT
) ) - (1 << 15);
671 /* check the column number is in range */
672 if (col
< 1 || col
> maxcount
)
674 ERR("column %d out of range (maxcount: %d)\n", col
, maxcount
);
677 /* check if this column was already set */
678 if (colinfo
[col
- 1].number
)
680 ERR("duplicate column %d\n", col
);
683 colinfo
[col
- 1].tablename
= msi_string_lookup( db
->strings
, table_id
, NULL
);
684 colinfo
[col
- 1].number
= col
;
685 colinfo
[col
- 1].colname
= msi_string_lookup( db
->strings
, id
, NULL
);
686 colinfo
[col
- 1].type
= read_table_int( table
->data
, i
, table
->colinfo
[3].offset
,
687 sizeof(USHORT
) ) - (1 << 15);
688 colinfo
[col
- 1].offset
= 0;
689 colinfo
[col
- 1].hash_table
= NULL
;
693 TRACE("%s has %d columns\n", debugstr_w(szTableName
), n
);
695 if (colinfo
&& n
!= maxcount
)
697 ERR("missing column in table %s\n", debugstr_w(szTableName
));
698 msi_free_colinfo( colinfo
, maxcount
);
699 return ERROR_FUNCTION_FAILED
;
701 table_calc_column_offsets( db
, colinfo
, n
);
703 return ERROR_SUCCESS
;
706 UINT
msi_create_table( MSIDATABASE
*db
, LPCWSTR name
, column_info
*col_info
,
707 MSICONDITION persistent
, BOOL hold
)
711 MSIRECORD
*rec
= NULL
;
716 /* only add tables that don't exist already */
717 if( TABLE_Exists(db
, name
) )
719 WARN("table %s exists\n", debugstr_w(name
));
720 return ERROR_BAD_QUERY_SYNTAX
;
723 table
= msi_alloc( sizeof (MSITABLE
) + lstrlenW(name
)*sizeof (WCHAR
) );
725 return ERROR_FUNCTION_FAILED
;
727 table
->ref_count
= 0;
728 table
->row_count
= 0;
730 table
->data_persistent
= NULL
;
731 table
->colinfo
= NULL
;
732 table
->col_count
= 0;
733 table
->persistent
= persistent
;
734 lstrcpyW( table
->name
, name
);
739 for( col
= col_info
; col
; col
= col
->next
)
742 table
->colinfo
= msi_alloc( table
->col_count
* sizeof(MSICOLUMNINFO
) );
746 return ERROR_FUNCTION_FAILED
;
749 for( i
= 0, col
= col_info
; col
; i
++, col
= col
->next
)
751 UINT table_id
= msi_add_string( db
->strings
, col
->table
, -1, persistent
);
752 UINT col_id
= msi_add_string( db
->strings
, col
->column
, -1, persistent
);
754 table
->colinfo
[ i
].tablename
= msi_string_lookup( db
->strings
, table_id
, NULL
);
755 table
->colinfo
[ i
].number
= i
+ 1;
756 table
->colinfo
[ i
].colname
= msi_string_lookup( db
->strings
, col_id
, NULL
);
757 table
->colinfo
[ i
].type
= col
->type
;
758 table
->colinfo
[ i
].offset
= 0;
759 table
->colinfo
[ i
].hash_table
= NULL
;
761 table_calc_column_offsets( db
, table
->colinfo
, table
->col_count
);
763 r
= TABLE_CreateView( db
, L
"_Tables", &tv
);
764 TRACE("CreateView returned %x\n", r
);
771 r
= tv
->ops
->execute( tv
, 0 );
772 TRACE("tv execute returned %x\n", r
);
776 rec
= MSI_CreateRecord( 1 );
780 r
= MSI_RecordSetStringW( rec
, 1, name
);
784 r
= tv
->ops
->insert_row( tv
, rec
, -1, persistent
== MSICONDITION_FALSE
);
785 TRACE("insert_row returned %x\n", r
);
789 tv
->ops
->delete( tv
);
792 msiobj_release( &rec
->hdr
);
795 if( persistent
!= MSICONDITION_FALSE
)
797 /* add each column to the _Columns table */
798 r
= TABLE_CreateView( db
, L
"_Columns", &tv
);
802 r
= tv
->ops
->execute( tv
, 0 );
803 TRACE("tv execute returned %x\n", r
);
807 rec
= MSI_CreateRecord( 4 );
811 r
= MSI_RecordSetStringW( rec
, 1, name
);
816 * need to set the table, column number, col name and type
817 * for each column we enter in the table
820 for( col
= col_info
; col
; col
= col
->next
)
822 r
= MSI_RecordSetInteger( rec
, 2, nField
);
826 r
= MSI_RecordSetStringW( rec
, 3, col
->column
);
830 r
= MSI_RecordSetInteger( rec
, 4, col
->type
);
834 r
= tv
->ops
->insert_row( tv
, rec
, -1, FALSE
);
846 msiobj_release( &rec
->hdr
);
847 /* FIXME: remove values from the string table on error */
849 tv
->ops
->delete( tv
);
851 if (r
== ERROR_SUCCESS
)
852 list_add_head( &db
->tables
, &table
->entry
);
859 static UINT
save_table( MSIDATABASE
*db
, const MSITABLE
*t
, UINT bytes_per_strref
)
861 BYTE
*rawdata
= NULL
;
862 UINT rawsize
, i
, j
, row_size
, row_count
;
863 UINT r
= ERROR_FUNCTION_FAILED
;
865 /* Nothing to do for non-persistent tables */
866 if( t
->persistent
== MSICONDITION_FALSE
)
867 return ERROR_SUCCESS
;
869 TRACE("Saving %s\n", debugstr_w( t
->name
) );
871 row_size
= msi_table_get_row_size( db
, t
->colinfo
, t
->col_count
, bytes_per_strref
);
872 row_count
= t
->row_count
;
873 for (i
= 0; i
< t
->row_count
; i
++)
875 if (!t
->data_persistent
[i
])
877 row_count
= 1; /* yes, this is bizarre */
881 rawsize
= row_count
* row_size
;
882 rawdata
= msi_alloc_zero( rawsize
);
885 r
= ERROR_NOT_ENOUGH_MEMORY
;
890 for (i
= 0; i
< row_count
; i
++)
892 UINT ofs
= 0, ofs_mem
= 0;
894 if (!t
->data_persistent
[i
]) break;
896 for (j
= 0; j
< t
->col_count
; j
++)
898 UINT m
= bytes_per_column( db
, &t
->colinfo
[j
], LONG_STR_BYTES
);
899 UINT n
= bytes_per_column( db
, &t
->colinfo
[j
], bytes_per_strref
);
902 if (n
!= 2 && n
!= 3 && n
!= 4)
904 ERR("oops - unknown column width %d\n", n
);
907 if (t
->colinfo
[j
].type
& MSITYPE_STRING
&& n
< m
)
909 UINT id
= read_table_int( t
->data
, i
, ofs_mem
, LONG_STR_BYTES
);
910 if (id
> 1 << bytes_per_strref
* 8)
912 ERR("string id %u out of range\n", id
);
916 for (k
= 0; k
< n
; k
++)
918 rawdata
[ofs
* row_count
+ i
* n
+ k
] = t
->data
[i
][ofs_mem
+ k
];
926 TRACE("writing %d bytes\n", rawsize
);
927 r
= write_stream_data( db
->storage
, t
->name
, rawdata
, rawsize
, TRUE
);
934 static void msi_update_table_columns( MSIDATABASE
*db
, LPCWSTR name
)
937 UINT size
, offset
, old_count
;
940 if (!(table
= find_cached_table( db
, name
))) return;
941 old_count
= table
->col_count
;
942 msi_free_colinfo( table
->colinfo
, table
->col_count
);
943 msi_free( table
->colinfo
);
944 table
->colinfo
= NULL
;
946 table_get_column_info( db
, name
, &table
->colinfo
, &table
->col_count
);
947 if (!table
->col_count
) return;
949 size
= msi_table_get_row_size( db
, table
->colinfo
, table
->col_count
, LONG_STR_BYTES
);
950 offset
= table
->colinfo
[table
->col_count
- 1].offset
;
952 for ( n
= 0; n
< table
->row_count
; n
++ )
954 table
->data
[n
] = msi_realloc( table
->data
[n
], size
);
955 if (old_count
< table
->col_count
)
956 memset( &table
->data
[n
][offset
], 0, size
- offset
);
960 /* try to find the table name in the _Tables table */
961 BOOL
TABLE_Exists( MSIDATABASE
*db
, LPCWSTR name
)
966 if( !wcscmp( name
, L
"_Tables" ) || !wcscmp( name
, L
"_Columns" ) ||
967 !wcscmp( name
, L
"_Streams" ) || !wcscmp( name
, L
"_Storages" ) )
970 r
= msi_string2id( db
->strings
, name
, -1, &table_id
);
971 if( r
!= ERROR_SUCCESS
)
973 TRACE("Couldn't find id for %s\n", debugstr_w(name
));
977 r
= get_table( db
, L
"_Tables", &table
);
978 if( r
!= ERROR_SUCCESS
)
980 ERR("table _Tables not available\n");
984 for( i
= 0; i
< table
->row_count
; i
++ )
986 if( read_table_int( table
->data
, i
, 0, LONG_STR_BYTES
) == table_id
)
993 /* below is the query interface to a table */
995 typedef struct tagMSITABLEVIEW
1000 MSICOLUMNINFO
*columns
;
1006 static UINT
TABLE_fetch_int( struct tagMSIVIEW
*view
, UINT row
, UINT col
, UINT
*val
)
1008 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1012 return ERROR_INVALID_PARAMETER
;
1014 if( (col
==0) || (col
>tv
->num_cols
) )
1015 return ERROR_INVALID_PARAMETER
;
1017 /* how many rows are there ? */
1018 if( row
>= tv
->table
->row_count
)
1019 return ERROR_NO_MORE_ITEMS
;
1021 if( tv
->columns
[col
-1].offset
>= tv
->row_size
)
1023 ERR("Stuffed up %d >= %d\n", tv
->columns
[col
-1].offset
, tv
->row_size
);
1024 ERR("%p %p\n", tv
, tv
->columns
);
1025 return ERROR_FUNCTION_FAILED
;
1028 n
= bytes_per_column( tv
->db
, &tv
->columns
[col
- 1], LONG_STR_BYTES
);
1029 if (n
!= 2 && n
!= 3 && n
!= 4)
1031 ERR("oops! what is %d bytes per column?\n", n
);
1032 return ERROR_FUNCTION_FAILED
;
1035 offset
= tv
->columns
[col
-1].offset
;
1036 *val
= read_table_int(tv
->table
->data
, row
, offset
, n
);
1038 /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1040 return ERROR_SUCCESS
;
1043 static UINT
get_stream_name( const MSITABLEVIEW
*tv
, UINT row
, WCHAR
**pstname
)
1045 LPWSTR p
, stname
= NULL
;
1046 UINT i
, r
, type
, ival
;
1049 MSIVIEW
*view
= (MSIVIEW
*) tv
;
1051 TRACE("%p %d\n", tv
, row
);
1053 len
= lstrlenW( tv
->name
) + 1;
1054 stname
= msi_alloc( len
*sizeof(WCHAR
) );
1057 r
= ERROR_OUTOFMEMORY
;
1061 lstrcpyW( stname
, tv
->name
);
1063 for ( i
= 0; i
< tv
->num_cols
; i
++ )
1065 type
= tv
->columns
[i
].type
;
1066 if ( type
& MSITYPE_KEY
)
1070 r
= TABLE_fetch_int( view
, row
, i
+1, &ival
);
1071 if ( r
!= ERROR_SUCCESS
)
1074 if ( tv
->columns
[i
].type
& MSITYPE_STRING
)
1076 sval
= msi_string_lookup( tv
->db
->strings
, ival
, NULL
);
1079 r
= ERROR_INVALID_PARAMETER
;
1085 UINT n
= bytes_per_column( tv
->db
, &tv
->columns
[i
], LONG_STR_BYTES
);
1090 swprintf( number
, ARRAY_SIZE(number
), L
"%d", ival
-0x8000 );
1093 swprintf( number
, ARRAY_SIZE(number
), L
"%d", ival
^0x80000000 );
1096 ERR( "oops - unknown column width %d\n", n
);
1097 r
= ERROR_FUNCTION_FAILED
;
1103 len
+= lstrlenW( L
"." ) + lstrlenW( sval
);
1104 p
= msi_realloc ( stname
, len
*sizeof(WCHAR
) );
1107 r
= ERROR_OUTOFMEMORY
;
1112 lstrcatW( stname
, L
"." );
1113 lstrcatW( stname
, sval
);
1120 return ERROR_SUCCESS
;
1129 * We need a special case for streams, as we need to reference column with
1130 * the name of the stream in the same table, and the table name
1131 * which may not be available at higher levels of the query
1133 static UINT
TABLE_fetch_stream( struct tagMSIVIEW
*view
, UINT row
, UINT col
, IStream
**stm
)
1135 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1139 if( !view
->ops
->fetch_int
)
1140 return ERROR_INVALID_PARAMETER
;
1142 r
= get_stream_name( tv
, row
, &name
);
1143 if (r
!= ERROR_SUCCESS
)
1145 ERR("fetching stream, error = %u\n", r
);
1149 r
= msi_get_stream( tv
->db
, name
, stm
);
1150 if (r
!= ERROR_SUCCESS
)
1151 ERR("fetching stream %s, error = %u\n", debugstr_w(name
), r
);
1157 /* Set a table value, i.e. preadjusted integer or string ID. */
1158 static UINT
table_set_bytes( MSITABLEVIEW
*tv
, UINT row
, UINT col
, UINT val
)
1163 return ERROR_INVALID_PARAMETER
;
1165 if( (col
==0) || (col
>tv
->num_cols
) )
1166 return ERROR_INVALID_PARAMETER
;
1168 if( row
>= tv
->table
->row_count
)
1169 return ERROR_INVALID_PARAMETER
;
1171 if( tv
->columns
[col
-1].offset
>= tv
->row_size
)
1173 ERR("Stuffed up %d >= %d\n", tv
->columns
[col
-1].offset
, tv
->row_size
);
1174 ERR("%p %p\n", tv
, tv
->columns
);
1175 return ERROR_FUNCTION_FAILED
;
1178 msi_free( tv
->columns
[col
-1].hash_table
);
1179 tv
->columns
[col
-1].hash_table
= NULL
;
1181 n
= bytes_per_column( tv
->db
, &tv
->columns
[col
- 1], LONG_STR_BYTES
);
1182 if ( n
!= 2 && n
!= 3 && n
!= 4 )
1184 ERR("oops! what is %d bytes per column?\n", n
);
1185 return ERROR_FUNCTION_FAILED
;
1188 offset
= tv
->columns
[col
-1].offset
;
1189 for ( i
= 0; i
< n
; i
++ )
1190 tv
->table
->data
[row
][offset
+ i
] = (val
>> i
* 8) & 0xff;
1192 return ERROR_SUCCESS
;
1195 static UINT
int_to_table_storage( const MSITABLEVIEW
*tv
, UINT col
, int val
, UINT
*ret
)
1197 if ((tv
->columns
[col
-1].type
& MSI_DATASIZEMASK
) == 2)
1199 if (val
== MSI_NULL_INTEGER
)
1201 else if ((val
+ 0x8000) & 0xffff0000)
1203 ERR("value %d out of range\n", val
);
1204 return ERROR_FUNCTION_FAILED
;
1207 *ret
= val
+ 0x8000;
1210 *ret
= val
^ 0x80000000;
1212 return ERROR_SUCCESS
;
1215 static UINT
TABLE_set_int( MSIVIEW
*view
, UINT row
, UINT col
, int val
)
1217 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1220 TRACE("row %u, col %u, val %d.\n", row
, col
, val
);
1222 if ((r
= int_to_table_storage( tv
, col
, val
, &table_int
)))
1225 if (tv
->columns
[col
-1].type
& MSITYPE_KEY
)
1229 if ((r
= TABLE_fetch_int( view
, row
, col
, &key
)))
1231 if (key
!= table_int
)
1233 ERR("Cannot modify primary key %s.%s.\n",
1234 debugstr_w(tv
->table
->name
), debugstr_w(tv
->columns
[col
-1].colname
));
1235 return ERROR_FUNCTION_FAILED
;
1239 return table_set_bytes( tv
, row
, col
, table_int
);
1242 static UINT
TABLE_set_string( MSIVIEW
*view
, UINT row
, UINT col
, const WCHAR
*val
, int len
)
1244 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1248 TRACE("row %u, col %u, val %s.\n", row
, col
, debugstr_wn(val
, len
));
1250 persistent
= (tv
->table
->persistent
!= MSICONDITION_FALSE
)
1251 && tv
->table
->data_persistent
[row
];
1255 r
= msi_string2id( tv
->db
->strings
, val
, len
, &id
);
1256 if (r
!= ERROR_SUCCESS
)
1257 id
= msi_add_string( tv
->db
->strings
, val
, len
, persistent
);
1262 if (tv
->columns
[col
-1].type
& MSITYPE_KEY
)
1266 if ((r
= TABLE_fetch_int( view
, row
, col
, &key
)))
1270 ERR("Cannot modify primary key %s.%s.\n",
1271 debugstr_w(tv
->table
->name
), debugstr_w(tv
->columns
[col
-1].colname
));
1272 return ERROR_FUNCTION_FAILED
;
1276 return table_set_bytes( tv
, row
, col
, id
);
1279 static UINT
TABLE_get_row( struct tagMSIVIEW
*view
, UINT row
, MSIRECORD
**rec
)
1281 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1284 return ERROR_INVALID_PARAMETER
;
1286 return msi_view_get_row(tv
->db
, view
, row
, rec
);
1289 static UINT
add_stream( MSIDATABASE
*db
, const WCHAR
*name
, IStream
*data
)
1295 TRACE("%p %s %p\n", db
, debugstr_w(name
), data
);
1297 if (!(rec
= MSI_CreateRecord( 2 )))
1298 return ERROR_OUTOFMEMORY
;
1300 r
= MSI_RecordSetStringW( rec
, 1, name
);
1301 if (r
!= ERROR_SUCCESS
)
1304 r
= MSI_RecordSetIStream( rec
, 2, data
);
1305 if (r
!= ERROR_SUCCESS
)
1308 r
= MSI_DatabaseOpenViewW( db
, L
"INSERT INTO `_Streams` (`Name`,`Data`) VALUES (?,?)", &query
);
1309 if (r
!= ERROR_SUCCESS
)
1312 r
= MSI_ViewExecute( query
, rec
);
1313 msiobj_release( &query
->hdr
);
1314 if (r
== ERROR_SUCCESS
)
1317 msiobj_release( &rec
->hdr
);
1318 if (!(rec
= MSI_CreateRecord( 2 )))
1319 return ERROR_OUTOFMEMORY
;
1321 r
= MSI_RecordSetIStream( rec
, 1, data
);
1322 if (r
!= ERROR_SUCCESS
)
1325 r
= MSI_RecordSetStringW( rec
, 2, name
);
1326 if (r
!= ERROR_SUCCESS
)
1329 r
= MSI_DatabaseOpenViewW( db
, L
"UPDATE `_Streams` SET `Data` = ? WHERE `Name` = ?", &query
);
1330 if (r
!= ERROR_SUCCESS
)
1333 r
= MSI_ViewExecute( query
, rec
);
1334 msiobj_release( &query
->hdr
);
1337 msiobj_release( &rec
->hdr
);
1341 static UINT
TABLE_set_stream( MSIVIEW
*view
, UINT row
, UINT col
, IStream
*stream
)
1343 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1347 TRACE("row %u, col %u, stream %p.\n", row
, col
, stream
);
1349 if ((r
= get_stream_name( tv
, row
- 1, &name
)))
1352 r
= add_stream( tv
->db
, name
, stream
);
1357 static UINT
get_table_value_from_record( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT iField
, UINT
*pvalue
)
1359 MSICOLUMNINFO columninfo
;
1362 if (!iField
|| iField
> tv
->num_cols
|| MSI_RecordIsNull( rec
, iField
))
1363 return ERROR_FUNCTION_FAILED
;
1365 columninfo
= tv
->columns
[ iField
- 1 ];
1367 if ( MSITYPE_IS_BINARY(columninfo
.type
) )
1369 *pvalue
= 1; /* refers to the first key column */
1371 else if ( columninfo
.type
& MSITYPE_STRING
)
1374 const WCHAR
*sval
= msi_record_get_string( rec
, iField
, &len
);
1377 r
= msi_string2id( tv
->db
->strings
, sval
, len
, pvalue
);
1378 if (r
!= ERROR_SUCCESS
)
1379 return ERROR_NOT_FOUND
;
1384 return int_to_table_storage( tv
, iField
, MSI_RecordGetInteger( rec
, iField
), pvalue
);
1386 return ERROR_SUCCESS
;
1389 static UINT
TABLE_set_row( struct tagMSIVIEW
*view
, UINT row
, MSIRECORD
*rec
, UINT mask
)
1391 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1392 UINT i
, val
, r
= ERROR_SUCCESS
;
1395 return ERROR_INVALID_PARAMETER
;
1397 /* test if any of the mask bits are invalid */
1398 if ( mask
>= (1<<tv
->num_cols
) )
1399 return ERROR_INVALID_PARAMETER
;
1401 for ( i
= 0; i
< tv
->num_cols
; i
++ )
1405 /* only update the fields specified in the mask */
1406 if ( !(mask
&(1<<i
)) )
1409 persistent
= (tv
->table
->persistent
!= MSICONDITION_FALSE
) &&
1410 (tv
->table
->data_persistent
[row
]);
1411 /* FIXME: should we allow updating keys? */
1414 if ( !MSI_RecordIsNull( rec
, i
+ 1 ) )
1416 r
= get_table_value_from_record (tv
, rec
, i
+ 1, &val
);
1417 if ( MSITYPE_IS_BINARY(tv
->columns
[ i
].type
) )
1422 if ( r
!= ERROR_SUCCESS
)
1423 return ERROR_FUNCTION_FAILED
;
1425 r
= MSI_RecordGetIStream( rec
, i
+ 1, &stm
);
1426 if ( r
!= ERROR_SUCCESS
)
1429 r
= get_stream_name( tv
, row
, &stname
);
1430 if ( r
!= ERROR_SUCCESS
)
1432 IStream_Release( stm
);
1436 r
= add_stream( tv
->db
, stname
, stm
);
1437 IStream_Release( stm
);
1438 msi_free ( stname
);
1440 if ( r
!= ERROR_SUCCESS
)
1443 else if ( tv
->columns
[i
].type
& MSITYPE_STRING
)
1447 if ( r
!= ERROR_SUCCESS
)
1450 const WCHAR
*sval
= msi_record_get_string( rec
, i
+ 1, &len
);
1451 val
= msi_add_string( tv
->db
->strings
, sval
, len
, persistent
);
1455 TABLE_fetch_int(&tv
->view
, row
, i
+ 1, &x
);
1462 if ( r
!= ERROR_SUCCESS
)
1463 return ERROR_FUNCTION_FAILED
;
1467 r
= table_set_bytes( tv
, row
, i
+1, val
);
1468 if ( r
!= ERROR_SUCCESS
)
1474 static UINT
table_create_new_row( struct tagMSIVIEW
*view
, UINT
*num
, BOOL temporary
)
1476 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1481 BOOL
**data_persist_ptr
;
1484 TRACE("%p %s\n", view
, temporary
? "TRUE" : "FALSE");
1487 return ERROR_INVALID_PARAMETER
;
1489 row
= msi_alloc_zero( tv
->row_size
);
1491 return ERROR_NOT_ENOUGH_MEMORY
;
1493 row_count
= &tv
->table
->row_count
;
1494 data_ptr
= &tv
->table
->data
;
1495 data_persist_ptr
= &tv
->table
->data_persistent
;
1497 *num
= tv
->table
->row_count
;
1499 sz
= (*row_count
+ 1) * sizeof (BYTE
*);
1501 p
= msi_realloc( *data_ptr
, sz
);
1503 p
= msi_alloc( sz
);
1507 return ERROR_NOT_ENOUGH_MEMORY
;
1510 sz
= (*row_count
+ 1) * sizeof (BOOL
);
1511 if( *data_persist_ptr
)
1512 b
= msi_realloc( *data_persist_ptr
, sz
);
1514 b
= msi_alloc( sz
);
1519 return ERROR_NOT_ENOUGH_MEMORY
;
1523 (*data_ptr
)[*row_count
] = row
;
1525 *data_persist_ptr
= b
;
1526 (*data_persist_ptr
)[*row_count
] = !temporary
;
1530 return ERROR_SUCCESS
;
1533 static UINT
TABLE_execute( struct tagMSIVIEW
*view
, MSIRECORD
*record
)
1535 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1537 TRACE("%p %p\n", tv
, record
);
1539 TRACE("There are %d columns\n", tv
->num_cols
);
1541 return ERROR_SUCCESS
;
1544 static UINT
TABLE_close( struct tagMSIVIEW
*view
)
1546 TRACE("%p\n", view
);
1548 return ERROR_SUCCESS
;
1551 static UINT
TABLE_get_dimensions( struct tagMSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
1553 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1555 TRACE("%p %p %p\n", view
, rows
, cols
);
1558 *cols
= tv
->num_cols
;
1562 return ERROR_INVALID_PARAMETER
;
1563 *rows
= tv
->table
->row_count
;
1566 return ERROR_SUCCESS
;
1569 static UINT
TABLE_get_column_info( struct tagMSIVIEW
*view
,
1570 UINT n
, LPCWSTR
*name
, UINT
*type
, BOOL
*temporary
,
1571 LPCWSTR
*table_name
)
1573 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1575 TRACE("%p %d %p %p\n", tv
, n
, name
, type
);
1577 if( ( n
== 0 ) || ( n
> tv
->num_cols
) )
1578 return ERROR_INVALID_PARAMETER
;
1582 *name
= tv
->columns
[n
-1].colname
;
1584 return ERROR_FUNCTION_FAILED
;
1589 *table_name
= tv
->columns
[n
-1].tablename
;
1591 return ERROR_FUNCTION_FAILED
;
1595 *type
= tv
->columns
[n
-1].type
;
1598 *temporary
= (tv
->columns
[n
-1].type
& MSITYPE_TEMPORARY
) != 0;
1600 return ERROR_SUCCESS
;
1603 static UINT
msi_table_find_row( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT
*row
, UINT
*column
);
1605 static UINT
table_validate_new( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT
*column
)
1609 /* check there are no null values where they're not allowed */
1610 for( i
= 0; i
< tv
->num_cols
; i
++ )
1612 if ( tv
->columns
[i
].type
& MSITYPE_NULLABLE
)
1615 if ( MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
1616 TRACE("skipping binary column\n");
1617 else if ( tv
->columns
[i
].type
& MSITYPE_STRING
)
1620 const WCHAR
*str
= msi_record_get_string( rec
, i
+1, &len
);
1622 if (!str
|| (!str
[0] && !len
))
1624 if (column
) *column
= i
;
1625 return ERROR_INVALID_DATA
;
1632 n
= MSI_RecordGetInteger( rec
, i
+1 );
1633 if (n
== MSI_NULL_INTEGER
)
1635 if (column
) *column
= i
;
1636 return ERROR_INVALID_DATA
;
1641 /* check there are no duplicate keys */
1642 r
= msi_table_find_row( tv
, rec
, &row
, column
);
1643 if (r
== ERROR_SUCCESS
)
1644 return ERROR_FUNCTION_FAILED
;
1646 return ERROR_SUCCESS
;
1649 static int compare_record( MSITABLEVIEW
*tv
, UINT row
, MSIRECORD
*rec
)
1651 UINT r
, i
, ivalue
, x
;
1653 for (i
= 0; i
< tv
->num_cols
; i
++ )
1655 if (!(tv
->columns
[i
].type
& MSITYPE_KEY
)) continue;
1657 r
= get_table_value_from_record( tv
, rec
, i
+ 1, &ivalue
);
1658 if (r
!= ERROR_SUCCESS
)
1661 r
= TABLE_fetch_int( &tv
->view
, row
, i
+ 1, &x
);
1662 if (r
!= ERROR_SUCCESS
)
1664 WARN("TABLE_fetch_int should not fail here %u\n", r
);
1671 else if (ivalue
== x
)
1673 if (i
< tv
->num_cols
- 1) continue;
1682 static int find_insert_index( MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
1684 int idx
, c
, low
= 0, high
= tv
->table
->row_count
- 1;
1686 TRACE("%p %p\n", tv
, rec
);
1690 idx
= (low
+ high
) / 2;
1691 c
= compare_record( tv
, idx
, rec
);
1699 TRACE("found %u\n", idx
);
1703 TRACE("found %u\n", high
+ 1);
1707 static UINT
TABLE_insert_row( struct tagMSIVIEW
*view
, MSIRECORD
*rec
, UINT row
, BOOL temporary
)
1709 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1712 TRACE("%p %p %s\n", tv
, rec
, temporary
? "TRUE" : "FALSE" );
1714 /* check that the key is unique - can we find a matching row? */
1715 r
= table_validate_new( tv
, rec
, NULL
);
1716 if( r
!= ERROR_SUCCESS
)
1717 return ERROR_FUNCTION_FAILED
;
1720 row
= find_insert_index( tv
, rec
);
1722 r
= table_create_new_row( view
, &row
, temporary
);
1723 TRACE("insert_row returned %08x\n", r
);
1724 if( r
!= ERROR_SUCCESS
)
1727 /* shift the rows to make room for the new row */
1728 for (i
= tv
->table
->row_count
- 1; i
> row
; i
--)
1730 memmove(&(tv
->table
->data
[i
][0]),
1731 &(tv
->table
->data
[i
- 1][0]), tv
->row_size
);
1732 tv
->table
->data_persistent
[i
] = tv
->table
->data_persistent
[i
- 1];
1735 /* Re-set the persistence flag */
1736 tv
->table
->data_persistent
[row
] = !temporary
;
1737 return TABLE_set_row( view
, row
, rec
, (1<<tv
->num_cols
) - 1 );
1740 static UINT
TABLE_delete_row( struct tagMSIVIEW
*view
, UINT row
)
1742 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1743 UINT r
, num_rows
, num_cols
, i
;
1745 TRACE("%p %d\n", tv
, row
);
1748 return ERROR_INVALID_PARAMETER
;
1750 r
= TABLE_get_dimensions( view
, &num_rows
, &num_cols
);
1751 if ( r
!= ERROR_SUCCESS
)
1754 if ( row
>= num_rows
)
1755 return ERROR_FUNCTION_FAILED
;
1757 num_rows
= tv
->table
->row_count
;
1758 tv
->table
->row_count
--;
1760 /* reset the hash tables */
1761 for (i
= 0; i
< tv
->num_cols
; i
++)
1763 msi_free( tv
->columns
[i
].hash_table
);
1764 tv
->columns
[i
].hash_table
= NULL
;
1767 for (i
= row
+ 1; i
< num_rows
; i
++)
1769 memcpy(tv
->table
->data
[i
- 1], tv
->table
->data
[i
], tv
->row_size
);
1770 tv
->table
->data_persistent
[i
- 1] = tv
->table
->data_persistent
[i
];
1773 msi_free(tv
->table
->data
[num_rows
- 1]);
1775 return ERROR_SUCCESS
;
1778 static UINT
msi_table_update(struct tagMSIVIEW
*view
, MSIRECORD
*rec
, UINT row
)
1780 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1783 /* FIXME: MsiViewFetch should set rec index 0 to some ID that
1784 * sets the fetched record apart from other records
1788 return ERROR_INVALID_PARAMETER
;
1790 r
= msi_table_find_row(tv
, rec
, &new_row
, NULL
);
1791 if (r
!= ERROR_SUCCESS
)
1793 ERR("can't find row to modify\n");
1794 return ERROR_FUNCTION_FAILED
;
1797 /* the row cannot be changed */
1799 return ERROR_FUNCTION_FAILED
;
1801 return TABLE_set_row(view
, new_row
, rec
, (1 << tv
->num_cols
) - 1);
1804 static UINT
msi_table_assign(struct tagMSIVIEW
*view
, MSIRECORD
*rec
)
1806 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1810 return ERROR_INVALID_PARAMETER
;
1812 r
= msi_table_find_row(tv
, rec
, &row
, NULL
);
1813 if (r
== ERROR_SUCCESS
)
1814 return TABLE_set_row(view
, row
, rec
, (1 << tv
->num_cols
) - 1);
1816 return TABLE_insert_row( view
, rec
, -1, FALSE
);
1819 static UINT
msi_refresh_record( struct tagMSIVIEW
*view
, MSIRECORD
*rec
, UINT row
)
1824 r
= TABLE_get_row(view
, row
, &curr
);
1825 if (r
!= ERROR_SUCCESS
)
1828 /* Close the original record */
1829 MSI_CloseRecord(&rec
->hdr
);
1831 count
= MSI_RecordGetFieldCount(rec
);
1832 for (i
= 0; i
< count
; i
++)
1833 MSI_RecordCopyField(curr
, i
+ 1, rec
, i
+ 1);
1835 msiobj_release(&curr
->hdr
);
1836 return ERROR_SUCCESS
;
1839 static UINT
TABLE_modify( struct tagMSIVIEW
*view
, MSIMODIFY eModifyMode
,
1840 MSIRECORD
*rec
, UINT row
)
1842 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1843 UINT r
, frow
, column
;
1845 TRACE("%p %d %p\n", view
, eModifyMode
, rec
);
1847 switch (eModifyMode
)
1849 case MSIMODIFY_DELETE
:
1850 r
= TABLE_delete_row( view
, row
);
1852 case MSIMODIFY_VALIDATE_NEW
:
1853 r
= table_validate_new( tv
, rec
, &column
);
1854 if (r
!= ERROR_SUCCESS
)
1856 tv
->view
.error
= MSIDBERROR_DUPLICATEKEY
;
1857 tv
->view
.error_column
= tv
->columns
[column
].colname
;
1858 r
= ERROR_INVALID_DATA
;
1862 case MSIMODIFY_INSERT
:
1863 r
= table_validate_new( tv
, rec
, NULL
);
1864 if (r
!= ERROR_SUCCESS
)
1866 r
= TABLE_insert_row( view
, rec
, -1, FALSE
);
1869 case MSIMODIFY_INSERT_TEMPORARY
:
1870 r
= table_validate_new( tv
, rec
, NULL
);
1871 if (r
!= ERROR_SUCCESS
)
1873 r
= TABLE_insert_row( view
, rec
, -1, TRUE
);
1876 case MSIMODIFY_REFRESH
:
1877 r
= msi_refresh_record( view
, rec
, row
);
1880 case MSIMODIFY_UPDATE
:
1881 r
= msi_table_update( view
, rec
, row
);
1884 case MSIMODIFY_ASSIGN
:
1885 r
= msi_table_assign( view
, rec
);
1888 case MSIMODIFY_MERGE
:
1889 /* check row that matches this record */
1890 r
= msi_table_find_row( tv
, rec
, &frow
, &column
);
1891 if (r
!= ERROR_SUCCESS
)
1893 r
= table_validate_new( tv
, rec
, NULL
);
1894 if (r
== ERROR_SUCCESS
)
1895 r
= TABLE_insert_row( view
, rec
, -1, FALSE
);
1899 case MSIMODIFY_REPLACE
:
1900 case MSIMODIFY_VALIDATE
:
1901 case MSIMODIFY_VALIDATE_FIELD
:
1902 case MSIMODIFY_VALIDATE_DELETE
:
1903 FIXME("%p %d %p - mode not implemented\n", view
, eModifyMode
, rec
);
1904 r
= ERROR_CALL_NOT_IMPLEMENTED
;
1908 r
= ERROR_INVALID_DATA
;
1914 static UINT
TABLE_delete( struct tagMSIVIEW
*view
)
1916 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1918 TRACE("%p\n", view
);
1925 return ERROR_SUCCESS
;
1928 static UINT
TABLE_add_ref(struct tagMSIVIEW
*view
)
1930 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1932 TRACE("%p %d\n", view
, tv
->table
->ref_count
);
1933 return InterlockedIncrement(&tv
->table
->ref_count
);
1936 static UINT
TABLE_remove_column(struct tagMSIVIEW
*view
, UINT number
)
1938 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1939 MSIRECORD
*rec
= NULL
;
1940 MSIVIEW
*columns
= NULL
;
1943 if (tv
->table
->col_count
!= number
)
1944 return ERROR_BAD_QUERY_SYNTAX
;
1946 if (tv
->table
->colinfo
[number
-1].type
& MSITYPE_TEMPORARY
)
1948 UINT size
= tv
->table
->colinfo
[number
-1].offset
;
1949 tv
->table
->col_count
--;
1950 tv
->table
->colinfo
= msi_realloc( tv
->table
->colinfo
, sizeof(*tv
->table
->colinfo
) * tv
->table
->col_count
);
1952 for (row
= 0; row
< tv
->table
->row_count
; row
++)
1953 tv
->table
->data
[row
] = msi_realloc( tv
->table
->data
[row
], size
);
1954 return ERROR_SUCCESS
;
1957 rec
= MSI_CreateRecord(2);
1959 return ERROR_OUTOFMEMORY
;
1961 MSI_RecordSetStringW(rec
, 1, tv
->name
);
1962 MSI_RecordSetInteger(rec
, 2, number
);
1964 r
= TABLE_CreateView(tv
->db
, L
"_Columns", &columns
);
1965 if (r
!= ERROR_SUCCESS
)
1967 msiobj_release(&rec
->hdr
);
1971 r
= msi_table_find_row((MSITABLEVIEW
*)columns
, rec
, &row
, NULL
);
1972 if (r
!= ERROR_SUCCESS
)
1975 r
= TABLE_delete_row(columns
, row
);
1976 if (r
!= ERROR_SUCCESS
)
1979 msi_update_table_columns(tv
->db
, tv
->name
);
1982 msiobj_release(&rec
->hdr
);
1983 columns
->ops
->delete(columns
);
1987 static UINT
TABLE_release(struct tagMSIVIEW
*view
)
1989 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
1990 INT ref
= tv
->table
->ref_count
;
1994 TRACE("%p %d\n", view
, ref
);
1996 ref
= InterlockedDecrement(&tv
->table
->ref_count
);
1999 for (i
= tv
->table
->col_count
- 1; i
>= 0; i
--)
2001 if (tv
->table
->colinfo
[i
].type
& MSITYPE_TEMPORARY
)
2003 r
= TABLE_remove_column(view
, tv
->table
->colinfo
[i
].number
);
2004 if (r
!= ERROR_SUCCESS
)
2013 if (!tv
->table
->col_count
)
2015 list_remove(&tv
->table
->entry
);
2016 free_table(tv
->table
);
2024 static UINT
TABLE_add_column(struct tagMSIVIEW
*view
, LPCWSTR column
,
2025 INT type
, BOOL hold
)
2027 UINT i
, r
, table_id
, col_id
, size
, offset
;
2028 BOOL temporary
= type
& MSITYPE_TEMPORARY
;
2029 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2030 MSICOLUMNINFO
*colinfo
;
2032 if (temporary
&& !hold
&& !tv
->table
->ref_count
)
2033 return ERROR_SUCCESS
;
2035 if (!temporary
&& tv
->table
->col_count
&&
2036 tv
->table
->colinfo
[tv
->table
->col_count
-1].type
& MSITYPE_TEMPORARY
)
2037 return ERROR_BAD_QUERY_SYNTAX
;
2039 for (i
= 0; i
< tv
->table
->col_count
; i
++)
2041 if (!wcscmp(tv
->table
->colinfo
[i
].colname
, column
))
2042 return ERROR_BAD_QUERY_SYNTAX
;
2045 colinfo
= msi_realloc(tv
->table
->colinfo
, sizeof(*tv
->table
->colinfo
) * (tv
->table
->col_count
+ 1));
2047 return ERROR_OUTOFMEMORY
;
2048 tv
->table
->colinfo
= colinfo
;
2050 r
= msi_string2id( tv
->db
->strings
, tv
->name
, -1, &table_id
);
2051 if (r
!= ERROR_SUCCESS
)
2053 col_id
= msi_add_string( tv
->db
->strings
, column
, -1, !temporary
);
2055 colinfo
[tv
->table
->col_count
].tablename
= msi_string_lookup( tv
->db
->strings
, table_id
, NULL
);
2056 colinfo
[tv
->table
->col_count
].number
= tv
->table
->col_count
+ 1;
2057 colinfo
[tv
->table
->col_count
].colname
= msi_string_lookup( tv
->db
->strings
, col_id
, NULL
);
2058 colinfo
[tv
->table
->col_count
].type
= type
;
2059 colinfo
[tv
->table
->col_count
].offset
= 0;
2060 colinfo
[tv
->table
->col_count
].hash_table
= NULL
;
2061 tv
->table
->col_count
++;
2063 table_calc_column_offsets( tv
->db
, tv
->table
->colinfo
, tv
->table
->col_count
);
2065 size
= msi_table_get_row_size( tv
->db
, tv
->table
->colinfo
, tv
->table
->col_count
, LONG_STR_BYTES
);
2066 offset
= tv
->table
->colinfo
[tv
->table
->col_count
- 1].offset
;
2067 for (i
= 0; i
< tv
->table
->row_count
; i
++)
2069 BYTE
*data
= msi_realloc( tv
->table
->data
[i
], size
);
2072 tv
->table
->col_count
--;
2073 return ERROR_OUTOFMEMORY
;
2076 tv
->table
->data
[i
] = data
;
2077 memset(data
+ offset
, 0, size
- offset
);
2085 rec
= MSI_CreateRecord(4);
2088 tv
->table
->col_count
--;
2089 return ERROR_OUTOFMEMORY
;
2092 MSI_RecordSetStringW(rec
, 1, tv
->name
);
2093 MSI_RecordSetInteger(rec
, 2, tv
->table
->col_count
);
2094 MSI_RecordSetStringW(rec
, 3, column
);
2095 MSI_RecordSetInteger(rec
, 4, type
);
2097 r
= TABLE_CreateView(tv
->db
, L
"_Columns", &columns
);
2098 if (r
!= ERROR_SUCCESS
)
2100 tv
->table
->col_count
--;
2101 msiobj_release(&rec
->hdr
);
2105 r
= TABLE_insert_row(columns
, rec
, -1, FALSE
);
2106 columns
->ops
->delete(columns
);
2107 msiobj_release(&rec
->hdr
);
2108 if (r
!= ERROR_SUCCESS
)
2110 tv
->table
->col_count
--;
2115 TABLE_add_ref(view
);
2116 return ERROR_SUCCESS
;
2119 static UINT
TABLE_drop(struct tagMSIVIEW
*view
)
2121 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2122 MSIVIEW
*tables
= NULL
;
2123 MSIRECORD
*rec
= NULL
;
2127 TRACE("dropping table %s\n", debugstr_w(tv
->name
));
2129 for (i
= tv
->table
->col_count
- 1; i
>= 0; i
--)
2131 r
= TABLE_remove_column(view
, tv
->table
->colinfo
[i
].number
);
2132 if (r
!= ERROR_SUCCESS
)
2136 rec
= MSI_CreateRecord(1);
2138 return ERROR_OUTOFMEMORY
;
2140 MSI_RecordSetStringW(rec
, 1, tv
->name
);
2142 r
= TABLE_CreateView(tv
->db
, L
"_Tables", &tables
);
2143 if (r
!= ERROR_SUCCESS
)
2145 msiobj_release(&rec
->hdr
);
2149 r
= msi_table_find_row((MSITABLEVIEW
*)tables
, rec
, &row
, NULL
);
2150 if (r
!= ERROR_SUCCESS
)
2153 r
= TABLE_delete_row(tables
, row
);
2154 if (r
!= ERROR_SUCCESS
)
2157 list_remove(&tv
->table
->entry
);
2158 free_table(tv
->table
);
2161 msiobj_release(&rec
->hdr
);
2162 tables
->ops
->delete(tables
);
2167 static const MSIVIEWOPS table_ops
=
2179 TABLE_get_dimensions
,
2180 TABLE_get_column_info
,
2190 UINT
TABLE_CreateView( MSIDATABASE
*db
, LPCWSTR name
, MSIVIEW
**view
)
2195 TRACE("%p %s %p\n", db
, debugstr_w(name
), view
);
2197 if ( !wcscmp( name
, L
"_Streams" ) )
2198 return STREAMS_CreateView( db
, view
);
2199 else if ( !wcscmp( name
, L
"_Storages" ) )
2200 return STORAGES_CreateView( db
, view
);
2202 sz
= FIELD_OFFSET( MSITABLEVIEW
, name
[lstrlenW( name
) + 1] );
2203 tv
= msi_alloc_zero( sz
);
2205 return ERROR_FUNCTION_FAILED
;
2207 r
= get_table( db
, name
, &tv
->table
);
2208 if( r
!= ERROR_SUCCESS
)
2211 WARN("table not found\n");
2215 TRACE("table %p found with %d columns\n", tv
->table
, tv
->table
->col_count
);
2217 /* fill the structure */
2218 tv
->view
.ops
= &table_ops
;
2220 tv
->columns
= tv
->table
->colinfo
;
2221 tv
->num_cols
= tv
->table
->col_count
;
2222 tv
->row_size
= msi_table_get_row_size( db
, tv
->table
->colinfo
, tv
->table
->col_count
, LONG_STR_BYTES
);
2224 TRACE("%s one row is %d bytes\n", debugstr_w(name
), tv
->row_size
);
2226 *view
= (MSIVIEW
*) tv
;
2227 lstrcpyW( tv
->name
, name
);
2229 return ERROR_SUCCESS
;
2232 static WCHAR
* create_key_string(MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
2234 DWORD i
, p
, len
, key_len
= 0;
2237 for (i
= 0; i
< tv
->num_cols
; i
++)
2239 if (!(tv
->columns
[i
].type
& MSITYPE_KEY
))
2241 if (MSI_RecordGetStringW( rec
, i
+1, NULL
, &len
) == ERROR_SUCCESS
)
2246 key
= msi_alloc( key_len
* sizeof(WCHAR
) );
2251 for (i
= 0; i
< tv
->num_cols
; i
++)
2253 if (!(tv
->columns
[i
].type
& MSITYPE_KEY
))
2258 if (MSI_RecordGetStringW( rec
, i
+1, key
+ p
, &len
) == ERROR_SUCCESS
)
2264 static UINT
msi_record_stream_name( const MSITABLEVIEW
*tv
, MSIRECORD
*rec
, LPWSTR name
, UINT
*len
)
2266 UINT p
= 0, l
, i
, r
;
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
;
2330 UINT i
, p
, r
, len
, qlen
;
2332 if (!wcscmp( tv
->name
, L
"_Columns" ))
2334 ERR( "trying to modify existing column\n" );
2335 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2338 if (!wcscmp( tv
->name
, L
"_Tables" ))
2340 ERR( "trying to modify existing table\n" );
2341 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2344 key
= create_key_string( tv
, rec
);
2346 return ERROR_OUTOFMEMORY
;
2348 r
= msi_view_get_row( tv
->db
, view
, row
, &old_rec
);
2349 if (r
!= ERROR_SUCCESS
)
2352 for (i
= 0; i
< tv
->num_cols
; i
++)
2354 if (!(mask
& (1 << i
)))
2356 if (tv
->columns
[i
].type
& MSITYPE_KEY
)
2359 qlen
= p
= ARRAY_SIZE( query_pfx
) - 1;
2360 qlen
+= wcslen( tv
->name
) + 3; /* strlen("','") */
2361 qlen
+= wcslen( tv
->columns
[i
].colname
) + 3;
2362 qlen
+= wcslen( key
) + 3;
2363 if (MSITYPE_IS_BINARY( tv
->columns
[i
].type
))
2364 r
= msi_record_stream_name( tv
, rec
, NULL
, &len
);
2366 r
= MSI_RecordGetStringW( rec
, i
+ 1, NULL
, &len
);
2367 if (r
!= ERROR_SUCCESS
)
2370 msiobj_release( &old_rec
->hdr
);
2375 if (old_rec
&& (r
= MSI_RecordGetStringW( old_rec
, i
+1, NULL
, &len
)))
2377 msiobj_release( &old_rec
->hdr
);
2381 qlen
+= len
+ 3; /* strlen("')") + 1 */
2383 if (qlen
> ARRAY_SIZE(buf
))
2385 query
= msi_alloc( qlen
* sizeof(WCHAR
) );
2389 msiobj_release( &old_rec
->hdr
);
2391 return ERROR_OUTOFMEMORY
;
2399 memcpy( query
, query_pfx
, p
* sizeof(WCHAR
) );
2400 len
= wcslen( tv
->name
);
2401 memcpy( query
+ p
, tv
->name
, len
* sizeof(WCHAR
) );
2406 len
= wcslen( tv
->columns
[i
].colname
);
2407 memcpy( query
+ p
, tv
->columns
[i
].colname
, len
* sizeof(WCHAR
) );
2412 len
= wcslen( key
);
2413 memcpy( query
+ p
, key
, len
* sizeof(WCHAR
) );
2419 if (MSITYPE_IS_BINARY( tv
->columns
[i
].type
))
2420 msi_record_stream_name( tv
, rec
, query
+ p
, &len
);
2422 MSI_RecordGetStringW( rec
, i
+ 1, query
+ p
, &len
);
2430 MSI_RecordGetStringW( old_rec
, i
+ 1, query
+ p
, &len
);
2437 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2440 if (r
!= ERROR_SUCCESS
)
2443 msiobj_release( &old_rec
->hdr
);
2448 r
= MSI_ViewExecute( q
, NULL
);
2449 msiobj_release( &q
->hdr
);
2450 if (r
!= ERROR_SUCCESS
)
2453 msiobj_release( &old_rec
->hdr
);
2460 msiobj_release( &old_rec
->hdr
);
2462 return ERROR_SUCCESS
;
2465 static UINT
TransformView_create_table( MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
2467 static const WCHAR query_fmt
[] =
2468 L
"INSERT INTO `_TransformView` (`Table`, `Column`, `new`) VALUES ('%s', 'CREATE', 1)";
2470 WCHAR buf
[256], *query
= buf
;
2476 name
= msi_record_get_string( rec
, 1, &len
);
2478 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2480 len
= _snwprintf( NULL
, 0, query_fmt
, name
) + 1;
2481 if (len
> ARRAY_SIZE(buf
))
2483 query
= msi_alloc( len
* sizeof(WCHAR
) );
2485 return ERROR_OUTOFMEMORY
;
2487 swprintf( query
, len
, query_fmt
, name
);
2489 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2492 if (r
!= ERROR_SUCCESS
)
2495 r
= MSI_ViewExecute( q
, NULL
);
2496 msiobj_release( &q
->hdr
);
2500 static UINT
TransformView_add_column( MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
2502 static const WCHAR query_pfx
[] =
2503 L
"INSERT INTO `_TransformView` (`new`, `Table`, `Current`, `Column`, `Data`) VALUES (1, '";
2505 WCHAR buf
[256], *query
= buf
;
2506 UINT i
, p
, len
, r
, qlen
;
2509 qlen
= p
= wcslen( query_pfx
);
2510 for (i
= 1; i
<= 4; i
++)
2512 r
= MSI_RecordGetStringW( rec
, i
, NULL
, &len
);
2513 if (r
!= ERROR_SUCCESS
)
2515 qlen
+= len
+ 3; /* strlen( "','" ) */
2518 if (qlen
> ARRAY_SIZE(buf
))
2520 query
= msi_alloc( len
* sizeof(WCHAR
) );
2523 return ERROR_OUTOFMEMORY
;
2526 memcpy( query
, query_pfx
, p
* sizeof(WCHAR
) );
2527 for (i
= 1; i
<= 4; i
++)
2530 MSI_RecordGetStringW( rec
, i
, query
+ p
, &len
);
2542 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2545 if (r
!= ERROR_SUCCESS
)
2548 r
= MSI_ViewExecute( q
, NULL
);
2549 msiobj_release( &q
->hdr
);
2553 static UINT
TransformView_insert_row( MSIVIEW
*view
, MSIRECORD
*rec
, UINT row
, BOOL temporary
)
2555 static const WCHAR query_fmt
[] =
2556 L
"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`) VALUES (1, '%s', 'INSERT', '%s')";
2558 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2559 WCHAR buf
[256], *query
= buf
;
2565 if (!wcscmp(tv
->name
, L
"_Tables"))
2566 return TransformView_create_table( tv
, rec
);
2568 if (!wcscmp(tv
->name
, L
"_Columns"))
2569 return TransformView_add_column( tv
, rec
);
2571 key
= create_key_string( tv
, rec
);
2573 return ERROR_OUTOFMEMORY
;
2575 len
= _snwprintf( NULL
, 0, query_fmt
, tv
->name
, key
) + 1;
2576 if (len
> ARRAY_SIZE(buf
))
2578 query
= msi_alloc( len
* sizeof(WCHAR
) );
2582 return ERROR_OUTOFMEMORY
;
2585 swprintf( query
, len
, query_fmt
, tv
->name
, key
);
2588 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2591 if (r
!= ERROR_SUCCESS
)
2594 r
= MSI_ViewExecute( q
, NULL
);
2595 msiobj_release( &q
->hdr
);
2596 if (r
!= ERROR_SUCCESS
)
2599 return TransformView_set_row( view
, row
, rec
, ~0 );
2602 static UINT
TransformView_drop_table( MSITABLEVIEW
*tv
, UINT row
)
2604 static const WCHAR query_pfx
[] = L
"INSERT INTO `_TransformView` ( `new`, `Table`, `Column` ) VALUES ( 1, '";
2605 static const WCHAR query_sfx
[] = L
"', 'DROP' )";
2607 WCHAR buf
[256], *query
= buf
;
2608 UINT r
, table_id
, len
;
2613 r
= TABLE_fetch_int( &tv
->view
, row
, 1, &table_id
);
2614 if (r
!= ERROR_SUCCESS
)
2617 table
= msi_string_lookup( tv
->db
->strings
, table_id
, &table_len
);
2619 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2621 len
= ARRAY_SIZE(query_pfx
) - 1 + table_len
+ ARRAY_SIZE(query_sfx
);
2622 if (len
> ARRAY_SIZE(buf
))
2624 query
= msi_alloc( len
* sizeof(WCHAR
) );
2626 return ERROR_OUTOFMEMORY
;
2629 memcpy( query
, query_pfx
, ARRAY_SIZE(query_pfx
) * sizeof(WCHAR
) );
2630 len
= ARRAY_SIZE(query_pfx
) - 1;
2631 memcpy( query
+ len
, table
, table_len
* sizeof(WCHAR
) );
2633 memcpy( query
+ len
, query_sfx
, ARRAY_SIZE(query_sfx
) * sizeof(WCHAR
) );
2635 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2638 if (r
!= ERROR_SUCCESS
)
2641 r
= MSI_ViewExecute( q
, NULL
);
2642 msiobj_release( &q
->hdr
);
2646 static UINT
TransformView_delete_row( MSIVIEW
*view
, UINT row
)
2648 static const WCHAR query_pfx
[] = L
"INSERT INTO `_TransformView` ( `new`, `Table`, `Column`, `Row`) VALUES ( 1, '";
2649 static const WCHAR query_column
[] = L
"', 'DELETE', '";
2650 static const WCHAR query_sfx
[] = L
"')";
2652 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2653 WCHAR
*key
, buf
[256], *query
= buf
;
2654 UINT r
, len
, name_len
, key_len
;
2658 if (!wcscmp( tv
->name
, L
"_Columns" ))
2660 ERR("trying to remove column\n");
2661 return ERROR_INSTALL_TRANSFORM_FAILURE
;
2664 if (!wcscmp( tv
->name
, L
"_Tables" ))
2665 return TransformView_drop_table( tv
, row
);
2667 r
= msi_view_get_row( tv
->db
, view
, row
, &rec
);
2668 if (r
!= ERROR_SUCCESS
)
2671 key
= create_key_string( tv
, rec
);
2672 msiobj_release( &rec
->hdr
);
2674 return ERROR_OUTOFMEMORY
;
2676 name_len
= wcslen( tv
->name
);
2677 key_len
= wcslen( key
);
2678 len
= ARRAY_SIZE(query_pfx
) + name_len
+ ARRAY_SIZE(query_column
) + key_len
+ ARRAY_SIZE(query_sfx
) - 2;
2679 if (len
> ARRAY_SIZE(buf
))
2681 query
= msi_alloc( len
* sizeof(WCHAR
) );
2686 return ERROR_OUTOFMEMORY
;
2690 memcpy( query
, query_pfx
, ARRAY_SIZE(query_pfx
) * sizeof(WCHAR
) );
2691 len
= ARRAY_SIZE(query_pfx
) - 1;
2692 memcpy( query
+ len
, tv
->name
, name_len
* sizeof(WCHAR
) );
2694 memcpy( query
+ len
, query_column
, ARRAY_SIZE(query_column
) * sizeof(WCHAR
) );
2695 len
+= ARRAY_SIZE(query_column
) - 1;
2696 memcpy( query
+ len
, key
, key_len
* sizeof(WCHAR
) );
2698 memcpy( query
+ len
, query_sfx
, ARRAY_SIZE(query_sfx
) * sizeof(WCHAR
) );
2701 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2704 if (r
!= ERROR_SUCCESS
)
2707 r
= MSI_ViewExecute( q
, NULL
);
2708 msiobj_release( &q
->hdr
);
2712 static UINT
TransformView_execute( MSIVIEW
*view
, MSIRECORD
*record
)
2714 return ERROR_SUCCESS
;
2717 static UINT
TransformView_close( MSIVIEW
*view
)
2719 return ERROR_SUCCESS
;
2722 static UINT
TransformView_get_dimensions( MSIVIEW
*view
, UINT
*rows
, UINT
*cols
)
2724 return TABLE_get_dimensions( view
, rows
, cols
);
2727 static UINT
TransformView_get_column_info( MSIVIEW
*view
, UINT n
, LPCWSTR
*name
, UINT
*type
,
2728 BOOL
*temporary
, LPCWSTR
*table_name
)
2730 return TABLE_get_column_info( view
, n
, name
, type
, temporary
, table_name
);
2733 static UINT
TransformView_delete( MSIVIEW
*view
)
2735 MSITABLEVIEW
*tv
= (MSITABLEVIEW
*)view
;
2736 if (!tv
->table
|| tv
->columns
!= tv
->table
->colinfo
)
2737 msi_free( tv
->columns
);
2738 return TABLE_delete( view
);
2741 static const MSIVIEWOPS transform_view_ops
=
2743 TransformView_fetch_int
,
2744 TransformView_fetch_stream
,
2748 TransformView_set_row
,
2749 TransformView_insert_row
,
2750 TransformView_delete_row
,
2751 TransformView_execute
,
2752 TransformView_close
,
2753 TransformView_get_dimensions
,
2754 TransformView_get_column_info
,
2756 TransformView_delete
,
2764 UINT
TransformView_Create( MSIDATABASE
*db
, string_table
*st
, LPCWSTR name
, MSIVIEW
**view
)
2766 static const WCHAR query_pfx
[] = L
"SELECT `Column`, `Data`, `Current` FROM `_TransformView` WHERE `Table`='";
2767 static const WCHAR query_sfx
[] = L
"' AND `Row` IS NULL AND `Current` IS NOT NULL AND `new` = 1";
2769 WCHAR buf
[256], *query
= buf
;
2770 UINT r
, len
, name_len
, size
, add_col
;
2771 MSICOLUMNINFO
*colinfo
;
2776 name_len
= wcslen( name
);
2778 r
= TABLE_CreateView( db
, name
, view
);
2779 if (r
== ERROR_INVALID_PARAMETER
)
2781 /* table does not exist */
2782 size
= FIELD_OFFSET( MSITABLEVIEW
, name
[name_len
+ 1] );
2783 tv
= msi_alloc_zero( size
);
2785 return ERROR_OUTOFMEMORY
;
2788 memcpy( tv
->name
, name
, name_len
* sizeof(WCHAR
) );
2789 *view
= (MSIVIEW
*)tv
;
2791 else if (r
!= ERROR_SUCCESS
)
2797 tv
= (MSITABLEVIEW
*)*view
;
2800 tv
->view
.ops
= &transform_view_ops
;
2802 len
= ARRAY_SIZE(query_pfx
) + name_len
+ ARRAY_SIZE(query_sfx
) - 1;
2803 if (len
> ARRAY_SIZE(buf
))
2805 query
= msi_alloc( len
* sizeof(WCHAR
) );
2809 return ERROR_OUTOFMEMORY
;
2812 memcpy( query
, query_pfx
, ARRAY_SIZE(query_pfx
) * sizeof(WCHAR
) );
2813 len
= ARRAY_SIZE(query_pfx
) - 1;
2814 memcpy( query
+ len
, name
, name_len
* sizeof(WCHAR
) );
2816 memcpy( query
+ len
, query_sfx
, ARRAY_SIZE(query_sfx
) * sizeof(WCHAR
) );
2818 r
= MSI_DatabaseOpenViewW( tv
->db
, query
, &q
);
2821 if (r
!= ERROR_SUCCESS
)
2827 r
= MSI_ViewExecute( q
, NULL
);
2828 if (r
!= ERROR_SUCCESS
)
2834 r
= q
->view
->ops
->get_dimensions( q
->view
, &add_col
, NULL
);
2835 if (r
!= ERROR_SUCCESS
)
2838 msiobj_release( &q
->hdr
);
2845 msiobj_release( &q
->hdr
);
2846 return ERROR_SUCCESS
;
2849 colinfo
= msi_alloc_zero( (add_col
+ tv
->num_cols
) * sizeof(*colinfo
) );
2853 msiobj_release( &q
->hdr
);
2858 while (MSI_ViewFetch( q
, &rec
) == ERROR_SUCCESS
)
2861 const WCHAR
*name
= msi_record_get_string( rec
, 1, &name_len
);
2862 const WCHAR
*type
= msi_record_get_string( rec
, 2, NULL
);
2865 idx
= _wtoi( msi_record_get_string(rec
, 3, NULL
) );
2866 colinfo
[idx
- 1].number
= idx
;
2867 colinfo
[idx
- 1].type
= _wtoi( type
);
2869 r
= msi_string2id( st
, name
, name_len
, &name_id
);
2870 if (r
== ERROR_SUCCESS
)
2871 colinfo
[idx
- 1].colname
= msi_string_lookup( st
, name_id
, NULL
);
2873 ERR( "column name %s is not defined in strings table\n", wine_dbgstr_w(name
) );
2874 msiobj_release( &rec
->hdr
);
2877 msiobj_release( &q
->hdr
);
2879 memcpy( colinfo
, tv
->columns
, tv
->num_cols
* sizeof(*colinfo
) );
2880 tv
->columns
= colinfo
;
2881 tv
->num_cols
+= add_col
;
2882 return ERROR_SUCCESS
;
2885 UINT
MSI_CommitTables( MSIDATABASE
*db
)
2887 UINT r
, bytes_per_strref
;
2889 MSITABLE
*table
= NULL
;
2893 r
= msi_save_string_table( db
->strings
, db
->storage
, &bytes_per_strref
);
2894 if( r
!= ERROR_SUCCESS
)
2896 WARN("failed to save string table r=%08x\n",r
);
2900 LIST_FOR_EACH_ENTRY( table
, &db
->tables
, MSITABLE
, entry
)
2902 r
= save_table( db
, table
, bytes_per_strref
);
2903 if( r
!= ERROR_SUCCESS
)
2905 WARN("failed to save table %s (r=%08x)\n",
2906 debugstr_w(table
->name
), r
);
2911 hr
= IStorage_Commit( db
->storage
, 0 );
2914 WARN("failed to commit changes 0x%08x\n", hr
);
2915 r
= ERROR_FUNCTION_FAILED
;
2920 MSICONDITION
MSI_DatabaseIsTablePersistent( MSIDATABASE
*db
, LPCWSTR table
)
2925 TRACE("%p %s\n", db
, debugstr_w(table
));
2928 return MSICONDITION_ERROR
;
2930 r
= get_table( db
, table
, &t
);
2931 if (r
!= ERROR_SUCCESS
)
2932 return MSICONDITION_NONE
;
2934 return t
->persistent
;
2937 static UINT
read_raw_int(const BYTE
*data
, UINT col
, UINT bytes
)
2941 for (i
= 0; i
< bytes
; i
++)
2942 ret
+= (data
[col
+ i
] << i
* 8);
2947 static UINT
msi_record_encoded_stream_name( const MSITABLEVIEW
*tv
, MSIRECORD
*rec
, LPWSTR
*pstname
)
2952 TRACE("%p %p\n", tv
, rec
);
2954 r
= msi_record_stream_name( tv
, rec
, NULL
, &len
);
2955 if (r
!= ERROR_SUCCESS
)
2959 name
= msi_alloc( len
* sizeof(WCHAR
) );
2961 return ERROR_OUTOFMEMORY
;
2963 r
= msi_record_stream_name( tv
, rec
, name
, &len
);
2964 if (r
!= ERROR_SUCCESS
)
2970 *pstname
= encode_streamname( FALSE
, name
);
2972 return ERROR_SUCCESS
;
2975 static MSIRECORD
*msi_get_transform_record( const MSITABLEVIEW
*tv
, const string_table
*st
,
2976 IStorage
*stg
, const BYTE
*rawdata
, UINT bytes_per_strref
)
2978 UINT i
, val
, ofs
= 0;
2980 MSICOLUMNINFO
*columns
= tv
->columns
;
2983 mask
= rawdata
[0] | (rawdata
[1] << 8);
2986 rec
= MSI_CreateRecord( tv
->num_cols
);
2991 for( i
=0; i
<tv
->num_cols
; i
++ )
2993 if ( (mask
&1) && (i
>=(mask
>>8)) )
2995 /* all keys must be present */
2996 if ( (~mask
&1) && (~columns
[i
].type
& MSITYPE_KEY
) && ((1<<i
) & ~mask
) )
2999 if( MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
3002 IStream
*stm
= NULL
;
3005 ofs
+= bytes_per_column( tv
->db
, &columns
[i
], bytes_per_strref
);
3007 r
= msi_record_encoded_stream_name( tv
, rec
, &encname
);
3008 if ( r
!= ERROR_SUCCESS
)
3010 msiobj_release( &rec
->hdr
);
3013 r
= IStorage_OpenStream( stg
, encname
, NULL
, STGM_READ
| STGM_SHARE_EXCLUSIVE
, 0, &stm
);
3014 if ( r
!= ERROR_SUCCESS
)
3016 msiobj_release( &rec
->hdr
);
3017 msi_free( encname
);
3021 MSI_RecordSetStream( rec
, i
+1, stm
);
3022 TRACE(" field %d [%s]\n", i
+1, debugstr_w(encname
));
3023 msi_free( encname
);
3025 else if( columns
[i
].type
& MSITYPE_STRING
)
3030 val
= read_raw_int(rawdata
, ofs
, bytes_per_strref
);
3031 sval
= msi_string_lookup( st
, val
, &len
);
3032 msi_record_set_string( rec
, i
+1, sval
, len
);
3033 TRACE(" field %d [%s]\n", i
+1, debugstr_wn(sval
, len
));
3034 ofs
+= bytes_per_strref
;
3038 UINT n
= bytes_per_column( tv
->db
, &columns
[i
], bytes_per_strref
);
3042 val
= read_raw_int(rawdata
, ofs
, n
);
3044 MSI_RecordSetInteger( rec
, i
+1, val
-0x8000 );
3045 TRACE(" field %d [0x%04x]\n", i
+1, val
);
3048 val
= read_raw_int(rawdata
, ofs
, n
);
3050 MSI_RecordSetInteger( rec
, i
+1, val
^0x80000000 );
3051 TRACE(" field %d [0x%08x]\n", i
+1, val
);
3054 ERR("oops - unknown column width %d\n", n
);
3063 static void dump_table( const string_table
*st
, const USHORT
*rawdata
, UINT rawsize
)
3066 for (i
= 0; i
< rawsize
/ 2; i
++)
3069 const WCHAR
*sval
= msi_string_lookup( st
, rawdata
[i
], &len
);
3070 MESSAGE(" %04x %s\n", rawdata
[i
], debugstr_wn(sval
, len
) );
3074 static UINT
* msi_record_to_row( const MSITABLEVIEW
*tv
, MSIRECORD
*rec
)
3078 data
= msi_alloc( tv
->num_cols
*sizeof (UINT
) );
3079 for( i
=0; i
<tv
->num_cols
; i
++ )
3083 if ( ~tv
->columns
[i
].type
& MSITYPE_KEY
)
3086 /* turn the transform column value into a row value */
3087 if ( ( tv
->columns
[i
].type
& MSITYPE_STRING
) &&
3088 ! MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
3091 const WCHAR
*str
= msi_record_get_string( rec
, i
+1, &len
);
3094 r
= msi_string2id( tv
->db
->strings
, str
, len
, &data
[i
] );
3096 /* if there's no matching string in the string table,
3097 these keys can't match any record, so fail now. */
3098 if (r
!= ERROR_SUCCESS
)
3108 if (int_to_table_storage( tv
, i
+ 1, MSI_RecordGetInteger( rec
, i
+ 1 ), &data
[i
] ))
3118 static UINT
msi_row_matches( MSITABLEVIEW
*tv
, UINT row
, const UINT
*data
, UINT
*column
)
3120 UINT i
, r
, x
, ret
= ERROR_FUNCTION_FAILED
;
3122 for( i
=0; i
<tv
->num_cols
; i
++ )
3124 if ( ~tv
->columns
[i
].type
& MSITYPE_KEY
)
3127 /* turn the transform column value into a row value */
3128 r
= TABLE_fetch_int( &tv
->view
, row
, i
+1, &x
);
3129 if ( r
!= ERROR_SUCCESS
)
3131 ERR("TABLE_fetch_int shouldn't fail here\n");
3135 /* if this key matches, move to the next column */
3138 ret
= ERROR_FUNCTION_FAILED
;
3141 if (column
) *column
= i
;
3142 ret
= ERROR_SUCCESS
;
3147 static UINT
msi_table_find_row( MSITABLEVIEW
*tv
, MSIRECORD
*rec
, UINT
*row
, UINT
*column
)
3149 UINT i
, r
= ERROR_FUNCTION_FAILED
, *data
;
3151 data
= msi_record_to_row( tv
, rec
);
3154 for( i
= 0; i
< tv
->table
->row_count
; i
++ )
3156 r
= msi_row_matches( tv
, i
, data
, column
);
3157 if( r
== ERROR_SUCCESS
)
3173 static UINT
msi_table_load_transform( MSIDATABASE
*db
, IStorage
*stg
,
3174 string_table
*st
, TRANSFORMDATA
*transform
,
3175 UINT bytes_per_strref
, int err_cond
)
3177 BYTE
*rawdata
= NULL
;
3178 MSITABLEVIEW
*tv
= NULL
;
3179 UINT r
, n
, sz
, i
, mask
, num_cols
, colcol
= 0, rawsize
= 0;
3180 MSIRECORD
*rec
= NULL
;
3185 return ERROR_SUCCESS
;
3187 name
= transform
->name
;
3190 TRACE("%p %p %p %s\n", db
, stg
, st
, debugstr_w(name
) );
3192 /* read the transform data */
3193 read_stream_data( stg
, name
, TRUE
, &rawdata
, &rawsize
);
3196 TRACE("table %s empty\n", debugstr_w(name
) );
3197 return ERROR_INVALID_TABLE
;
3200 /* create a table view */
3201 if ( err_cond
& MSITRANSFORM_ERROR_VIEWTRANSFORM
)
3202 r
= TransformView_Create( db
, st
, name
, (MSIVIEW
**) &tv
);
3204 r
= TABLE_CreateView( db
, name
, (MSIVIEW
**) &tv
);
3205 if( r
!= ERROR_SUCCESS
)
3208 r
= tv
->view
.ops
->execute( &tv
->view
, NULL
);
3209 if( r
!= ERROR_SUCCESS
)
3212 TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
3213 debugstr_w(name
), tv
->num_cols
, tv
->row_size
, rawsize
);
3215 /* interpret the data */
3216 for (n
= 0; n
< rawsize
;)
3218 mask
= rawdata
[n
] | (rawdata
[n
+ 1] << 8);
3222 * if the low bit is set, columns are continuous and
3223 * the number of columns is specified in the high byte
3226 num_cols
= mask
>> 8;
3227 if (num_cols
> tv
->num_cols
)
3229 ERR("excess columns in transform: %u > %u\n", num_cols
, tv
->num_cols
);
3233 for (i
= 0; i
< num_cols
; i
++)
3235 if( (tv
->columns
[i
].type
& MSITYPE_STRING
) &&
3236 ! MSITYPE_IS_BINARY(tv
->columns
[i
].type
) )
3237 sz
+= bytes_per_strref
;
3239 sz
+= bytes_per_column( tv
->db
, &tv
->columns
[i
], bytes_per_strref
);
3245 * If the low bit is not set, mask is a bitmask.
3246 * Excepting for key fields, which are always present,
3247 * each bit indicates that a field is present in the transform record.
3249 * mask == 0 is a special case ... only the keys will be present
3250 * and it means that this row should be deleted.
3253 num_cols
= tv
->num_cols
;
3254 for (i
= 0; i
< num_cols
; i
++)
3256 if ((tv
->columns
[i
].type
& MSITYPE_KEY
) || ((1 << i
) & mask
))
3258 if ((tv
->columns
[i
].type
& MSITYPE_STRING
) &&
3259 !MSITYPE_IS_BINARY(tv
->columns
[i
].type
))
3260 sz
+= bytes_per_strref
;
3262 sz
+= bytes_per_column( tv
->db
, &tv
->columns
[i
], bytes_per_strref
);
3267 /* check we didn't run of the end of the table */
3268 if (n
+ sz
> rawsize
)
3271 dump_table( st
, (USHORT
*)rawdata
, rawsize
);
3275 rec
= msi_get_transform_record( tv
, st
, stg
, &rawdata
[n
], bytes_per_strref
);
3280 UINT number
= MSI_NULL_INTEGER
;
3283 if (!wcscmp( name
, L
"_Columns" ))
3285 MSI_RecordGetStringW( rec
, 1, table
, &sz
);
3286 number
= MSI_RecordGetInteger( rec
, 2 );
3289 * Native msi seems writes nul into the Number (2nd) column of
3290 * the _Columns table when there are new columns
3292 if ( number
== MSI_NULL_INTEGER
)
3294 /* reset the column number on a new table */
3295 if (wcscmp( coltable
, table
))
3298 lstrcpyW( coltable
, table
);
3301 /* fix nul column numbers */
3302 MSI_RecordSetInteger( rec
, 2, ++colcol
);
3306 if (TRACE_ON(msidb
)) dump_record( rec
);
3309 r
= msi_table_find_row( tv
, rec
, &row
, NULL
);
3311 r
= ERROR_FUNCTION_FAILED
;
3312 if (r
== ERROR_SUCCESS
)
3316 TRACE("deleting row [%d]:\n", row
);
3317 r
= tv
->view
.ops
->delete_row( &tv
->view
, row
);
3318 if (r
!= ERROR_SUCCESS
)
3319 WARN("failed to delete row %u\n", r
);
3323 TRACE("modifying full row [%d]:\n", row
);
3324 r
= tv
->view
.ops
->set_row( &tv
->view
, row
, rec
, (1 << tv
->num_cols
) - 1 );
3325 if (r
!= ERROR_SUCCESS
)
3326 WARN("failed to modify row %u\n", r
);
3330 TRACE("modifying masked row [%d]:\n", row
);
3331 r
= tv
->view
.ops
->set_row( &tv
->view
, row
, rec
, mask
);
3332 if (r
!= ERROR_SUCCESS
)
3333 WARN("failed to modify row %u\n", r
);
3338 TRACE("inserting row\n");
3339 r
= tv
->view
.ops
->insert_row( &tv
->view
, rec
, -1, FALSE
);
3340 if (r
!= ERROR_SUCCESS
)
3341 WARN("failed to insert row %u\n", r
);
3344 if (!(err_cond
& MSITRANSFORM_ERROR_VIEWTRANSFORM
) &&
3345 !wcscmp( name
, L
"_Columns" ))
3346 msi_update_table_columns( db
, table
);
3348 msiobj_release( &rec
->hdr
);
3355 /* no need to free the table, it's associated with the database */
3356 msi_free( rawdata
);
3358 tv
->view
.ops
->delete( &tv
->view
);
3360 return ERROR_SUCCESS
;
3364 * msi_table_apply_transform
3366 * Enumerate the table transforms in a transform storage and apply each one.
3368 UINT
msi_table_apply_transform( MSIDATABASE
*db
, IStorage
*stg
, int err_cond
)
3370 struct list transforms
;
3371 IEnumSTATSTG
*stgenum
= NULL
;
3372 TRANSFORMDATA
*transform
;
3373 TRANSFORMDATA
*tables
= NULL
, *columns
= NULL
;
3376 string_table
*strings
;
3377 UINT ret
= ERROR_FUNCTION_FAILED
;
3378 UINT bytes_per_strref
;
3379 BOOL property_update
= FALSE
;
3380 MSIVIEW
*transform_view
= NULL
;
3382 TRACE("%p %p\n", db
, stg
);
3384 strings
= msi_load_string_table( stg
, &bytes_per_strref
);
3388 hr
= IStorage_EnumElements( stg
, 0, NULL
, 0, &stgenum
);
3392 list_init(&transforms
);
3396 MSITABLEVIEW
*tv
= NULL
;
3400 hr
= IEnumSTATSTG_Next( stgenum
, 1, &stat
, &count
);
3401 if (FAILED( hr
) || !count
)
3404 decode_streamname( stat
.pwcsName
, name
);
3405 CoTaskMemFree( stat
.pwcsName
);
3406 if ( name
[0] != 0x4840 )
3409 if ( !wcscmp( name
+1, L
"_StringPool" ) ||
3410 !wcscmp( name
+1, L
"_StringData" ) )
3413 transform
= msi_alloc_zero( sizeof(TRANSFORMDATA
) );
3417 list_add_tail( &transforms
, &transform
->entry
);
3419 transform
->name
= strdupW( name
+ 1 );
3421 if ( !wcscmp( transform
->name
, L
"_Tables" ) )
3423 else if (!wcscmp( transform
->name
, L
"_Columns" ) )
3424 columns
= transform
;
3425 else if (!wcscmp( transform
->name
, L
"Property" ))
3426 property_update
= TRUE
;
3428 TRACE("transform contains stream %s\n", debugstr_w(name
));
3430 /* load the table */
3431 if (TABLE_CreateView( db
, transform
->name
, (MSIVIEW
**) &tv
) != ERROR_SUCCESS
)
3434 if (tv
->view
.ops
->execute( &tv
->view
, NULL
) != ERROR_SUCCESS
)
3436 tv
->view
.ops
->delete( &tv
->view
);
3440 tv
->view
.ops
->delete( &tv
->view
);
3443 if (err_cond
& MSITRANSFORM_ERROR_VIEWTRANSFORM
)
3445 static const WCHAR create_query
[] = L
"CREATE TABLE `_TransformView` ( "
3446 L
"`Table` CHAR(0) NOT NULL TEMPORARY, `Column` CHAR(0) NOT NULL TEMPORARY, "
3447 L
"`Row` CHAR(0) TEMPORARY, `Data` CHAR(0) TEMPORARY, `Current` CHAR(0) TEMPORARY "
3448 L
"PRIMARY KEY `Table`, `Column`, `Row` ) HOLD";
3453 r
= MSI_DatabaseOpenViewW( db
, create_query
, &query
);
3454 if (r
!= ERROR_SUCCESS
)
3457 r
= MSI_ViewExecute( query
, NULL
);
3458 if (r
== ERROR_SUCCESS
)
3459 MSI_ViewClose( query
);
3460 msiobj_release( &query
->hdr
);
3461 if (r
!= ERROR_BAD_QUERY_SYNTAX
&& r
!= ERROR_SUCCESS
)
3464 if (TABLE_CreateView(db
, L
"_TransformView", &transform_view
) != ERROR_SUCCESS
)
3467 if (r
== ERROR_BAD_QUERY_SYNTAX
)
3468 transform_view
->ops
->add_ref( transform_view
);
3470 r
= transform_view
->ops
->add_column( transform_view
, L
"new",
3471 MSITYPE_TEMPORARY
| MSITYPE_NULLABLE
| 0x402 /* INT */, FALSE
);
3472 if (r
!= ERROR_SUCCESS
)
3477 * Apply _Tables and _Columns transforms first so that
3478 * the table metadata is correct, and empty tables exist.
3480 ret
= msi_table_load_transform( db
, stg
, strings
, tables
, bytes_per_strref
, err_cond
);
3481 if (ret
!= ERROR_SUCCESS
&& ret
!= ERROR_INVALID_TABLE
)
3484 ret
= msi_table_load_transform( db
, stg
, strings
, columns
, bytes_per_strref
, err_cond
);
3485 if (ret
!= ERROR_SUCCESS
&& ret
!= ERROR_INVALID_TABLE
)
3488 ret
= ERROR_SUCCESS
;
3490 while ( !list_empty( &transforms
) )
3492 transform
= LIST_ENTRY( list_head( &transforms
), TRANSFORMDATA
, entry
);
3494 if ( wcscmp( transform
->name
, L
"_Columns" ) &&
3495 wcscmp( transform
->name
, L
"_Tables" ) &&
3496 ret
== ERROR_SUCCESS
)
3498 ret
= msi_table_load_transform( db
, stg
, strings
, transform
, bytes_per_strref
, err_cond
);
3501 list_remove( &transform
->entry
);
3502 msi_free( transform
->name
);
3503 msi_free( transform
);
3506 if ( ret
== ERROR_SUCCESS
)
3508 append_storage_to_db( db
, stg
);
3509 if (property_update
) msi_clone_properties( db
);
3514 IEnumSTATSTG_Release( stgenum
);
3516 msi_destroy_stringtable( strings
);
3519 struct tagMSITABLE
*table
= ((MSITABLEVIEW
*)transform_view
)->table
;
3521 if (ret
!= ERROR_SUCCESS
)
3522 transform_view
->ops
->release( transform_view
);
3524 if (!wcscmp(table
->colinfo
[table
->col_count
- 1].colname
, L
"new"))
3525 TABLE_remove_column( transform_view
, table
->colinfo
[table
->col_count
- 1].number
);
3526 transform_view
->ops
->delete( transform_view
);