ReactOS 0.4.15-dev-8102-g108db8f
table.c
Go to the documentation of this file.
1/*
2 * Implementation of the Microsoft Installer (msi.dll)
3 *
4 * Copyright 2002-2005 Mike McCormack for CodeWeavers
5 *
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.
10 *
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.
15 *
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
19 */
20
21#include <stdarg.h>
22#include <assert.h>
23
24#define COBJMACROS
25
26#include "windef.h"
27#include "winbase.h"
28#include "winerror.h"
29#include "msi.h"
30#include "msiquery.h"
31#include "objbase.h"
32#include "objidl.h"
33#include "winnls.h"
34#include "msipriv.h"
35#include "query.h"
36
37#include "wine/debug.h"
38
40
41#define MSITABLE_HASH_TABLE_SIZE 37
42
44{
49
50typedef struct tagMSICOLUMNINFO
51{
59
61{
65 struct list entry;
71};
72
73/* information for default tables */
74static 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 },
79};
80
81static const MSICOLUMNINFO _Tables_cols[1] = {
82 { L"_Tables", 1, L"Name", MSITYPE_VALID | MSITYPE_STRING | MSITYPE_KEY | 64, 0, NULL },
83};
84
85#define MAX_STREAM_NAME 0x1f
86
87static inline UINT bytes_per_column( MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref )
88{
89 if( MSITYPE_IS_BINARY(col->type) )
90 return 2;
91
92 if( col->type & MSITYPE_STRING )
93 return bytes_per_strref;
94
95 if( (col->type & 0xff) <= 2)
96 return 2;
97
98 if( (col->type & 0xff) != 4 )
99 ERR("Invalid column size %u\n", col->type & 0xff);
100
101 return 4;
102}
103
104static int utf2mime(int x)
105{
106 if( (x>='0') && (x<='9') )
107 return x-'0';
108 if( (x>='A') && (x<='Z') )
109 return x-'A'+10;
110 if( (x>='a') && (x<='z') )
111 return x-'a'+10+26;
112 if( x=='.' )
113 return 10+26+26;
114 if( x=='_' )
115 return 10+26+26+1;
116 return -1;
117}
118
120{
122 DWORD ch, next;
123 LPWSTR out, p;
124
125 if( !bTable )
126 count = lstrlenW( in )+2;
127 if (!(out = msi_alloc( count*sizeof(WCHAR) ))) return NULL;
128 p = out;
129
130 if( bTable )
131 {
132 *p++ = 0x4840;
133 count --;
134 }
135 while( count -- )
136 {
137 ch = *in++;
138 if( !ch )
139 {
140 *p = ch;
141 return out;
142 }
143 if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
144 {
145 ch = utf2mime(ch) + 0x4800;
146 next = *in;
147 if( next && (next<0x80) )
148 {
149 next = utf2mime(next);
150 if( next != -1 )
151 {
152 next += 0x3ffffc0;
153 ch += (next<<6);
154 in++;
155 }
156 }
157 }
158 *p++ = ch;
159 }
160 ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
161 msi_free( out );
162 return NULL;
163}
164
165static int mime2utf(int x)
166{
167 if( x<10 )
168 return x + '0';
169 if( x<(10+26))
170 return x - 10 + 'A';
171 if( x<(10+26+26))
172 return x - 10 - 26 + 'a';
173 if( x == (10+26+26) )
174 return '.';
175 return '_';
176}
177
179{
180 WCHAR ch;
181 DWORD count = 0;
182
183 while ( (ch = *in++) )
184 {
185 if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
186 {
187 if( ch >= 0x4800 )
188 ch = mime2utf(ch-0x4800);
189 else
190 {
191 ch -= 0x3800;
192 *out++ = mime2utf(ch&0x3f);
193 count++;
194 ch = mime2utf((ch>>6)&0x3f);
195 }
196 }
197 *out++ = ch;
198 count++;
199 }
200 *out = 0;
201 return count;
202}
203
205{
206 IEnumSTATSTG *stgenum = NULL;
207 HRESULT r;
208 STATSTG stat;
209 ULONG n, count;
210 WCHAR name[0x40];
211
212 r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
213 if( FAILED( r ) )
214 return;
215
216 n = 0;
217 while( 1 )
218 {
219 count = 0;
220 r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
221 if( FAILED( r ) || !count )
222 break;
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 );
226 n++;
227 }
228
229 IEnumSTATSTG_Release( stgenum );
230}
231
233 BYTE **pdata, UINT *psz )
234{
235 HRESULT r;
237 VOID *data;
238 ULONG sz, count;
239 IStream *stm = NULL;
240 STATSTG stat;
241 LPWSTR encname;
242
243 encname = encode_streamname(table, stname);
244
245 TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
246
247 r = IStorage_OpenStream(stg, encname, NULL,
249 msi_free( encname );
250 if( FAILED( r ) )
251 {
252 WARN( "open stream failed r = %#lx - empty table?\n", r );
253 return ret;
254 }
255
256 r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
257 if( FAILED( r ) )
258 {
259 WARN( "open stream failed r = %#lx!\n", r );
260 goto end;
261 }
262
263 if( stat.cbSize.QuadPart >> 32 )
264 {
265 WARN("Too big!\n");
266 goto end;
267 }
268
269 sz = stat.cbSize.QuadPart;
270 data = msi_alloc( sz );
271 if( !data )
272 {
273 WARN( "couldn't allocate memory r = %#lx!\n", r );
275 goto end;
276 }
277
278 r = IStream_Read(stm, data, sz, &count );
279 if( FAILED( r ) || ( count != sz ) )
280 {
281 msi_free( data );
282 WARN("read stream failed r = %#lx!\n", r);
283 goto end;
284 }
285
286 *pdata = data;
287 *psz = sz;
289
290end:
291 IStream_Release( stm );
292
293 return ret;
294}
295
297 LPCVOID data, UINT sz, BOOL bTable )
298{
299 HRESULT r;
301 ULONG count;
302 IStream *stm = NULL;
305 LPWSTR encname;
306
307 encname = encode_streamname(bTable, stname );
308 r = IStorage_OpenStream( stg, encname, NULL,
310 if( FAILED(r) )
311 {
312 r = IStorage_CreateStream( stg, encname,
313 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
314 }
315 msi_free( encname );
316 if( FAILED( r ) )
317 {
318 WARN( "open stream failed r = %#lx\n", r );
319 return ret;
320 }
321
322 size.QuadPart = sz;
323 r = IStream_SetSize( stm, size );
324 if( FAILED( r ) )
325 {
326 WARN("Failed to SetSize\n");
327 goto end;
328 }
329
330 pos.QuadPart = 0;
331 r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
332 if( FAILED( r ) )
333 {
334 WARN("Failed to Seek\n");
335 goto end;
336 }
337
338 if (sz)
339 {
340 r = IStream_Write(stm, data, sz, &count );
341 if( FAILED( r ) || ( count != sz ) )
342 {
343 WARN("Failed to Write\n");
344 goto end;
345 }
346 }
347
349
350end:
351 IStream_Release( stm );
352
353 return ret;
354}
355
356static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count )
357{
358 UINT i;
359 for (i = 0; i < count; i++) msi_free( colinfo[i].hash_table );
360}
361
362static void free_table( MSITABLE *table )
363{
364 UINT i;
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 );
371 msi_free( table );
372}
373
374static UINT msi_table_get_row_size( MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref )
375{
376 const MSICOLUMNINFO *last_col;
377
378 if (!count)
379 return 0;
380
381 if (bytes_per_strref != LONG_STR_BYTES)
382 {
383 UINT i, size = 0;
384 for (i = 0; i < count; i++) size += bytes_per_column( db, &cols[i], bytes_per_strref );
385 return size;
386 }
387 last_col = &cols[count - 1];
388 return last_col->offset + bytes_per_column( db, last_col, bytes_per_strref );
389}
390
391/* add this table to the list of cached tables in the database */
393{
394 BYTE *rawdata = NULL;
395 UINT rawsize = 0, i, j, row_size, row_size_mem;
396
397 TRACE("%s\n",debugstr_w(t->name));
398
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 );
401
402 /* if we can't read the table, just assume that it's empty */
403 read_stream_data( stg, t->name, TRUE, &rawdata, &rawsize );
404 if( !rawdata )
405 return ERROR_SUCCESS;
406
407 TRACE("Read %d bytes\n", rawsize );
408
409 if( rawsize % row_size )
410 {
411 WARN("Table size is invalid %d/%d\n", rawsize, row_size );
412 goto err;
413 }
414
415 if ((t->row_count = rawsize / row_size))
416 {
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;
419 }
420
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++)
424 {
425 UINT ofs = 0, ofs_mem = 0;
426
427 t->data[i] = msi_alloc( row_size_mem );
428 if( !t->data[i] )
429 goto err;
430 t->data_persistent[i] = TRUE;
431
432 for (j = 0; j < t->col_count; j++)
433 {
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 );
436 UINT k;
437
438 if ( n != 2 && n != 3 && n != 4 )
439 {
440 ERR("oops - unknown column width %d\n", n);
441 goto err;
442 }
443 if (t->colinfo[j].type & MSITYPE_STRING && n < m)
444 {
445 for (k = 0; k < m; k++)
446 {
447 if (k < n)
448 t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
449 else
450 t->data[i][ofs_mem + k] = 0;
451 }
452 }
453 else
454 {
455 for (k = 0; k < n; k++)
456 t->data[i][ofs_mem + k] = rawdata[ofs * t->row_count + i * n + k];
457 }
458 ofs_mem += m;
459 ofs += n;
460 }
461 }
462
463 msi_free( rawdata );
464 return ERROR_SUCCESS;
465err:
466 msi_free( rawdata );
468}
469
471{
472 while( !list_empty( &db->tables ) )
473 {
475
476 list_remove( &t->entry );
477 free_table( t );
478 }
479}
480
482{
483 MSITABLE *t;
484
486 if( !wcscmp( name, t->name ) )
487 return t;
488
489 return NULL;
490}
491
493{
494 DWORD i;
495
496 for (i = 0; colinfo && i < count; i++)
497 {
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;
502
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);
506 }
507}
508
510{
511 const MSICOLUMNINFO *p;
512 DWORD i, n;
513
514 TRACE("%s\n", debugstr_w(name));
515
516 if (!wcscmp( name, L"_Tables" ))
517 {
518 p = _Tables_cols;
519 n = 1;
520 }
521 else if (!wcscmp( name, L"_Columns" ))
522 {
524 n = 4;
525 }
526 else return ERROR_FUNCTION_FAILED;
527
528 for (i = 0; i < n; i++)
529 {
530 if (colinfo && i < *sz) colinfo[i] = p[i];
531 if (colinfo && i >= *sz) break;
532 }
533 table_calc_column_offsets( db, colinfo, n );
534 *sz = n;
535 return ERROR_SUCCESS;
536}
537
538static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz );
539
541{
542 UINT r, column_count = 0;
543 MSICOLUMNINFO *columns;
544
545 /* get the number of columns in this table */
546 column_count = 0;
547 r = get_tablecolumns( db, name, NULL, &column_count );
548 if (r != ERROR_SUCCESS)
549 return r;
550
551 *pcount = column_count;
552
553 /* if there are no columns, there's no table */
554 if (!column_count)
556
557 TRACE("table %s found\n", debugstr_w(name));
558
559 columns = msi_alloc( column_count * sizeof(MSICOLUMNINFO) );
560 if (!columns)
562
563 r = get_tablecolumns( db, name, columns, &column_count );
564 if (r != ERROR_SUCCESS)
565 {
566 msi_free( columns );
568 }
569 *pcols = columns;
570 return r;
571}
572
573static UINT get_table( MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret )
574{
576 UINT r;
577
578 /* first, see if the table is cached */
580 if (table)
581 {
582 *table_ret = table;
583 return ERROR_SUCCESS;
584 }
585
586 /* nonexistent tables should be interpreted as empty tables */
587 table = msi_alloc( sizeof(MSITABLE) + lstrlenW( name ) * sizeof(WCHAR) );
588 if (!table)
590
591 table->row_count = 0;
592 table->data = NULL;
593 table->data_persistent = NULL;
594 table->colinfo = NULL;
595 table->col_count = 0;
596 table->persistent = MSICONDITION_TRUE;
597 lstrcpyW( table->name, name );
598
599 if (!wcscmp( name, L"_Tables" ) || !wcscmp( name, L"_Columns" ))
600 table->persistent = MSICONDITION_NONE;
601
602 r = table_get_column_info( db, name, &table->colinfo, &table->col_count );
603 if (r != ERROR_SUCCESS)
604 {
605 free_table( table );
606 return r;
607 }
609 if (r != ERROR_SUCCESS)
610 {
611 free_table( table );
612 return r;
613 }
614 list_add_head( &db->tables, &table->entry );
615 *table_ret = table;
616 return ERROR_SUCCESS;
617}
618
620{
621 UINT ret = 0, i;
622
623 for (i = 0; i < bytes; i++)
624 ret += data[row][col + i] << i * 8;
625
626 return ret;
627}
628
629static UINT get_tablecolumns( MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz )
630{
631 UINT r, i, n = 0, table_id, count, maxcount = *sz;
633
634 TRACE("%s\n", debugstr_w(szTableName));
635
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)
639 return r;
640
641 r = get_table( db, L"_Columns", &table );
642 if (r != ERROR_SUCCESS)
643 {
644 ERR("couldn't load _Columns table\n");
646 }
647
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)
651 {
652 WARN("Couldn't find id for %s\n", debugstr_w(szTableName));
653 return r;
654 }
655 TRACE("Table id is %d, row count is %d\n", table_id, table->row_count);
656
657 /* Note: _Columns table doesn't have non-persistent data */
658
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++)
663 {
664 if (read_table_int( table->data, i, 0, LONG_STR_BYTES) != table_id) continue;
665 if (colinfo)
666 {
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);
669
670 /* check the column number is in range */
671 if (col < 1 || col > maxcount)
672 {
673 ERR("column %d out of range (maxcount: %d)\n", col, maxcount);
674 continue;
675 }
676 /* check if this column was already set */
677 if (colinfo[col - 1].number)
678 {
679 ERR("duplicate column %d\n", col);
680 continue;
681 }
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;
689 }
690 n++;
691 }
692 TRACE("%s has %d columns\n", debugstr_w(szTableName), n);
693
694 if (colinfo && n != maxcount)
695 {
696 ERR("missing column in table %s\n", debugstr_w(szTableName));
697 msi_free_colinfo( colinfo, maxcount );
699 }
700 table_calc_column_offsets( db, colinfo, n );
701 *sz = n;
702 return ERROR_SUCCESS;
703}
704
706 MSICONDITION persistent, BOOL hold )
707{
708 UINT r, nField;
709 MSIVIEW *tv = NULL;
710 MSIRECORD *rec = NULL;
711 column_info *col;
713 UINT i;
714
715 /* only add tables that don't exist already */
716 if( TABLE_Exists(db, name ) )
717 {
718 WARN("table %s exists\n", debugstr_w(name));
720 }
721
722 table = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
723 if( !table )
725
726 table->ref_count = 0;
727 table->row_count = 0;
728 table->data = NULL;
729 table->data_persistent = NULL;
730 table->colinfo = NULL;
731 table->col_count = 0;
732 table->persistent = persistent;
733 lstrcpyW( table->name, name );
734
735 if( hold )
736 table->ref_count++;
737
738 for( col = col_info; col; col = col->next )
739 table->col_count++;
740
741 table->colinfo = msi_alloc( table->col_count * sizeof(MSICOLUMNINFO) );
742 if (!table->colinfo)
743 {
744 free_table( table );
746 }
747
748 for( i = 0, col = col_info; col; i++, col = col->next )
749 {
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 );
752
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;
759 }
760 table_calc_column_offsets( db, table->colinfo, table->col_count);
761
762 r = TABLE_CreateView( db, L"_Tables", &tv );
763 TRACE("CreateView returned %x\n", r);
764 if( r )
765 {
766 free_table( table );
767 return r;
768 }
769
770 r = tv->ops->execute( tv, 0 );
771 TRACE("tv execute returned %x\n", r);
772 if( r )
773 goto err;
774
775 rec = MSI_CreateRecord( 1 );
776 if( !rec )
777 goto err;
778
779 r = MSI_RecordSetStringW( rec, 1, name );
780 if( r )
781 goto err;
782
783 r = tv->ops->insert_row( tv, rec, -1, persistent == MSICONDITION_FALSE );
784 TRACE("insert_row returned %x\n", r);
785 if( r )
786 goto err;
787
788 tv->ops->delete( tv );
789 tv = NULL;
790
791 msiobj_release( &rec->hdr );
792 rec = NULL;
793
794 if( persistent != MSICONDITION_FALSE )
795 {
796 /* add each column to the _Columns table */
797 r = TABLE_CreateView( db, L"_Columns", &tv );
798 if( r )
799 goto err;
800
801 r = tv->ops->execute( tv, 0 );
802 TRACE("tv execute returned %x\n", r);
803 if( r )
804 goto err;
805
806 rec = MSI_CreateRecord( 4 );
807 if( !rec )
808 goto err;
809
810 r = MSI_RecordSetStringW( rec, 1, name );
811 if( r )
812 goto err;
813
814 /*
815 * need to set the table, column number, col name and type
816 * for each column we enter in the table
817 */
818 nField = 1;
819 for( col = col_info; col; col = col->next )
820 {
821 r = MSI_RecordSetInteger( rec, 2, nField );
822 if( r )
823 goto err;
824
825 r = MSI_RecordSetStringW( rec, 3, col->column );
826 if( r )
827 goto err;
828
829 r = MSI_RecordSetInteger( rec, 4, col->type );
830 if( r )
831 goto err;
832
833 r = tv->ops->insert_row( tv, rec, -1, FALSE );
834 if( r )
835 goto err;
836
837 nField++;
838 }
839 if( !col )
841 }
842
843err:
844 if (rec)
845 msiobj_release( &rec->hdr );
846 /* FIXME: remove values from the string table on error */
847 if( tv )
848 tv->ops->delete( tv );
849
850 if (r == ERROR_SUCCESS)
851 list_add_head( &db->tables, &table->entry );
852 else
853 free_table( table );
854
855 return r;
856}
857
858static UINT save_table( MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref )
859{
860 BYTE *rawdata = NULL;
861 UINT rawsize, i, j, row_size, row_count;
863
864 /* Nothing to do for non-persistent tables */
865 if( t->persistent == MSICONDITION_FALSE )
866 return ERROR_SUCCESS;
867
868 TRACE("Saving %s\n", debugstr_w( t->name ) );
869
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++)
873 {
874 if (!t->data_persistent[i])
875 {
876 row_count = 1; /* yes, this is bizarre */
877 break;
878 }
879 }
880 rawsize = row_count * row_size;
881 rawdata = msi_alloc_zero( rawsize );
882 if( !rawdata )
883 {
885 goto err;
886 }
887
888 rawsize = 0;
889 for (i = 0; i < row_count; i++)
890 {
891 UINT ofs = 0, ofs_mem = 0;
892
893 if (!t->data_persistent[i]) break;
894
895 for (j = 0; j < t->col_count; j++)
896 {
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 );
899 UINT k;
900
901 if (n != 2 && n != 3 && n != 4)
902 {
903 ERR("oops - unknown column width %d\n", n);
904 goto err;
905 }
906 if (t->colinfo[j].type & MSITYPE_STRING && n < m)
907 {
908 UINT id = read_table_int( t->data, i, ofs_mem, LONG_STR_BYTES );
909 if (id > 1 << bytes_per_strref * 8)
910 {
911 ERR("string id %u out of range\n", id);
912 goto err;
913 }
914 }
915 for (k = 0; k < n; k++)
916 {
917 rawdata[ofs * row_count + i * n + k] = t->data[i][ofs_mem + k];
918 }
919 ofs_mem += m;
920 ofs += n;
921 }
922 rawsize += row_size;
923 }
924
925 TRACE("writing %d bytes\n", rawsize);
926 r = write_stream_data( db->storage, t->name, rawdata, rawsize, TRUE );
927
928err:
929 msi_free( rawdata );
930 return r;
931}
932
934{
936 UINT size, offset, old_count;
937 UINT n;
938
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;
944
945 table_get_column_info( db, name, &table->colinfo, &table->col_count );
946 if (!table->col_count) return;
947
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;
950
951 for ( n = 0; n < table->row_count; n++ )
952 {
954 if (old_count < table->col_count)
955 memset( &table->data[n][offset], 0, size - offset );
956 }
957}
958
959/* try to find the table name in the _Tables table */
961{
962 UINT r, table_id, i;
964
965 if( !wcscmp( name, L"_Tables" ) || !wcscmp( name, L"_Columns" ) ||
966 !wcscmp( name, L"_Streams" ) || !wcscmp( name, L"_Storages" ) )
967 return TRUE;
968
969 r = msi_string2id( db->strings, name, -1, &table_id );
970 if( r != ERROR_SUCCESS )
971 {
972 TRACE("Couldn't find id for %s\n", debugstr_w(name));
973 return FALSE;
974 }
975
976 r = get_table( db, L"_Tables", &table );
977 if( r != ERROR_SUCCESS )
978 {
979 ERR("table _Tables not available\n");
980 return FALSE;
981 }
982
983 for( i = 0; i < table->row_count; i++ )
984 {
985 if( read_table_int( table->data, i, 0, LONG_STR_BYTES ) == table_id )
986 return TRUE;
987 }
988
989 return FALSE;
990}
991
992/* below is the query interface to a table */
993
994typedef struct tagMSITABLEVIEW
995{
1004
1006{
1008 UINT offset, n;
1009
1010 if( !tv->table )
1012
1013 if( (col==0) || (col>tv->num_cols) )
1015
1016 /* how many rows are there ? */
1017 if( row >= tv->table->row_count )
1018 return ERROR_NO_MORE_ITEMS;
1019
1020 if( tv->columns[col-1].offset >= tv->row_size )
1021 {
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;
1025 }
1026
1027 n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1028 if (n != 2 && n != 3 && n != 4)
1029 {
1030 ERR("oops! what is %d bytes per column?\n", n );
1031 return ERROR_FUNCTION_FAILED;
1032 }
1033
1034 offset = tv->columns[col-1].offset;
1035 *val = read_table_int(tv->table->data, row, offset, n);
1036
1037 /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */
1038
1039 return ERROR_SUCCESS;
1040}
1041
1042static UINT get_stream_name( const MSITABLEVIEW *tv, UINT row, WCHAR **pstname )
1043{
1044 LPWSTR p, stname = NULL;
1045 UINT i, r, type, ival;
1046 DWORD len;
1047 LPCWSTR sval;
1048 MSIVIEW *view = (MSIVIEW *) tv;
1049
1050 TRACE("%p %d\n", tv, row);
1051
1052 len = lstrlenW( tv->name ) + 1;
1053 stname = msi_alloc( len*sizeof(WCHAR) );
1054 if ( !stname )
1055 {
1057 goto err;
1058 }
1059
1060 lstrcpyW( stname, tv->name );
1061
1062 for ( i = 0; i < tv->num_cols; i++ )
1063 {
1064 type = tv->columns[i].type;
1065 if ( type & MSITYPE_KEY )
1066 {
1067 WCHAR number[0x20];
1068
1069 r = TABLE_fetch_int( view, row, i+1, &ival );
1070 if ( r != ERROR_SUCCESS )
1071 goto err;
1072
1073 if ( tv->columns[i].type & MSITYPE_STRING )
1074 {
1075 sval = msi_string_lookup( tv->db->strings, ival, NULL );
1076 if ( !sval )
1077 {
1079 goto err;
1080 }
1081 }
1082 else
1083 {
1084 UINT n = bytes_per_column( tv->db, &tv->columns[i], LONG_STR_BYTES );
1085
1086 switch( n )
1087 {
1088 case 2:
1089 swprintf( number, ARRAY_SIZE(number), L"%d", ival-0x8000 );
1090 break;
1091 case 4:
1092 swprintf( number, ARRAY_SIZE(number), L"%d", ival^0x80000000 );
1093 break;
1094 default:
1095 ERR( "oops - unknown column width %d\n", n );
1097 goto err;
1098 }
1099 sval = number;
1100 }
1101
1102 len += lstrlenW( L"." ) + lstrlenW( sval );
1103 p = msi_realloc ( stname, len*sizeof(WCHAR) );
1104 if ( !p )
1105 {
1107 goto err;
1108 }
1109 stname = p;
1110
1111 lstrcatW( stname, L"." );
1112 lstrcatW( stname, sval );
1113 }
1114 else
1115 continue;
1116 }
1117
1118 *pstname = stname;
1119 return ERROR_SUCCESS;
1120
1121err:
1122 msi_free( stname );
1123 *pstname = NULL;
1124 return r;
1125}
1126
1127/*
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
1131 */
1133{
1135 UINT r;
1136 WCHAR *name;
1137
1138 if( !view->ops->fetch_int )
1140
1141 r = get_stream_name( tv, row, &name );
1142 if (r != ERROR_SUCCESS)
1143 {
1144 ERR("fetching stream, error = %u\n", r);
1145 return r;
1146 }
1147
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);
1151
1152 msi_free( name );
1153 return r;
1154}
1155
1156/* Set a table value, i.e. preadjusted integer or string ID. */
1158{
1159 UINT offset, n, i;
1160
1161 if( !tv->table )
1163
1164 if( (col==0) || (col>tv->num_cols) )
1166
1167 if( row >= tv->table->row_count )
1169
1170 if( tv->columns[col-1].offset >= tv->row_size )
1171 {
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;
1175 }
1176
1177 msi_free( tv->columns[col-1].hash_table );
1178 tv->columns[col-1].hash_table = NULL;
1179
1180 n = bytes_per_column( tv->db, &tv->columns[col - 1], LONG_STR_BYTES );
1181 if ( n != 2 && n != 3 && n != 4 )
1182 {
1183 ERR("oops! what is %d bytes per column?\n", n );
1184 return ERROR_FUNCTION_FAILED;
1185 }
1186
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;
1190
1191 return ERROR_SUCCESS;
1192}
1193
1194static UINT int_to_table_storage( const MSITABLEVIEW *tv, UINT col, int val, UINT *ret )
1195{
1196 if ((tv->columns[col-1].type & MSI_DATASIZEMASK) == 2)
1197 {
1198 if (val == MSI_NULL_INTEGER)
1199 *ret = 0;
1200 else if ((val + 0x8000) & 0xffff0000)
1201 {
1202 ERR("value %d out of range\n", val);
1203 return ERROR_FUNCTION_FAILED;
1204 }
1205 else
1206 *ret = val + 0x8000;
1207 }
1208 else
1209 *ret = val ^ 0x80000000;
1210
1211 return ERROR_SUCCESS;
1212}
1213
1215{
1217 UINT r, table_int;
1218
1219 TRACE("row %u, col %u, val %d.\n", row, col, val);
1220
1221 if ((r = int_to_table_storage( tv, col, val, &table_int )))
1222 return r;
1223
1224 if (tv->columns[col-1].type & MSITYPE_KEY)
1225 {
1226 UINT key;
1227
1228 if ((r = TABLE_fetch_int( view, row, col, &key )))
1229 return r;
1230 if (key != table_int)
1231 {
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;
1235 }
1236 }
1237
1238 return table_set_bytes( tv, row, col, table_int );
1239}
1240
1241static UINT TABLE_set_string( MSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len )
1242{
1244 BOOL persistent;
1245 UINT id, r;
1246
1247 TRACE("row %u, col %u, val %s.\n", row, col, debugstr_wn(val, len));
1248
1249 persistent = (tv->table->persistent != MSICONDITION_FALSE)
1250 && tv->table->data_persistent[row];
1251
1252 if (val)
1253 {
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 );
1257 }
1258 else
1259 id = 0;
1260
1261 if (tv->columns[col-1].type & MSITYPE_KEY)
1262 {
1263 UINT key;
1264
1265 if ((r = TABLE_fetch_int( view, row, col, &key )))
1266 return r;
1267 if (key != id)
1268 {
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;
1272 }
1273 }
1274
1275 return table_set_bytes( tv, row, col, id );
1276}
1277
1279{
1281
1282 if (!tv->table)
1284
1285 return msi_view_get_row(tv->db, view, row, rec);
1286}
1287
1289{
1290 MSIQUERY *query;
1291 MSIRECORD *rec;
1292 UINT r;
1293
1294 TRACE("%p %s %p\n", db, debugstr_w(name), data);
1295
1296 if (!(rec = MSI_CreateRecord( 2 )))
1297 return ERROR_OUTOFMEMORY;
1298
1299 r = MSI_RecordSetStringW( rec, 1, name );
1300 if (r != ERROR_SUCCESS)
1301 goto done;
1302
1303 r = MSI_RecordSetIStream( rec, 2, data );
1304 if (r != ERROR_SUCCESS)
1305 goto done;
1306
1307 r = MSI_DatabaseOpenViewW( db, L"INSERT INTO `_Streams` (`Name`,`Data`) VALUES (?,?)", &query );
1308 if (r != ERROR_SUCCESS)
1309 goto done;
1310
1311 r = MSI_ViewExecute( query, rec );
1312 msiobj_release( &query->hdr );
1313 if (r == ERROR_SUCCESS)
1314 goto done;
1315
1316 msiobj_release( &rec->hdr );
1317 if (!(rec = MSI_CreateRecord( 2 )))
1318 return ERROR_OUTOFMEMORY;
1319
1320 r = MSI_RecordSetIStream( rec, 1, data );
1321 if (r != ERROR_SUCCESS)
1322 goto done;
1323
1324 r = MSI_RecordSetStringW( rec, 2, name );
1325 if (r != ERROR_SUCCESS)
1326 goto done;
1327
1328 r = MSI_DatabaseOpenViewW( db, L"UPDATE `_Streams` SET `Data` = ? WHERE `Name` = ?", &query );
1329 if (r != ERROR_SUCCESS)
1330 goto done;
1331
1332 r = MSI_ViewExecute( query, rec );
1333 msiobj_release( &query->hdr );
1334
1335done:
1336 msiobj_release( &rec->hdr );
1337 return r;
1338}
1339
1341{
1343 WCHAR *name;
1344 UINT r;
1345
1346 TRACE("row %u, col %u, stream %p.\n", row, col, stream);
1347
1348 if ((r = get_stream_name( tv, row - 1, &name )))
1349 return r;
1350
1351 r = add_stream( tv->db, name, stream );
1352 msi_free( name );
1353 return r;
1354}
1355
1357{
1359 UINT r;
1360
1361 if (!iField || iField > tv->num_cols || MSI_RecordIsNull( rec, iField ))
1362 return ERROR_FUNCTION_FAILED;
1363
1364 columninfo = tv->columns[ iField - 1 ];
1365
1366 if ( MSITYPE_IS_BINARY(columninfo.type) )
1367 {
1368 *pvalue = 1; /* refers to the first key column */
1369 }
1370 else if ( columninfo.type & MSITYPE_STRING )
1371 {
1372 int len;
1373 const WCHAR *sval = msi_record_get_string( rec, iField, &len );
1374 if (sval)
1375 {
1376 r = msi_string2id( tv->db->strings, sval, len, pvalue );
1377 if (r != ERROR_SUCCESS)
1378 return ERROR_NOT_FOUND;
1379 }
1380 else *pvalue = 0;
1381 }
1382 else
1383 return int_to_table_storage( tv, iField, MSI_RecordGetInteger( rec, iField ), pvalue );
1384
1385 return ERROR_SUCCESS;
1386}
1387
1389{
1391 UINT i, val, r = ERROR_SUCCESS;
1392
1393 if ( !tv->table )
1395
1396 /* test if any of the mask bits are invalid */
1397 if ( mask >= (1<<tv->num_cols) )
1399
1400 for ( i = 0; i < tv->num_cols; i++ )
1401 {
1402 BOOL persistent;
1403
1404 /* only update the fields specified in the mask */
1405 if ( !(mask&(1<<i)) )
1406 continue;
1407
1408 persistent = (tv->table->persistent != MSICONDITION_FALSE) &&
1409 (tv->table->data_persistent[row]);
1410 /* FIXME: should we allow updating keys? */
1411
1412 val = 0;
1413 if ( !MSI_RecordIsNull( rec, i + 1 ) )
1414 {
1415 r = get_table_value_from_record (tv, rec, i + 1, &val);
1416 if ( MSITYPE_IS_BINARY(tv->columns[ i ].type) )
1417 {
1418 IStream *stm;
1419 LPWSTR stname;
1420
1421 if ( r != ERROR_SUCCESS )
1422 return ERROR_FUNCTION_FAILED;
1423
1424 r = MSI_RecordGetIStream( rec, i + 1, &stm );
1425 if ( r != ERROR_SUCCESS )
1426 return r;
1427
1428 r = get_stream_name( tv, row, &stname );
1429 if ( r != ERROR_SUCCESS )
1430 {
1431 IStream_Release( stm );
1432 return r;
1433 }
1434
1435 r = add_stream( tv->db, stname, stm );
1436 IStream_Release( stm );
1437 msi_free ( stname );
1438
1439 if ( r != ERROR_SUCCESS )
1440 return r;
1441 }
1442 else if ( tv->columns[i].type & MSITYPE_STRING )
1443 {
1444 UINT x;
1445
1446 if ( r != ERROR_SUCCESS )
1447 {
1448 int len;
1449 const WCHAR *sval = msi_record_get_string( rec, i + 1, &len );
1450 val = msi_add_string( tv->db->strings, sval, len, persistent );
1451 }
1452 else
1453 {
1454 TABLE_fetch_int(&tv->view, row, i + 1, &x);
1455 if (val == x)
1456 continue;
1457 }
1458 }
1459 else
1460 {
1461 if ( r != ERROR_SUCCESS )
1462 return ERROR_FUNCTION_FAILED;
1463 }
1464 }
1465
1466 r = table_set_bytes( tv, row, i+1, val );
1467 if ( r != ERROR_SUCCESS )
1468 break;
1469 }
1470 return r;
1471}
1472
1473static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num, BOOL temporary )
1474{
1476 BYTE **p, *row;
1477 BOOL *b;
1478 UINT sz;
1479 BYTE ***data_ptr;
1480 BOOL **data_persist_ptr;
1481 UINT *row_count;
1482
1483 TRACE("%p %s\n", view, temporary ? "TRUE" : "FALSE");
1484
1485 if( !tv->table )
1487
1488 row = msi_alloc_zero( tv->row_size );
1489 if( !row )
1491
1492 row_count = &tv->table->row_count;
1493 data_ptr = &tv->table->data;
1494 data_persist_ptr = &tv->table->data_persistent;
1495 if (*num == -1)
1496 *num = tv->table->row_count;
1497
1498 sz = (*row_count + 1) * sizeof (BYTE*);
1499 if( *data_ptr )
1500 p = msi_realloc( *data_ptr, sz );
1501 else
1502 p = msi_alloc( sz );
1503 if( !p )
1504 {
1505 msi_free( row );
1507 }
1508
1509 sz = (*row_count + 1) * sizeof (BOOL);
1510 if( *data_persist_ptr )
1511 b = msi_realloc( *data_persist_ptr, sz );
1512 else
1513 b = msi_alloc( sz );
1514 if( !b )
1515 {
1516 msi_free( row );
1517 msi_free( p );
1519 }
1520
1521 *data_ptr = p;
1522 (*data_ptr)[*row_count] = row;
1523
1524 *data_persist_ptr = b;
1525 (*data_persist_ptr)[*row_count] = !temporary;
1526
1527 (*row_count)++;
1528
1529 return ERROR_SUCCESS;
1530}
1531
1533{
1535
1536 TRACE("%p %p\n", tv, record);
1537
1538 TRACE("There are %d columns\n", tv->num_cols );
1539
1540 return ERROR_SUCCESS;
1541}
1542
1544{
1545 TRACE("%p\n", view );
1546
1547 return ERROR_SUCCESS;
1548}
1549
1550static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1551{
1553
1554 TRACE("%p %p %p\n", view, rows, cols );
1555
1556 if( cols )
1557 *cols = tv->num_cols;
1558 if( rows )
1559 {
1560 if( !tv->table )
1562 *rows = tv->table->row_count;
1563 }
1564
1565 return ERROR_SUCCESS;
1566}
1567
1569 UINT n, LPCWSTR *name, UINT *type, BOOL *temporary,
1570 LPCWSTR *table_name )
1571{
1573
1574 TRACE("%p %d %p %p\n", tv, n, name, type );
1575
1576 if( ( n == 0 ) || ( n > tv->num_cols ) )
1578
1579 if( name )
1580 {
1581 *name = tv->columns[n-1].colname;
1582 if( !*name )
1583 return ERROR_FUNCTION_FAILED;
1584 }
1585
1586 if( table_name )
1587 {
1588 *table_name = tv->columns[n-1].tablename;
1589 if( !*table_name )
1590 return ERROR_FUNCTION_FAILED;
1591 }
1592
1593 if( type )
1594 *type = tv->columns[n-1].type;
1595
1596 if( temporary )
1597 *temporary = (tv->columns[n-1].type & MSITYPE_TEMPORARY) != 0;
1598
1599 return ERROR_SUCCESS;
1600}
1601
1603
1605{
1606 UINT r, row, i;
1607
1608 /* check there are no null values where they're not allowed */
1609 for( i = 0; i < tv->num_cols; i++ )
1610 {
1611 if ( tv->columns[i].type & MSITYPE_NULLABLE )
1612 continue;
1613
1614 if ( MSITYPE_IS_BINARY(tv->columns[i].type) )
1615 TRACE("skipping binary column\n");
1616 else if ( tv->columns[i].type & MSITYPE_STRING )
1617 {
1618 int len;
1619 const WCHAR *str = msi_record_get_string( rec, i+1, &len );
1620
1621 if (!str || (!str[0] && !len))
1622 {
1623 if (column) *column = i;
1624 return ERROR_INVALID_DATA;
1625 }
1626 }
1627 else
1628 {
1629 UINT n;
1630
1631 n = MSI_RecordGetInteger( rec, i+1 );
1632 if (n == MSI_NULL_INTEGER)
1633 {
1634 if (column) *column = i;
1635 return ERROR_INVALID_DATA;
1636 }
1637 }
1638 }
1639
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;
1644
1645 return ERROR_SUCCESS;
1646}
1647
1649{
1650 UINT r, i, ivalue, x;
1651
1652 for (i = 0; i < tv->num_cols; i++ )
1653 {
1654 if (!(tv->columns[i].type & MSITYPE_KEY)) continue;
1655
1656 r = get_table_value_from_record( tv, rec, i + 1, &ivalue );
1657 if (r != ERROR_SUCCESS)
1658 return 1;
1659
1660 r = TABLE_fetch_int( &tv->view, row, i + 1, &x );
1661 if (r != ERROR_SUCCESS)
1662 {
1663 WARN("TABLE_fetch_int should not fail here %u\n", r);
1664 return -1;
1665 }
1666 if (ivalue > x)
1667 {
1668 return 1;
1669 }
1670 else if (ivalue == x)
1671 {
1672 if (i < tv->num_cols - 1) continue;
1673 return 0;
1674 }
1675 else
1676 return -1;
1677 }
1678 return 1;
1679}
1680
1682{
1683 int idx, c, low = 0, high = tv->table->row_count - 1;
1684
1685 TRACE("%p %p\n", tv, rec);
1686
1687 while (low <= high)
1688 {
1689 idx = (low + high) / 2;
1690 c = compare_record( tv, idx, rec );
1691
1692 if (c < 0)
1693 high = idx - 1;
1694 else if (c > 0)
1695 low = idx + 1;
1696 else
1697 {
1698 TRACE("found %u\n", idx);
1699 return idx;
1700 }
1701 }
1702 TRACE("found %u\n", high + 1);
1703 return high + 1;
1704}
1705
1706static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary )
1707{
1709 UINT i, r;
1710
1711 TRACE("%p %p %s\n", tv, rec, temporary ? "TRUE" : "FALSE" );
1712
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;
1717
1718 if (row == -1)
1719 row = find_insert_index( tv, rec );
1720
1721 r = table_create_new_row( view, &row, temporary );
1722 TRACE("insert_row returned %08x\n", r);
1723 if( r != ERROR_SUCCESS )
1724 return r;
1725
1726 /* shift the rows to make room for the new row */
1727 for (i = tv->table->row_count - 1; i > row; i--)
1728 {
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];
1732 }
1733
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 );
1737}
1738
1740{
1743
1744 TRACE("%p %d\n", tv, row);
1745
1746 if ( !tv->table )
1748
1750 if ( r != ERROR_SUCCESS )
1751 return r;
1752
1753 if ( row >= num_rows )
1754 return ERROR_FUNCTION_FAILED;
1755
1756 num_rows = tv->table->row_count;
1757 tv->table->row_count--;
1758
1759 /* reset the hash tables */
1760 for (i = 0; i < tv->num_cols; i++)
1761 {
1762 msi_free( tv->columns[i].hash_table );
1763 tv->columns[i].hash_table = NULL;
1764 }
1765
1766 for (i = row + 1; i < num_rows; i++)
1767 {
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];
1770 }
1771
1772 msi_free(tv->table->data[num_rows - 1]);
1773
1774 return ERROR_SUCCESS;
1775}
1776
1778{
1780 UINT r, new_row;
1781
1782 /* FIXME: MsiViewFetch should set rec index 0 to some ID that
1783 * sets the fetched record apart from other records
1784 */
1785
1786 if (!tv->table)
1788
1789 r = msi_table_find_row(tv, rec, &new_row, NULL);
1790 if (r != ERROR_SUCCESS)
1791 {
1792 ERR("can't find row to modify\n");
1793 return ERROR_FUNCTION_FAILED;
1794 }
1795
1796 /* the row cannot be changed */
1797 if (row != new_row)
1798 return ERROR_FUNCTION_FAILED;
1799
1800 return TABLE_set_row(view, new_row, rec, (1 << tv->num_cols) - 1);
1801}
1802
1804{
1806 UINT r, row;
1807
1808 if (!tv->table)
1810
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);
1814 else
1815 return TABLE_insert_row( view, rec, -1, FALSE );
1816}
1817
1819{
1820 MSIRECORD *curr;
1821 UINT r, i, count;
1822
1823 r = TABLE_get_row(view, row, &curr);
1824 if (r != ERROR_SUCCESS)
1825 return r;
1826
1827 /* Close the original record */
1828 MSI_CloseRecord(&rec->hdr);
1829
1831 for (i = 0; i < count; i++)
1832 MSI_RecordCopyField(curr, i + 1, rec, i + 1);
1833
1834 msiobj_release(&curr->hdr);
1835 return ERROR_SUCCESS;
1836}
1837
1838static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode,
1839 MSIRECORD *rec, UINT row)
1840{
1842 UINT r, frow, column;
1843
1844 TRACE("%p %d %p\n", view, eModifyMode, rec );
1845
1846 switch (eModifyMode)
1847 {
1848 case MSIMODIFY_DELETE:
1849 r = TABLE_delete_row( view, row );
1850 break;
1852 r = table_validate_new( tv, rec, &column );
1853 if (r != ERROR_SUCCESS)
1854 {
1858 }
1859 break;
1860
1861 case MSIMODIFY_INSERT:
1862 r = table_validate_new( tv, rec, NULL );
1863 if (r != ERROR_SUCCESS)
1864 break;
1865 r = TABLE_insert_row( view, rec, -1, FALSE );
1866 break;
1867
1869 r = table_validate_new( tv, rec, NULL );
1870 if (r != ERROR_SUCCESS)
1871 break;
1872 r = TABLE_insert_row( view, rec, -1, TRUE );
1873 break;
1874
1875 case MSIMODIFY_REFRESH:
1876 r = msi_refresh_record( view, rec, row );
1877 break;
1878
1879 case MSIMODIFY_UPDATE:
1880 r = msi_table_update( view, rec, row );
1881 break;
1882
1883 case MSIMODIFY_ASSIGN:
1884 r = msi_table_assign( view, rec );
1885 break;
1886
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)
1891 {
1892 r = table_validate_new( tv, rec, NULL );
1893 if (r == ERROR_SUCCESS)
1894 r = TABLE_insert_row( view, rec, -1, FALSE );
1895 }
1896 break;
1897
1898 case MSIMODIFY_REPLACE:
1899 case MSIMODIFY_VALIDATE:
1902 FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec );
1904 break;
1905
1906 default:
1908 }
1909
1910 return r;
1911}
1912
1914{
1916
1917 TRACE("%p\n", view );
1918
1919 tv->table = NULL;
1920 tv->columns = NULL;
1921
1922 msi_free( tv );
1923
1924 return ERROR_SUCCESS;
1925}
1926
1928{
1930
1931 TRACE( "%p, %ld\n", view, tv->table->ref_count );
1932 return InterlockedIncrement(&tv->table->ref_count);
1933}
1934
1936{
1938 MSIRECORD *rec = NULL;
1939 MSIVIEW *columns = NULL;
1940 UINT row, r;
1941
1942 if (tv->table->col_count != number)
1944
1946 {
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 );
1950
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;
1954 }
1955
1956 rec = MSI_CreateRecord(2);
1957 if (!rec)
1958 return ERROR_OUTOFMEMORY;
1959
1960 MSI_RecordSetStringW(rec, 1, tv->name);
1962
1963 r = TABLE_CreateView(tv->db, L"_Columns", &columns);
1964 if (r != ERROR_SUCCESS)
1965 {
1966 msiobj_release(&rec->hdr);
1967 return r;
1968 }
1969
1970 r = msi_table_find_row((MSITABLEVIEW *)columns, rec, &row, NULL);
1971 if (r != ERROR_SUCCESS)
1972 goto done;
1973
1974 r = TABLE_delete_row(columns, row);
1975 if (r != ERROR_SUCCESS)
1976 goto done;
1977
1979
1980done:
1981 msiobj_release(&rec->hdr);
1982 columns->ops->delete(columns);
1983 return r;
1984}
1985
1987{
1989 INT ref = tv->table->ref_count;
1990 UINT r;
1991 INT i;
1992
1993 TRACE("%p %d\n", view, ref);
1994
1996 if (ref == 0)
1997 {
1998 for (i = tv->table->col_count - 1; i >= 0; i--)
1999 {
2000 if (tv->table->colinfo[i].type & MSITYPE_TEMPORARY)
2001 {
2003 if (r != ERROR_SUCCESS)
2004 break;
2005 }
2006 else
2007 {
2008 break;
2009 }
2010 }
2011
2012 if (!tv->table->col_count)
2013 {
2014 list_remove(&tv->table->entry);
2015 free_table(tv->table);
2017 }
2018 }
2019
2020 return ref;
2021}
2022
2024 INT type, BOOL hold)
2025{
2026 UINT i, r, table_id, col_id, size, offset;
2027 BOOL temporary = type & MSITYPE_TEMPORARY;
2029 MSICOLUMNINFO *colinfo;
2030
2031 if (temporary && !hold && !tv->table->ref_count)
2032 return ERROR_SUCCESS;
2033
2034 if (!temporary && tv->table->col_count &&
2037
2038 for (i = 0; i < tv->table->col_count; i++)
2039 {
2040 if (!wcscmp(tv->table->colinfo[i].colname, column))
2042 }
2043
2044 colinfo = msi_realloc(tv->table->colinfo, sizeof(*tv->table->colinfo) * (tv->table->col_count + 1));
2045 if (!colinfo)
2046 return ERROR_OUTOFMEMORY;
2047 tv->table->colinfo = colinfo;
2048
2049 r = msi_string2id( tv->db->strings, tv->name, -1, &table_id );
2050 if (r != ERROR_SUCCESS)
2051 return r;
2052 col_id = msi_add_string( tv->db->strings, column, -1, !temporary );
2053
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++;
2061
2063
2065 offset = tv->table->colinfo[tv->table->col_count - 1].offset;
2066 for (i = 0; i < tv->table->row_count; i++)
2067 {
2068 BYTE *data = msi_realloc( tv->table->data[i], size );
2069 if (!data)
2070 {
2071 tv->table->col_count--;
2072 return ERROR_OUTOFMEMORY;
2073 }
2074
2075 tv->table->data[i] = data;
2076 memset(data + offset, 0, size - offset);
2077 }
2078
2079 if (!temporary)
2080 {
2081 MSIVIEW *columns;
2082 MSIRECORD *rec;
2083
2084 rec = MSI_CreateRecord(4);
2085 if (!rec)
2086 {
2087 tv->table->col_count--;
2088 return ERROR_OUTOFMEMORY;
2089 }
2090
2091 MSI_RecordSetStringW(rec, 1, tv->name);
2092 MSI_RecordSetInteger(rec, 2, tv->table->col_count);
2094 MSI_RecordSetInteger(rec, 4, type);
2095
2096 r = TABLE_CreateView(tv->db, L"_Columns", &columns);
2097 if (r != ERROR_SUCCESS)
2098 {
2099 tv->table->col_count--;
2100 msiobj_release(&rec->hdr);
2101 return r;
2102 }
2103
2104 r = TABLE_insert_row(columns, rec, -1, FALSE);
2105 columns->ops->delete(columns);
2106 msiobj_release(&rec->hdr);
2107 if (r != ERROR_SUCCESS)
2108 {
2109 tv->table->col_count--;
2110 return r;
2111 }
2112 }
2113 if (hold)
2115 return ERROR_SUCCESS;
2116}
2117
2119{
2121 MSIVIEW *tables = NULL;
2122 MSIRECORD *rec = NULL;
2123 UINT r, row;
2124 INT i;
2125
2126 TRACE("dropping table %s\n", debugstr_w(tv->name));
2127
2128 for (i = tv->table->col_count - 1; i >= 0; i--)
2129 {
2131 if (r != ERROR_SUCCESS)
2132 return r;
2133 }
2134
2135 rec = MSI_CreateRecord(1);
2136 if (!rec)
2137 return ERROR_OUTOFMEMORY;
2138
2139 MSI_RecordSetStringW(rec, 1, tv->name);
2140
2141 r = TABLE_CreateView(tv->db, L"_Tables", &tables);
2142 if (r != ERROR_SUCCESS)
2143 {
2144 msiobj_release(&rec->hdr);
2145 return r;
2146 }
2147
2149 if (r != ERROR_SUCCESS)
2150 goto done;
2151
2153 if (r != ERROR_SUCCESS)
2154 goto done;
2155
2156 list_remove(&tv->table->entry);
2157 free_table(tv->table);
2158
2159done:
2160 msiobj_release(&rec->hdr);
2161 tables->ops->delete(tables);
2162
2163 return r;
2164}
2165
2166static const MSIVIEWOPS table_ops =
2167{
2185 NULL,
2186 TABLE_drop,
2187};
2188
2190{
2191 MSITABLEVIEW *tv ;
2192 UINT r, sz;
2193
2194 TRACE("%p %s %p\n", db, debugstr_w(name), view );
2195
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 );
2200
2201 sz = FIELD_OFFSET( MSITABLEVIEW, name[lstrlenW( name ) + 1] );
2202 tv = msi_alloc_zero( sz );
2203 if( !tv )
2204 return ERROR_FUNCTION_FAILED;
2205
2206 r = get_table( db, name, &tv->table );
2207 if( r != ERROR_SUCCESS )
2208 {
2209 msi_free( tv );
2210 WARN("table not found\n");
2211 return r;
2212 }
2213
2214 TRACE("table %p found with %d columns\n", tv->table, tv->table->col_count);
2215
2216 /* fill the structure */
2217 tv->view.ops = &table_ops;
2218 tv->db = db;
2219 tv->columns = tv->table->colinfo;
2220 tv->num_cols = tv->table->col_count;
2222
2223 TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size );
2224
2225 *view = (MSIVIEW*) tv;
2226 lstrcpyW( tv->name, name );
2227
2228 return ERROR_SUCCESS;
2229}
2230
2232{
2233 DWORD i, p, len, key_len = 0;
2234 WCHAR *key;
2235
2236 for (i = 0; i < tv->num_cols; i++)
2237 {
2238 if (!(tv->columns[i].type & MSITYPE_KEY))
2239 continue;
2240 if (MSI_RecordGetStringW( rec, i+1, NULL, &len ) == ERROR_SUCCESS)
2241 key_len += len;
2242 key_len++;
2243 }
2244
2245 key = msi_alloc( key_len * sizeof(WCHAR) );
2246 if(!key)
2247 return NULL;
2248
2249 p = 0;
2250 for (i = 0; i < tv->num_cols; i++)
2251 {
2252 if (!(tv->columns[i].type & MSITYPE_KEY))
2253 continue;
2254 if (p)
2255 key[p++] = '\t';
2256 len = key_len - p;
2257 if (MSI_RecordGetStringW( rec, i+1, key + p, &len ) == ERROR_SUCCESS)
2258 p += len;
2259 }
2260 return key;
2261}
2262
2264{
2265 UINT p = 0, i, r;
2266 DWORD l;
2267
2268 l = wcslen( tv->name );
2269 if (name && *len > l)
2270 memcpy(name, tv->name, l * sizeof(WCHAR));
2271 p += l;
2272
2273 for ( i = 0; i < tv->num_cols; i++ )
2274 {
2275 if (!(tv->columns[i].type & MSITYPE_KEY))
2276 continue;
2277
2278 if (name && *len > p + 1)
2279 name[p] = '.';
2280 p++;
2281
2282 l = (*len > p ? *len - p : 0);
2283 r = MSI_RecordGetStringW( rec, i + 1, name ? name + p : NULL, &l );
2284 if (r != ERROR_SUCCESS)
2285 return r;
2286 p += l;
2287 }
2288
2289 if (name && *len > p)
2290 name[p] = 0;
2291
2292 *len = p;
2293 return ERROR_SUCCESS;
2294}
2295
2297{
2299
2300 if (!tv->table || col > tv->table->col_count)
2301 {
2302 *val = 0;
2303 return ERROR_SUCCESS;
2304 }
2305 return TABLE_fetch_int( view, row, col, val );
2306}
2307
2309{
2311
2312 if (!tv->table || col > tv->table->col_count)
2313 {
2314 *stm = NULL;
2315 return ERROR_SUCCESS;
2316 }
2317 return TABLE_fetch_stream( view, row, col, stm );
2318}
2319
2321{
2322 static const WCHAR query_pfx[] =
2323 L"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`, `Data`, `Current`) VALUES (1, '";
2324
2326 WCHAR buf[256], *query;
2327 MSIRECORD *old_rec;
2328 MSIQUERY *q;
2329 WCHAR *key;
2330 UINT i, p, r, qlen;
2331 DWORD len;
2332
2333 if (!wcscmp( tv->name, L"_Columns" ))
2334 {
2335 ERR( "trying to modify existing column\n" );
2337 }
2338
2339 if (!wcscmp( tv->name, L"_Tables" ))
2340 {
2341 ERR( "trying to modify existing table\n" );
2343 }
2344
2345 key = create_key_string( tv, rec );
2346 if (!key)
2347 return ERROR_OUTOFMEMORY;
2348
2349 r = msi_view_get_row( tv->db, view, row, &old_rec );
2350 if (r != ERROR_SUCCESS)
2351 old_rec = NULL;
2352
2353 for (i = 0; i < tv->num_cols; i++)
2354 {
2355 if (!(mask & (1 << i)))
2356 continue;
2357 if (tv->columns[i].type & MSITYPE_KEY)
2358 continue;
2359
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 );
2366 else
2367 r = MSI_RecordGetStringW( rec, i + 1, NULL, &len );
2368 if (r != ERROR_SUCCESS)
2369 {
2370 if (old_rec)
2371 msiobj_release( &old_rec->hdr );
2372 msi_free( key );
2373 return r;
2374 }
2375 qlen += len + 3;
2376 if (old_rec && (r = MSI_RecordGetStringW( old_rec, i+1, NULL, &len )))
2377 {
2378 msiobj_release( &old_rec->hdr );
2379 msi_free( key );
2380 return r;
2381 }
2382 qlen += len + 3; /* strlen("')") + 1 */
2383
2384 if (qlen > ARRAY_SIZE(buf))
2385 {
2386 query = msi_alloc( qlen * sizeof(WCHAR) );
2387 if (!query)
2388 {
2389 if (old_rec)
2390 msiobj_release( &old_rec->hdr );
2391 msi_free( key );
2392 return ERROR_OUTOFMEMORY;
2393 }
2394 }
2395 else
2396 {
2397 query = buf;
2398 }
2399
2400 memcpy( query, query_pfx, p * sizeof(WCHAR) );
2401 len = wcslen( tv->name );
2402 memcpy( query + p, tv->name, len * sizeof(WCHAR) );
2403 p += len;
2404 query[p++] = '\'';
2405 query[p++] = ',';
2406 query[p++] = '\'';
2407 len = wcslen( tv->columns[i].colname );
2408 memcpy( query + p, tv->columns[i].colname, len * sizeof(WCHAR) );
2409 p += len;
2410 query[p++] = '\'';
2411 query[p++] = ',';
2412 query[p++] = '\'';
2413 len = wcslen( key );
2414 memcpy( query + p, key, len * sizeof(WCHAR) );
2415 p += len;
2416 query[p++] = '\'';
2417 query[p++] = ',';
2418 query[p++] = '\'';
2419 len = qlen - p;
2420 if (MSITYPE_IS_BINARY( tv->columns[i].type ))
2421 msi_record_stream_name( tv, rec, query + p, &len );
2422 else
2423 MSI_RecordGetStringW( rec, i + 1, query + p, &len );
2424 p += len;
2425 query[p++] = '\'';
2426 query[p++] = ',';
2427 query[p++] = '\'';
2428 if (old_rec)
2429 {
2430 len = qlen - p;
2431 MSI_RecordGetStringW( old_rec, i + 1, query + p, &len );
2432 p += len;
2433 }
2434 query[p++] = '\'';
2435 query[p++] = ')';
2436 query[p++] = 0;
2437
2438 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2439 if (query != buf)
2440 msi_free( query );
2441 if (r != ERROR_SUCCESS)
2442 {
2443 if (old_rec)
2444 msiobj_release( &old_rec->hdr );
2445 msi_free( key );
2446 return r;
2447 }
2448
2449 r = MSI_ViewExecute( q, NULL );
2450 msiobj_release( &q->hdr );
2451 if (r != ERROR_SUCCESS)
2452 {
2453 if (old_rec)
2454 msiobj_release( &old_rec->hdr );
2455 msi_free( key );
2456 return r;
2457 }
2458 }
2459
2460 if (old_rec)
2461 msiobj_release( &old_rec->hdr );
2462 msi_free( key );
2463 return ERROR_SUCCESS;
2464}
2465
2467{
2468 static const WCHAR query_fmt[] =
2469 L"INSERT INTO `_TransformView` (`Table`, `Column`, `new`) VALUES ('%s', 'CREATE', 1)";
2470
2471 WCHAR buf[256], *query = buf;
2472 const WCHAR *name;
2473 MSIQUERY *q;
2474 int len;
2475 UINT r;
2476
2477 name = msi_record_get_string( rec, 1, &len );
2478 if (!name)
2480
2481 len = _snwprintf( NULL, 0, query_fmt, name ) + 1;
2482 if (len > ARRAY_SIZE(buf))
2483 {
2484 query = msi_alloc( len * sizeof(WCHAR) );
2485 if (!query)
2486 return ERROR_OUTOFMEMORY;
2487 }
2488 swprintf( query, len, query_fmt, name );
2489
2490 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2491 if (query != buf)
2492 msi_free( query );
2493 if (r != ERROR_SUCCESS)
2494 return r;
2495
2496 r = MSI_ViewExecute( q, NULL );
2497 msiobj_release( &q->hdr );
2498 return r;
2499}
2500
2502{
2503 static const WCHAR query_pfx[] =
2504 L"INSERT INTO `_TransformView` (`new`, `Table`, `Current`, `Column`, `Data`) VALUES (1, '";
2505
2506 WCHAR buf[256], *query = buf;
2507 UINT i, p, r, qlen;
2508 DWORD len;
2509 MSIQUERY *q;
2510
2511 qlen = p = wcslen( query_pfx );
2512 for (i = 1; i <= 4; i++)
2513 {
2514 r = MSI_RecordGetStringW( rec, i, NULL, &len );
2515 if (r != ERROR_SUCCESS)
2516 return r;
2517 qlen += len + 3; /* strlen( "','" ) */
2518 }
2519
2520 if (qlen > ARRAY_SIZE(buf))
2521 {
2522 query = msi_alloc( len * sizeof(WCHAR) );
2523 qlen = len;
2524 if (!query)
2525 return ERROR_OUTOFMEMORY;
2526 }
2527
2528 memcpy( query, query_pfx, p * sizeof(WCHAR) );
2529 for (i = 1; i <= 4; i++)
2530 {
2531 len = qlen - p;
2532 MSI_RecordGetStringW( rec, i, query + p, &len );
2533 p += len;
2534 query[p++] = '\'';
2535 if (i != 4)
2536 {
2537 query[p++] = ',';
2538 query[p++] = '\'';
2539 }
2540 }
2541 query[p++] = ')';
2542 query[p++] = 0;
2543
2544 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2545 if (query != buf)
2546 msi_free( query );
2547 if (r != ERROR_SUCCESS)
2548 return r;
2549
2550 r = MSI_ViewExecute( q, NULL );
2551 msiobj_release( &q->hdr );
2552 return r;
2553}
2554
2556{
2557 static const WCHAR query_fmt[] =
2558 L"INSERT INTO `_TransformView` (`new`, `Table`, `Column`, `Row`) VALUES (1, '%s', 'INSERT', '%s')";
2559
2561 WCHAR buf[256], *query = buf;
2562 MSIQUERY *q;
2563 WCHAR *key;
2564 int len;
2565 UINT r;
2566
2567 if (!wcscmp(tv->name, L"_Tables"))
2568 return TransformView_create_table( tv, rec );
2569
2570 if (!wcscmp(tv->name, L"_Columns"))
2571 return TransformView_add_column( tv, rec );
2572
2573 key = create_key_string( tv, rec );
2574 if (!key)
2575 return ERROR_OUTOFMEMORY;
2576
2577 len = _snwprintf( NULL, 0, query_fmt, tv->name, key ) + 1;
2578 if (len > ARRAY_SIZE(buf))
2579 {
2580 query = msi_alloc( len * sizeof(WCHAR) );
2581 if (!query)
2582 {
2583 msi_free( key );
2584 return ERROR_OUTOFMEMORY;
2585 }
2586 }
2587 swprintf( query, len, query_fmt, tv->name, key );
2588 msi_free( key );
2589
2590 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2591 if (query != buf)
2592 msi_free( query );
2593 if (r != ERROR_SUCCESS)
2594 return r;
2595
2596 r = MSI_ViewExecute( q, NULL );
2597 msiobj_release( &q->hdr );
2598 if (r != ERROR_SUCCESS)
2599 return r;
2600
2601 return TransformView_set_row( view, row, rec, ~0 );
2602}
2603
2605{
2606 static const WCHAR query_pfx[] = L"INSERT INTO `_TransformView` ( `new`, `Table`, `Column` ) VALUES ( 1, '";
2607 static const WCHAR query_sfx[] = L"', 'DROP' )";
2608
2609 WCHAR buf[256], *query = buf;
2610 UINT r, table_id, len;
2611 const WCHAR *table;
2612 int table_len;
2613 MSIQUERY *q;
2614
2615 r = TABLE_fetch_int( &tv->view, row, 1, &table_id );
2616 if (r != ERROR_SUCCESS)
2617 return r;
2618
2619 table = msi_string_lookup( tv->db->strings, table_id, &table_len );
2620 if (!table)
2622
2623 len = ARRAY_SIZE(query_pfx) - 1 + table_len + ARRAY_SIZE(query_sfx);
2624 if (len > ARRAY_SIZE(buf))
2625 {
2626 query = msi_alloc( len * sizeof(WCHAR) );
2627 if (!query)
2628 return ERROR_OUTOFMEMORY;
2629 }
2630
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) );
2634 len += table_len;
2635 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2636
2637 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2638 if (query != buf)
2639 msi_free( query );
2640 if (r != ERROR_SUCCESS)
2641 return r;
2642
2643 r = MSI_ViewExecute( q, NULL );
2644 msiobj_release( &q->hdr );
2645 return r;
2646}
2647
2649{
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"')";
2653
2655 WCHAR *key, buf[256], *query = buf;
2656 UINT r, len, name_len, key_len;
2657 MSIRECORD *rec;
2658 MSIQUERY *q;
2659
2660 if (!wcscmp( tv->name, L"_Columns" ))
2661 {
2662 ERR("trying to remove column\n");
2664 }
2665
2666 if (!wcscmp( tv->name, L"_Tables" ))
2667 return TransformView_drop_table( tv, row );
2668
2669 r = msi_view_get_row( tv->db, view, row, &rec );
2670 if (r != ERROR_SUCCESS)
2671 return r;
2672
2673 key = create_key_string( tv, rec );
2674 msiobj_release( &rec->hdr );
2675 if (!key)
2676 return ERROR_OUTOFMEMORY;
2677
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))
2682 {
2683 query = msi_alloc( len * sizeof(WCHAR) );
2684 if (!query)
2685 {
2686 msi_free( tv );
2687 msi_free( key );
2688 return ERROR_OUTOFMEMORY;
2689 }
2690 }
2691
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) );
2695 len += name_len;
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) );
2699 len += key_len;
2700 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2701 msi_free( key );
2702
2703 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2704 if (query != buf)
2705 msi_free( query );
2706 if (r != ERROR_SUCCESS)
2707 return r;
2708
2709 r = MSI_ViewExecute( q, NULL );
2710 msiobj_release( &q->hdr );
2711 return r;
2712}
2713
2715{
2716 return ERROR_SUCCESS;
2717}
2718
2720{
2721 return ERROR_SUCCESS;
2722}
2723
2725{
2726 return TABLE_get_dimensions( view, rows, cols );
2727}
2728
2730 BOOL *temporary, LPCWSTR *table_name )
2731{
2732 return TABLE_get_column_info( view, n, name, type, temporary, table_name );
2733}
2734
2736{
2738 if (!tv->table || tv->columns != tv->table->colinfo)
2739 msi_free( tv->columns );
2740 return TABLE_delete( view );
2741}
2742
2744{
2747 NULL,
2748 NULL,
2749 NULL,
2757 NULL,
2759 NULL,
2760 NULL,
2761 NULL,
2762 NULL,
2763 NULL
2764};
2765
2767{
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";
2770
2771 WCHAR buf[256], *query = buf;
2772 UINT r, len, name_len, size, add_col;
2773 MSICOLUMNINFO *colinfo;
2774 MSITABLEVIEW *tv;
2775 MSIRECORD *rec;
2776 MSIQUERY *q;
2777
2778 name_len = wcslen( name );
2779
2780 r = TABLE_CreateView( db, name, view );
2782 {
2783 /* table does not exist */
2784 size = FIELD_OFFSET( MSITABLEVIEW, name[name_len + 1] );
2785 tv = msi_alloc_zero( size );
2786 if (!tv)
2787 return ERROR_OUTOFMEMORY;
2788
2789 tv->db = db;
2790 memcpy( tv->name, name, name_len * sizeof(WCHAR) );
2791 *view = (MSIVIEW*)tv;
2792 }
2793 else if (r != ERROR_SUCCESS)
2794 {
2795 return r;
2796 }
2797 else
2798 {
2799 tv = (MSITABLEVIEW*)*view;
2800 }
2801
2803
2804 len = ARRAY_SIZE(query_pfx) + name_len + ARRAY_SIZE(query_sfx) - 1;
2805 if (len > ARRAY_SIZE(buf))
2806 {
2807 query = msi_alloc( len * sizeof(WCHAR) );
2808 if (!query)
2809 {
2810 msi_free( tv );
2811 return ERROR_OUTOFMEMORY;
2812 }
2813 }
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) );
2817 len += name_len;
2818 memcpy( query + len, query_sfx, ARRAY_SIZE(query_sfx) * sizeof(WCHAR) );
2819
2820 r = MSI_DatabaseOpenViewW( tv->db, query, &q );
2821 if (query != buf)
2822 msi_free( query );
2823 if (r != ERROR_SUCCESS)
2824 {
2825 msi_free( tv );
2826 return r;
2827 }
2828
2829 r = MSI_ViewExecute( q, NULL );
2830 if (r != ERROR_SUCCESS)
2831 {
2832 msi_free( tv );
2833 return r;
2834 }
2835
2836 r = q->view->ops->get_dimensions( q->view, &add_col, NULL );
2837 if (r != ERROR_SUCCESS)
2838 {
2839 MSI_ViewClose( q );
2840 msiobj_release( &q->hdr );
2841 msi_free( tv );
2842 return r;
2843 }
2844 if (!add_col)
2845 {
2846 MSI_ViewClose( q );
2847 msiobj_release( &q->hdr );
2848 return ERROR_SUCCESS;
2849 }
2850
2851 colinfo = msi_alloc_zero( (add_col + tv->num_cols) * sizeof(*colinfo) );
2852 if (!colinfo)
2853 {
2854 MSI_ViewClose( q );
2855 msiobj_release( &q->hdr );
2856 msi_free( tv );
2857 return r;
2858 }
2859
2860 while (MSI_ViewFetch( q, &rec ) == ERROR_SUCCESS)
2861 {
2862 int name_len;
2863 const WCHAR *name = msi_record_get_string( rec, 1, &name_len );
2864 const WCHAR *type = msi_record_get_string( rec, 2, NULL );
2865 UINT name_id, idx;
2866
2867 idx = _wtoi( msi_record_get_string(rec, 3, NULL) );
2868 colinfo[idx - 1].number = idx;
2869 colinfo[idx - 1].type = _wtoi( type );
2870
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 );
2874 else
2875 ERR( "column name %s is not defined in strings table\n", wine_dbgstr_w(name) );
2876 msiobj_release( &rec->hdr );
2877 }
2878 MSI_ViewClose( q );
2879 msiobj_release( &q->hdr );
2880
2881 memcpy( colinfo, tv->columns, tv->num_cols * sizeof(*colinfo) );
2882 tv->columns = colinfo;
2883 tv->num_cols += add_col;
2884 return ERROR_SUCCESS;
2885}
2886
2888{
2889 UINT r, bytes_per_strref;
2890 HRESULT hr;
2891 MSITABLE *table = NULL;
2892
2893 TRACE("%p\n",db);
2894
2895 r = msi_save_string_table( db->strings, db->storage, &bytes_per_strref );
2896 if( r != ERROR_SUCCESS )
2897 {
2898 WARN("failed to save string table r=%08x\n",r);
2899 return r;
2900 }
2901
2903 {
2904 r = save_table( db, table, bytes_per_strref );
2905 if( r != ERROR_SUCCESS )
2906 {
2907 WARN("failed to save table %s (r=%08x)\n",
2908 debugstr_w(table->name), r);
2909 return r;
2910 }
2911 }
2912
2913 hr = IStorage_Commit( db->storage, 0 );
2914 if (FAILED( hr ))
2915 {
2916 WARN( "failed to commit changes %#lx\n", hr );
2918 }
2919 return r;
2920}
2921
2923{
2924 MSITABLE *t;
2925 UINT r;
2926
2927 TRACE("%p %s\n", db, debugstr_w(table));
2928
2929 if (!table)
2930 return MSICONDITION_ERROR;
2931
2932 r = get_table( db, table, &t );
2933 if (r != ERROR_SUCCESS)
2934 return MSICONDITION_NONE;
2935
2936 return t->persistent;
2937}
2938
2940{
2941 UINT ret = 0, i;
2942
2943 for (i = 0; i < bytes; i++)
2944 ret += (data[col + i] << i * 8);
2945
2946 return ret;
2947}
2948
2950{
2951 UINT r;
2952 DWORD len;
2953 WCHAR *name;
2954
2955 TRACE("%p %p\n", tv, rec);
2956
2957 r = msi_record_stream_name( tv, rec, NULL, &len );
2958 if (r != ERROR_SUCCESS)
2959 return r;
2960 len++;
2961
2962 name = msi_alloc( len * sizeof(WCHAR) );
2963 if (!name)
2964 return ERROR_OUTOFMEMORY;
2965
2966 r = msi_record_stream_name( tv, rec, name, &len );
2967 if (r != ERROR_SUCCESS)
2968 {
2969 msi_free( name );
2970 return r;
2971 }
2972
2973 *pstname = encode_streamname( FALSE, name );
2974 msi_free( name );
2975 return ERROR_SUCCESS;
2976}
2977
2979 IStorage *stg, const BYTE *rawdata, UINT bytes_per_strref )
2980{
2981 UINT i, val, ofs = 0;
2982 USHORT mask;
2983 MSICOLUMNINFO *columns = tv->columns;
2984 MSIRECORD *rec;
2985
2986 mask = rawdata[0] | (rawdata[1] << 8);
2987 rawdata += 2;
2988
2989 rec = MSI_CreateRecord( tv->num_cols );
2990 if( !rec )
2991 return rec;
2992
2993 TRACE("row ->\n");
2994 for( i=0; i<tv->num_cols; i++ )
2995 {
2996 if ( (mask&1) && (i>=(mask>>8)) )
2997 break;
2998 /* all keys must be present */
2999 if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<<i) & ~mask) )
3000 continue;
3001
3002 if( MSITYPE_IS_BINARY(tv->columns[i].type) )
3003 {
3004 LPWSTR encname;
3005 IStream *stm = NULL;
3006 UINT r;
3007
3008 ofs += bytes_per_column( tv->db, &columns[i], bytes_per_strref );
3009
3010 r = msi_record_encoded_stream_name( tv, rec, &encname );
3011 if ( r != ERROR_SUCCESS )
3012 {
3013 msiobj_release( &rec->hdr );
3014 return NULL;
3015 }
3016 r = IStorage_OpenStream( stg, encname, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
3017 if ( r != ERROR_SUCCESS )
3018 {
3019 msiobj_release( &rec->hdr );
3020 msi_free( encname );
3021 return NULL;
3022 }
3023
3024 MSI_RecordSetStream( rec, i+1, stm );
3025 TRACE(" field %d [%s]\n", i+1, debugstr_w(encname));
3026 msi_free( encname );
3027 }
3028 else if( columns[i].type & MSITYPE_STRING )
3029 {
3030 int len;
3031 const WCHAR *sval;
3032
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;
3038 }
3039 else
3040 {
3041 UINT n = bytes_per_column( tv->db, &columns[i], bytes_per_strref );
3042 switch( n )
3043 {
3044 case 2:
3045 val = read_raw_int(rawdata, ofs, n);
3046 if (val)
3047 MSI_RecordSetInteger( rec, i+1, val-0x8000 );
3048 TRACE(" field %d [0x%04x]\n", i+1, val );
3049 break;
3050 case 4:
3051 val = read_raw_int(rawdata, ofs, n);
3052 if (val)
3053 MSI_RecordSetInteger( rec, i+1, val^0x80000000 );
3054 TRACE(" field %d [0x%08x]\n", i+1, val );
3055 break;
3056 default:
3057 ERR("oops - unknown column width %d\n", n);
3058 break;
3059 }
3060 ofs += n;
3061 }
3062 }
3063 return rec;
3064}
3065
3066static void dump_table( const string_table *st, const USHORT *rawdata, UINT rawsize )
3067{
3068 UINT i;
3069 for (i = 0; i < rawsize / 2; i++)
3070 {
3071 int len;
3072 const WCHAR *sval = msi_string_lookup( st, rawdata[i], &len );
3073 MESSAGE(" %04x %s\n", rawdata[i], debugstr_wn(sval, len) );
3074 }
3075}
3076
3078{
3079 UINT i, r, *data;
3080
3081 data = msi_alloc( tv->num_cols *sizeof (UINT) );
3082 for( i=0; i<tv->num_cols; i++ )
3083 {
3084 data[i] = 0;
3085
3086 if ( ~tv->columns[i].type & MSITYPE_KEY )
3087 continue;
3088
3089 /* turn the transform column value into a row value */
3090 if ( ( tv->columns[i].type & MSITYPE_STRING ) &&
3092 {
3093 int len;
3094 const WCHAR *str = msi_record_get_string( rec, i+1, &len );
3095 if (str)
3096 {
3097 r = msi_string2id( tv->db->strings, str, len, &data[i] );
3098
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)
3102 {
3103 msi_free( data );
3104 return NULL;
3105 }
3106 }
3107 else data[i] = 0;
3108 }
3109 else
3110 {
3111 if (int_to_table_storage( tv, i + 1, MSI_RecordGetInteger( rec, i + 1 ), &data[i] ))
3112 {
3113 msi_free( data );
3114 return NULL;
3115 }
3116 }
3117 }
3118 return data;
3119}
3120
3122{
3124
3125 for( i=0; i<tv->num_cols; i++ )
3126 {
3127 if ( ~tv->columns[i].type & MSITYPE_KEY )
3128 continue;
3129
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 )
3133 {
3134 ERR("TABLE_fetch_int shouldn't fail here\n");
3135 break;
3136 }
3137
3138 /* if this key matches, move to the next column */
3139 if ( x != data[i] )
3140 {
3142 break;
3143 }
3144 if (column) *column = i;
3146 }
3147 return ret;
3148}
3149
3151{
3153
3154 data = msi_record_to_row( tv, rec );
3155 if( !data )
3156 return r;
3157 for( i = 0; i < tv->table->row_count; i++ )
3158 {
3159 r = msi_row_matches( tv, i, data, column );
3160 if( r == ERROR_SUCCESS )
3161 {
3162 *row = i;
3163 break;
3164 }
3165 }
3166 msi_free( data );
3167 return r;
3168}
3169
3170typedef struct
3171{
3172 struct list entry;
3175
3178 UINT bytes_per_strref, int err_cond )
3179{
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;
3184 WCHAR coltable[32];
3185 const WCHAR *name;
3186
3187 if (!transform)
3188 return ERROR_SUCCESS;
3189
3190 name = transform->name;
3191
3192 coltable[0] = 0;
3193 TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) );
3194
3195 /* read the transform data */
3196 read_stream_data( stg, name, TRUE, &rawdata, &rawsize );
3197 if ( !rawdata )
3198 {
3199 TRACE("table %s empty\n", debugstr_w(name) );
3200 return ERROR_INVALID_TABLE;
3201 }
3202
3203 /* create a table view */
3204 if ( err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM )
3205 r = TransformView_Create( db, st, name, (MSIVIEW**) &tv );
3206 else
3207 r = TABLE_CreateView( db, name, (MSIVIEW**) &tv );
3208 if( r != ERROR_SUCCESS )
3209 goto err;
3210
3211 r = tv->view.ops->execute( &tv->view, NULL );
3212 if( r != ERROR_SUCCESS )
3213 goto err;
3214
3215 TRACE("name = %s columns = %u row_size = %u raw size = %u\n",
3216 debugstr_w(name), tv->num_cols, tv->row_size, rawsize );
3217
3218 /* interpret the data */
3219 for (n = 0; n < rawsize;)
3220 {
3221 mask = rawdata[n] | (rawdata[n + 1] << 8);
3222 if (mask & 1)
3223 {
3224 /*
3225 * if the low bit is set, columns are continuous and
3226 * the number of columns is specified in the high byte
3227 */
3228 sz = 2;
3229 num_cols = mask >> 8;
3230 if (num_cols > tv->num_cols)
3231 {
3232 ERR("excess columns in transform: %u > %u\n", num_cols, tv->num_cols);
3233 break;
3234 }
3235
3236 for (i = 0; i < num_cols; i++)
3237 {
3238 if( (tv->columns[i].type & MSITYPE_STRING) &&
3240 sz += bytes_per_strref;
3241 else
3242 sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
3243 }
3244 }
3245 else
3246 {
3247 /*
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.
3251 *
3252 * mask == 0 is a special case ... only the keys will be present
3253 * and it means that this row should be deleted.
3254 */
3255 sz = 2;
3256 num_cols = tv->num_cols;
3257 for (i = 0; i < num_cols; i++)
3258 {
3259 if ((tv->columns[i].type & MSITYPE_KEY) || ((1 << i) & mask))
3260 {
3261 if ((tv->columns[i].type & MSITYPE_STRING) &&
3263 sz += bytes_per_strref;
3264 else
3265 sz += bytes_per_column( tv->db, &tv->columns[i], bytes_per_strref );
3266 }
3267 }
3268 }
3269
3270 /* check we didn't run of the end of the table */
3271 if (n + sz > rawsize)
3272 {
3273 ERR("borked.\n");
3274 dump_table( st, (USHORT *)rawdata, rawsize );
3275 break;
3276 }
3277
3278 rec = msi_get_transform_record( tv, st, stg, &rawdata[n], bytes_per_strref );
3279 if (rec)
3280 {
3281 WCHAR table[32];
3282 DWORD sz = 32;
3284 UINT row = 0;
3285
3286 if (!wcscmp( name, L"_Columns" ))
3287 {
3288 MSI_RecordGetStringW( rec, 1, table, &sz );
3289 number = MSI_RecordGetInteger( rec, 2 );
3290
3291 /*
3292 * Native msi seems writes nul into the Number (2nd) column of
3293 * the _Columns table when there are new columns
3294 */
3295 if ( number == MSI_NULL_INTEGER )
3296 {
3297 /* reset the column number on a new table */
3298 if (wcscmp( coltable, table ))
3299 {
3300 colcol = 0;
3301 lstrcpyW( coltable, table );
3302 }
3303
3304 /* fix nul column numbers */
3305 MSI_RecordSetInteger( rec, 2, ++colcol );
3306 }
3307 }
3308
3309 if (TRACE_ON(msidb)) dump_record( rec );
3310
3311 if (tv->table)
3312 r = msi_table_find_row( tv, rec, &row, NULL );
3313 else
3315 if (r == ERROR_SUCCESS)
3316 {
3317 if (!mask)
3318 {
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);
3323 }
3324 else if (mask & 1)
3325 {
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);
3330 }
3331 else
3332 {
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);
3337 }
3338 }
3339 else
3340 {
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);
3345 }
3346
3347 if (!(err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM) &&
3348 !wcscmp( name, L"_Columns" ))
3350
3351 msiobj_release( &rec->hdr );
3352 }
3353
3354 n += sz;
3355 }
3356
3357err:
3358 /* no need to free the table, it's associated with the database */
3359 msi_free( rawdata );
3360 if( tv )
3361 tv->view.ops->delete( &tv->view );
3362
3363 return ERROR_SUCCESS;
3364}
3365
3366/*
3367 * msi_table_apply_transform
3368 *
3369 * Enumerate the table transforms in a transform storage and apply each one.
3370 */
3372{
3373 struct list transforms;
3374 IEnumSTATSTG *stgenum = NULL;
3376 TRANSFORMDATA *tables = NULL, *columns = NULL;
3377 HRESULT hr;
3378 STATSTG stat;
3381 UINT bytes_per_strref;
3382 BOOL property_update = FALSE;
3384
3385 TRACE("%p %p\n", db, stg );
3386
3387 strings = msi_load_string_table( stg, &bytes_per_strref );
3388 if( !strings )
3389 goto end;
3390
3391 hr = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
3392 if (FAILED( hr ))
3393 goto end;
3394
3395 list_init(&transforms);
3396
3397 while ( TRUE )
3398 {
3399 MSITABLEVIEW *tv = NULL;
3400 WCHAR name[0x40];
3401 ULONG count = 0;
3402
3403 hr = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
3404 if (FAILED( hr ) || !count)
3405 break;
3406
3407 decode_streamname( stat.pwcsName, name );
3408 CoTaskMemFree( stat.pwcsName );
3409 if ( name[0] != 0x4840 )
3410 continue;
3411
3412 if ( !wcscmp( name+1, L"_StringPool" ) ||
3413 !wcscmp( name+1, L"_StringData" ) )
3414 continue;
3415
3417 if ( !transform )
3418 break;
3419
3420 list_add_tail( &transforms, &transform->entry );
3421
3422 transform->name = strdupW( name + 1 );
3423
3424 if ( !wcscmp( transform->name, L"_Tables" ) )
3425 tables = transform;
3426 else if (!wcscmp( transform->name, L"_Columns" ) )
3427 columns = transform;
3428 else if (!wcscmp( transform->name, L"Property" ))
3429 property_update = TRUE;
3430
3431 TRACE("transform contains stream %s\n", debugstr_w(name));
3432
3433 /* load the table */
3434 if (TABLE_CreateView( db, transform->name, (MSIVIEW**) &tv ) != ERROR_SUCCESS)
3435 continue;
3436
3437 if (tv->view.ops->execute( &tv->view, NULL ) != ERROR_SUCCESS)
3438 {
3439 tv->view.ops->delete( &tv->view );
3440 continue;
3441 }
3442
3443 tv->view.ops->delete( &tv->view );
3444 }
3445
3446 if (err_cond & MSITRANSFORM_ERROR_VIEWTRANSFORM)
3447 {
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";
3452
3453 MSIQUERY *query;
3454 UINT r;
3455
3457 if (r != ERROR_SUCCESS)
3458 goto end;
3459
3461 if (r == ERROR_SUCCESS)
3463 msiobj_release( &query->hdr );
3465 goto end;
3466
3467 if (TABLE_CreateView(db, L"_TransformView", &transform_view) != ERROR_SUCCESS)
3468 goto end;
3469
3471 transform_view->ops->add_ref( transform_view );
3472
3473 r = transform_view->ops->add_column( transform_view, L"new",
3474 MSITYPE_TEMPORARY | MSITYPE_NULLABLE | 0x402 /* INT */, FALSE );
3475 if (r != ERROR_SUCCESS)
3476 goto end;
3477 }
3478
3479 /*
3480 * Apply _Tables and _Columns transforms first so that
3481 * the table metadata is correct, and empty tables exist.
3482 */
3483 ret = msi_table_load_transform( db, stg, strings, tables, bytes_per_strref, err_cond );
3485 goto end;
3486
3487 ret = msi_table_load_transform( db, stg, strings, columns, bytes_per_strref, err_cond );
3489 goto end;
3490
3492
3493 while ( !list_empty( &transforms ) )
3494 {
3495 transform = LIST_ENTRY( list_head( &transforms ), TRANSFORMDATA, entry );
3496
3497 if ( wcscmp( transform->name, L"_Columns" ) &&
3498 wcscmp( transform->name, L"_Tables" ) &&
3499 ret == ERROR_SUCCESS )
3500 {
3501 ret = msi_table_load_transform( db, stg, strings, transform, bytes_per_strref, err_cond );
3502 }
3503
3504 list_remove( &transform->entry );
3505 msi_free( transform->name );
3507 }
3508
3509 if ( ret == ERROR_SUCCESS )
3510 {
3511 append_storage_to_db( db, stg );
3512 if (property_update) msi_clone_properties( db );
3513 }
3514
3515end:
3516 if ( stgenum )
3517 IEnumSTATSTG_Release( stgenum );
3518 if ( strings )
3520 if (transform_view)
3521 {
3522 struct tagMSITABLE *table = ((MSITABLEVIEW*)transform_view)->table;
3523
3524 if (ret != ERROR_SUCCESS)
3525 transform_view->ops->release( transform_view );
3526
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 );
3530 }
3531
3532 return ret;
3533}
#define stat
Definition: acwin.h:99
static unsigned char bytes[4]
Definition: adnsresfilter.c:74
#define InterlockedIncrement
Definition: armddk.h:53
#define InterlockedDecrement
Definition: armddk.h:52
#define WINE_DEFAULT_DEBUG_CHANNEL(t)
Definition: precomp.h:23
static WCHAR * strdupW(const WCHAR *src)
Definition: main.c:92
#define ARRAY_SIZE(A)
Definition: main.h:33
static void list_remove(struct list_entry *entry)
Definition: list.h:90
static int list_empty(struct list_entry *head)
Definition: list.h:58
static void list_add_tail(struct list_entry *head, struct list_entry *entry)
Definition: list.h:83
static void list_add_head(struct list_entry *head, struct list_entry *entry)
Definition: list.h:76
static void list_init(struct list_entry *head)
Definition: list.h:51
#define FIXME(fmt,...)
Definition: debug.h:114
#define WARN(fmt,...)
Definition: debug.h:115
#define ERR(fmt,...)
Definition: debug.h:113
r l[0]
Definition: byte_order.h:168
Definition: list.h:37
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
#define ERROR_OUTOFMEMORY
Definition: deptool.c:13
#define ERROR_SUCCESS
Definition: deptool.c:10
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
static void transform_view(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id)
Definition: state.c:3964
unsigned int idx
Definition: utils.c:41
#define ERROR_CALL_NOT_IMPLEMENTED
Definition: compat.h:102
#define ERROR_INVALID_PARAMETER
Definition: compat.h:101
#define ERROR_NO_MORE_ITEMS
Definition: compat.h:105
#define TRACE_ON(x)
Definition: compat.h:75
#define lstrcpyW
Definition: compat.h:749
#define lstrlenW
Definition: compat.h:750
void append_storage_to_db(MSIDATABASE *db, IStorage *stg)
Definition: database.c:76
int msiobj_release(MSIOBJECTHDR *info)
Definition: handle.c:241
static UINT TABLE_get_row(struct tagMSIVIEW *view, UINT row, MSIRECORD **rec)
Definition: table.c:1278
static void dump_table(const string_table *st, const USHORT *rawdata, UINT rawsize)
Definition: table.c:3066
static UINT save_table(MSIDATABASE *db, const MSITABLE *t, UINT bytes_per_strref)
Definition: table.c:858
static const MSIVIEWOPS table_ops
Definition: table.c:2166
static UINT TABLE_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec, UINT row)
Definition: table.c:1838
static UINT TABLE_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
Definition: table.c:1706
static UINT TABLE_delete_row(struct tagMSIVIEW *view, UINT row)
Definition: table.c:1739
UINT msi_create_table(MSIDATABASE *db, LPCWSTR name, column_info *col_info, MSICONDITION persistent, BOOL hold)
Definition: table.c:705
static int utf2mime(int x)
Definition: table.c:104
static UINT TransformView_get_column_info(MSIVIEW *view, UINT n, LPCWSTR *name, UINT *type, BOOL *temporary, LPCWSTR *table_name)
Definition: table.c:2729
static UINT TransformView_insert_row(MSIVIEW *view, MSIRECORD *rec, UINT row, BOOL temporary)
Definition: table.c:2555
static UINT TABLE_delete(struct tagMSIVIEW *view)
Definition: table.c:1913
static void msi_free_colinfo(MSICOLUMNINFO *colinfo, UINT count)
Definition: table.c:356
static UINT msi_table_load_transform(MSIDATABASE *db, IStorage *stg, string_table *st, TRANSFORMDATA *transform, UINT bytes_per_strref, int err_cond)
Definition: table.c:3176
static const MSICOLUMNINFO _Columns_cols[4]
Definition: table.c:74
static UINT TransformView_delete(MSIVIEW *view)
Definition: table.c:2735
static UINT TABLE_add_column(struct tagMSIVIEW *view, LPCWSTR column, INT type, BOOL hold)
Definition: table.c:2023
struct tagMSICOLUMNHASHENTRY MSICOLUMNHASHENTRY
static UINT TABLE_set_row(struct tagMSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
Definition: table.c:1388
static UINT TransformView_add_column(MSITABLEVIEW *tv, MSIRECORD *rec)
Definition: table.c:2501
static UINT TABLE_get_dimensions(struct tagMSIVIEW *view, UINT *rows, UINT *cols)
Definition: table.c:1550
static UINT int_to_table_storage(const MSITABLEVIEW *tv, UINT col, int val, UINT *ret)
Definition: table.c:1194
static int find_insert_index(MSITABLEVIEW *tv, MSIRECORD *rec)
Definition: table.c:1681
BOOL TABLE_Exists(MSIDATABASE *db, LPCWSTR name)
Definition: table.c:960
static void free_table(MSITABLE *table)
Definition: table.c:362
static UINT msi_record_encoded_stream_name(const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR *pstname)
Definition: table.c:2949
static UINT table_validate_new(MSITABLEVIEW *tv, MSIRECORD *rec, UINT *column)
Definition: table.c:1604
static UINT msi_table_assign(struct tagMSIVIEW *view, MSIRECORD *rec)
Definition: table.c:1803
static UINT TABLE_drop(struct tagMSIVIEW *view)
Definition: table.c:2118
static UINT TABLE_close(struct tagMSIVIEW *view)
Definition: table.c:1543
static UINT msi_table_find_row(MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row, UINT *column)
Definition: table.c:3150
static WCHAR * create_key_string(MSITABLEVIEW *tv, MSIRECORD *rec)
Definition: table.c:2231
static UINT TABLE_fetch_stream(struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
Definition: table.c:1132
static MSITABLE * find_cached_table(MSIDATABASE *db, LPCWSTR name)
Definition: table.c:481
static UINT msi_refresh_record(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row)
Definition: table.c:1818
static const MSIVIEWOPS transform_view_ops
Definition: table.c:2743
static UINT TABLE_set_int(MSIVIEW *view, UINT row, UINT col, int val)
Definition: table.c:1214
static UINT TransformView_fetch_stream(MSIVIEW *view, UINT row, UINT col, IStream **stm)
Definition: table.c:2308
static UINT TransformView_get_dimensions(MSIVIEW *view, UINT *rows, UINT *cols)
Definition: table.c:2724
static UINT TABLE_add_ref(struct tagMSIVIEW *view)
Definition: table.c:1927
static UINT TABLE_release(struct tagMSIVIEW *view)
Definition: table.c:1986
static UINT msi_row_matches(MSITABLEVIEW *tv, UINT row, const UINT *data, UINT *column)
Definition: table.c:3121
static void msi_update_table_columns(MSIDATABASE *db, LPCWSTR name)
Definition: table.c:933
static int mime2utf(int x)
Definition: table.c:165
struct tagMSICOLUMNINFO MSICOLUMNINFO
LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
Definition: table.c:119
static UINT TransformView_fetch_int(MSIVIEW *view, UINT row, UINT col, UINT *val)
Definition: table.c:2296
static UINT TABLE_remove_column(struct tagMSIVIEW *view, UINT number)
Definition: table.c:1935
static UINT table_set_bytes(MSITABLEVIEW *tv, UINT row, UINT col, UINT val)
Definition: table.c:1157
struct tagMSITABLEVIEW MSITABLEVIEW
UINT msi_table_apply_transform(MSIDATABASE *db, IStorage *stg, int err_cond)
Definition: table.c:3371
static UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table_ret)
Definition: table.c:573
static UINT TABLE_execute(struct tagMSIVIEW *view, MSIRECORD *record)
Definition: table.c:1532
static UINT read_table_int(BYTE *const *data, UINT row, UINT col, UINT bytes)
Definition: table.c:619
static MSIRECORD * msi_get_transform_record(const MSITABLEVIEW *tv, const string_table *st, IStorage *stg, const BYTE *rawdata, UINT bytes_per_strref)
Definition: table.c:2978
BOOL decode_streamname(LPCWSTR in, LPWSTR out)
Definition: table.c:178
static UINT msi_record_stream_name(const MSITABLEVIEW *tv, MSIRECORD *rec, LPWSTR name, DWORD *len)
Definition: table.c:2263
static UINT TransformView_drop_table(MSITABLEVIEW *tv, UINT row)
Definition: table.c:2604
static UINT TransformView_create_table(MSITABLEVIEW *tv, MSIRECORD *rec)
Definition: table.c:2466
static UINT TransformView_execute(MSIVIEW *view, MSIRECORD *record)
Definition: table.c:2714
static UINT TABLE_set_stream(MSIVIEW *view, UINT row, UINT col, IStream *stream)
Definition: table.c:1340
static UINT TABLE_set_string(MSIVIEW *view, UINT row, UINT col, const WCHAR *val, int len)
Definition: table.c:1241
static UINT table_get_column_info(MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount)
Definition: table.c:540
void enum_stream_names(IStorage *stg)
Definition: table.c:204
static int compare_record(MSITABLEVIEW *tv, UINT row, MSIRECORD *rec)
Definition: table.c:1648
MSICONDITION MSI_DatabaseIsTablePersistent(MSIDATABASE *db, LPCWSTR table)
Definition: table.c:2922
static UINT TransformView_delete_row(MSIVIEW *view, UINT row)
Definition: table.c:2648
static const MSICOLUMNINFO _Tables_cols[1]
Definition: table.c:81
static UINT read_table_from_storage(MSIDATABASE *db, MSITABLE *t, IStorage *stg)
Definition: table.c:392
static UINT TransformView_close(MSIVIEW *view)
Definition: table.c:2719
static UINT TABLE_fetch_int(struct tagMSIVIEW *view, UINT row, UINT col, UINT *val)
Definition: table.c:1005
static UINT msi_table_update(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row)
Definition: table.c:1777
static UINT bytes_per_column(MSIDATABASE *db, const MSICOLUMNINFO *col, UINT bytes_per_strref)
Definition: table.c:87
static UINT table_create_new_row(struct tagMSIVIEW *view, UINT *num, BOOL temporary)
Definition: table.c:1473
static UINT * msi_record_to_row(const MSITABLEVIEW *tv, MSIRECORD *rec)
Definition: table.c:3077
UINT read_stream_data(IStorage *stg, LPCWSTR stname, BOOL table, BYTE **pdata, UINT *psz)
Definition: table.c:232
void free_cached_tables(MSIDATABASE *db)
Definition: table.c:470
static UINT get_tablecolumns(MSIDATABASE *db, LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
Definition: table.c:629
static void table_calc_column_offsets(MSIDATABASE *db, MSICOLUMNINFO *colinfo, DWORD count)
Definition: table.c:492
static UINT add_stream(MSIDATABASE *db, const WCHAR *name, IStream *data)
Definition: table.c:1288
static UINT get_defaulttablecolumns(MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz)
Definition: table.c:509
UINT TransformView_Create(MSIDATABASE *db, string_table *st, LPCWSTR name, MSIVIEW **view)
Definition: table.c:2766
static UINT TransformView_set_row(MSIVIEW *view, UINT row, MSIRECORD *rec, UINT mask)
Definition: table.c:2320
UINT TABLE_CreateView(MSIDATABASE *db, LPCWSTR name, MSIVIEW **view)
Definition: table.c:2189
static UINT get_stream_name(const MSITABLEVIEW *tv, UINT row, WCHAR **pstname)
Definition: table.c:1042
static UINT read_raw_int(const BYTE *data, UINT col, UINT bytes)
Definition: table.c:2939
static UINT TABLE_get_column_info(struct tagMSIVIEW *view, UINT n, LPCWSTR *name, UINT *type, BOOL *temporary, LPCWSTR *table_name)
Definition: table.c:1568
UINT MSI_CommitTables(MSIDATABASE *db)
Definition: table.c:2887
UINT write_stream_data(IStorage *stg, LPCWSTR stname, LPCVOID data, UINT sz, BOOL bTable)
Definition: table.c:296
static UINT get_table_value_from_record(MSITABLEVIEW *tv, MSIRECORD *rec, UINT iField, UINT *pvalue)
Definition: table.c:1356
#define MAX_STREAM_NAME
Definition: table.c:85
static UINT msi_table_get_row_size(MSIDATABASE *db, const MSICOLUMNINFO *cols, UINT count, UINT bytes_per_strref)
Definition: table.c:374
struct png_info_def *typedef unsigned char **typedef struct png_info_def *typedef struct png_info_def *typedef struct png_info_def *typedef unsigned char ** row
Definition: typeof.h:78
#define swprintf
Definition: precomp.h:40
#define assert(x)
Definition: debug.h:53
struct nls_table * tables
Definition: nls_base.c:22
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
GLint GLint GLint GLint GLint x
Definition: gl.h:1548
GLuint GLuint GLsizei GLenum type
Definition: gl.h:1545
GLuint GLuint end
Definition: gl.h:1545
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
GLdouble GLdouble GLdouble r
Definition: gl.h:2055
GLdouble GLdouble t
Definition: gl.h:2047
GLdouble GLdouble GLdouble GLdouble q
Definition: gl.h:2063
GLsizeiptr size
Definition: glext.h:5919
GLdouble n
Definition: glext.h:7729
GLuint GLenum GLenum transform
Definition: glext.h:9407
GLsizei const GLchar *const * strings
Definition: