Commit ddbbf976 authored by Marko Mäkelä's avatar Marko Mäkelä

Merge 10.4 into 10.5

parents d1851b30 ed355f59
...@@ -2271,3 +2271,113 @@ a ...@@ -2271,3 +2271,113 @@ a
5 5
DROP TABLE t1; DROP TABLE t1;
SET GLOBAL innodb_stats_persistent= @stats.save; SET GLOBAL innodb_stats_persistent= @stats.save;
#
# MDEV-20056: index to build range filter should not be
# the same as table access index
#
SET @stats.save= @@innodb_stats_persistent;
SET GLOBAL innodb_stats_persistent= ON;
CREATE TABLE t1 (ch varchar(1), id int, id2 int) ENGINE=InnoDB;
INSERT INTO t1 VALUES
('l',3,2), ('e',NULL,NULL), ('r',7,3), ('h',NULL,2), (NULL,4,4), ('c',4,NULL),
('k',NULL,NULL), ('h',NULL,NULL), ('b',9,NULL), ('f',6,NULL);
CREATE TABLE t2 (
pk int NOT NULL, col_date_key date, ch2 varchar(1), id2 int,
PRIMARY KEY (pk), KEY (col_date_key), KEY (ch2), KEY (id2)
) ENGINE=InnoDB;
INSERT INTO t2 VALUES
(1,'2034-04-21','g',9), (2,'2006-09-08','y',1), (3,NULL,'h',2),
(4,'1987-03-02','s',2), (5,'2019-07-02','u',NULL),(6,'2012-12-18','z',1),
(7,NULL,'w',4), (8,'2005-03-10','o',8), (9,'1987-02-12','d',4);
CREATE TABLE t3 (id int) ENGINE=InnoDB;
INSERT INTO t3 VALUES (6);
ANALYZE TABLE t1,t2,t3;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
test.t2 analyze status Engine-independent statistics collected
test.t2 analyze status OK
test.t3 analyze status Engine-independent statistics collected
test.t3 analyze status OK
EXPLAIN EXTENDED SELECT 1 FROM t3
WHERE EXISTS ( SELECT 1 FROM t1
WHERE t3.id IN ( SELECT bt1.id FROM t2, t1 AS bt1
WHERE bt1.id = t2.pk AND
t2.ch2 <= 'g' ) OR
t1.id2 = t1.id);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 1 100.00 Using where
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 10 100.00 Using where
3 MATERIALIZED t2 range PRIMARY,col_date_key,ch2,id2 ch2 4 NULL 2 100.00 Using where; Using index
3 MATERIALIZED bt1 ALL NULL NULL NULL NULL 10 100.00 Using where; Using join buffer (flat, BNL join)
Warnings:
Note 1276 Field or reference 'test.t3.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where <in_optimizer>(1,<expr_cache><`test`.`t3`.`id`>(exists(/* select#2 */ select 1 from `test`.`t1` where <expr_cache><`test`.`t3`.`id`>(<in_optimizer>(`test`.`t3`.`id`,`test`.`t3`.`id` in ( <materialize> (/* select#3 */ select `test`.`bt1`.`id` from `test`.`t2` join `test`.`t1` `bt1` where `test`.`bt1`.`id` = `test`.`t2`.`pk` and `test`.`t2`.`ch2` <= 'g' ), <primary_index_lookup>(`test`.`t3`.`id` in <temporary table> on distinct_key where `test`.`t3`.`id` = `<subquery3>`.`id`)))) or `test`.`t1`.`id2` = `test`.`t1`.`id` limit 1)))
SELECT 1 FROM t3
WHERE EXISTS ( SELECT 1 FROM t1
WHERE t3.id IN ( SELECT bt1.id FROM t2, t1 AS bt1
WHERE bt1.id = t2.pk AND
t2.ch2 <= 'g' ) OR
t1.id2 = t1.id);
1
1
EXPLAIN EXTENDED SELECT 1 FROM t3
WHERE EXISTS ( SELECT 1 FROM t1
WHERE t3.id IN ( SELECT bt1.id FROM t2, t1 AS bt1
WHERE bt1.ch = t2.ch2 AND
bt1.id = t2.pk AND
t2.ch2 <= 'g' ) OR
t1.id2 = t1.id);
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t3 ALL NULL NULL NULL NULL 1 100.00 Using where
2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 10 100.00 Using where
3 MATERIALIZED t2 range PRIMARY,col_date_key,ch2,id2 ch2 4 NULL 2 100.00 Using where; Using index
3 MATERIALIZED bt1 ALL NULL NULL NULL NULL 10 100.00 Using where; Using join buffer (flat, BNL join)
Warnings:
Note 1276 Field or reference 'test.t3.id' of SELECT #2 was resolved in SELECT #1
Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where <in_optimizer>(1,<expr_cache><`test`.`t3`.`id`>(exists(/* select#2 */ select 1 from `test`.`t1` where <expr_cache><`test`.`t3`.`id`>(<in_optimizer>(`test`.`t3`.`id`,`test`.`t3`.`id` in ( <materialize> (/* select#3 */ select `test`.`bt1`.`id` from `test`.`t2` join `test`.`t1` `bt1` where `test`.`bt1`.`ch` = `test`.`t2`.`ch2` and `test`.`bt1`.`id` = `test`.`t2`.`pk` and `test`.`t2`.`ch2` <= 'g' ), <primary_index_lookup>(`test`.`t3`.`id` in <temporary table> on distinct_key where `test`.`t3`.`id` = `<subquery3>`.`id`)))) or `test`.`t1`.`id2` = `test`.`t1`.`id` limit 1)))
SELECT 1 FROM t3
WHERE EXISTS ( SELECT 1 FROM t1
WHERE t3.id IN ( SELECT bt1.id FROM t2, t1 AS bt1
WHERE bt1.ch = t2.ch2 AND
bt1.id = t2.pk AND
t2.ch2 <= 'g' ) OR
t1.id2 = t1.id);
1
1
DROP TABLE t1, t2, t3;
SET GLOBAL innodb_stats_persistent= @stats.save;
#
# MDEV-20407: usage of range filter is not supported when
# the joined table is accessed by a full text index
#
set @stats.save= @@innodb_stats_persistent;
set global innodb_stats_persistent=on;
create table t1(id int, s text, key (id), fulltext key (s)) engine=innodb;
insert into t1 values
(1119,'t'),(1134,'t'),(1134,'t'),(1143,'t'),(1143,'t'),(1187,'t'),(1187,'t'),
(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),
(1187,'t'),(1210,'t'),(1210,'t'),(1210,'t'),(1210,'t'),(1210,'t'),(1210,'t'),
(1214,'t'),(1214,'t'),(1215,'t'),(1215,'t'),(1215,'t'),(1216,'t'),(1218,'t'),
(1220,'t'),(1220,'t'),(1220,'t'),(1222,'t'),(1223,'t'),(1223,'t'),(1224,'t'),
(1225,'t'),(1225,'t'),(1226,'t'),(1226,'t'),(1227,'t'),(1227,'t'),(1228,'t'),
(1229,'t'),(1230,'t'),(1230,'t'),(1231,'t'),(1231,'t'),(1232,'t'),(1232,'t'),
(1232,'t'),(1232,'t'),(1233,'t'),(1241,'t'),(1245,'t'),(1247,'t'),(1247,'t'),
(1247,'t'),(1247,'t'),(1247,'t'),(1247,'t'),(1248,'like fttest');
analyze table t1;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze Warning Engine-independent statistics are not collected for column 's'
test.t1 analyze status OK
explain extended select count(0) from t1
where id=15066 and (match s against ('+"fttest"' in boolean mode));
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 fulltext id,s s 0 1 1.64 Using where
Warnings:
Note 1003 select count(0) AS `count(0)` from `test`.`t1` where `test`.`t1`.`id` = 15066 and (match `test`.`t1`.`s` against ('+"fttest"' in boolean mode))
select count(0) from t1
where id=15066 and (match s against ('+"fttest"' in boolean mode));
count(0)
0
drop table t1;
set global innodb_stats_persistent= @stats.save;
...@@ -129,3 +129,89 @@ SELECT a FROM t1 WHERE c < 'k' AND b > 't' ORDER BY a; ...@@ -129,3 +129,89 @@ SELECT a FROM t1 WHERE c < 'k' AND b > 't' ORDER BY a;
DROP TABLE t1; DROP TABLE t1;
SET GLOBAL innodb_stats_persistent= @stats.save; SET GLOBAL innodb_stats_persistent= @stats.save;
--echo #
--echo # MDEV-20056: index to build range filter should not be
--echo # the same as table access index
--echo #
SET @stats.save= @@innodb_stats_persistent;
SET GLOBAL innodb_stats_persistent= ON;
CREATE TABLE t1 (ch varchar(1), id int, id2 int) ENGINE=InnoDB;
INSERT INTO t1 VALUES
('l',3,2), ('e',NULL,NULL), ('r',7,3), ('h',NULL,2), (NULL,4,4), ('c',4,NULL),
('k',NULL,NULL), ('h',NULL,NULL), ('b',9,NULL), ('f',6,NULL);
CREATE TABLE t2 (
pk int NOT NULL, col_date_key date, ch2 varchar(1), id2 int,
PRIMARY KEY (pk), KEY (col_date_key), KEY (ch2), KEY (id2)
) ENGINE=InnoDB;
INSERT INTO t2 VALUES
(1,'2034-04-21','g',9), (2,'2006-09-08','y',1), (3,NULL,'h',2),
(4,'1987-03-02','s',2), (5,'2019-07-02','u',NULL),(6,'2012-12-18','z',1),
(7,NULL,'w',4), (8,'2005-03-10','o',8), (9,'1987-02-12','d',4);
CREATE TABLE t3 (id int) ENGINE=InnoDB;
INSERT INTO t3 VALUES (6);
ANALYZE TABLE t1,t2,t3;
let $q1=
SELECT 1 FROM t3
WHERE EXISTS ( SELECT 1 FROM t1
WHERE t3.id IN ( SELECT bt1.id FROM t2, t1 AS bt1
WHERE bt1.id = t2.pk AND
t2.ch2 <= 'g' ) OR
t1.id2 = t1.id);
eval EXPLAIN EXTENDED $q1;
eval $q1;
let $q2=
SELECT 1 FROM t3
WHERE EXISTS ( SELECT 1 FROM t1
WHERE t3.id IN ( SELECT bt1.id FROM t2, t1 AS bt1
WHERE bt1.ch = t2.ch2 AND
bt1.id = t2.pk AND
t2.ch2 <= 'g' ) OR
t1.id2 = t1.id);
eval EXPLAIN EXTENDED $q2;
eval $q2;
DROP TABLE t1, t2, t3;
SET GLOBAL innodb_stats_persistent= @stats.save;
--echo #
--echo # MDEV-20407: usage of range filter is not supported when
--echo # the joined table is accessed by a full text index
--echo #
set @stats.save= @@innodb_stats_persistent;
set global innodb_stats_persistent=on;
create table t1(id int, s text, key (id), fulltext key (s)) engine=innodb;
insert into t1 values
(1119,'t'),(1134,'t'),(1134,'t'),(1143,'t'),(1143,'t'),(1187,'t'),(1187,'t'),
(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),(1187,'t'),
(1187,'t'),(1210,'t'),(1210,'t'),(1210,'t'),(1210,'t'),(1210,'t'),(1210,'t'),
(1214,'t'),(1214,'t'),(1215,'t'),(1215,'t'),(1215,'t'),(1216,'t'),(1218,'t'),
(1220,'t'),(1220,'t'),(1220,'t'),(1222,'t'),(1223,'t'),(1223,'t'),(1224,'t'),
(1225,'t'),(1225,'t'),(1226,'t'),(1226,'t'),(1227,'t'),(1227,'t'),(1228,'t'),
(1229,'t'),(1230,'t'),(1230,'t'),(1231,'t'),(1231,'t'),(1232,'t'),(1232,'t'),
(1232,'t'),(1232,'t'),(1233,'t'),(1241,'t'),(1245,'t'),(1247,'t'),(1247,'t'),
(1247,'t'),(1247,'t'),(1247,'t'),(1247,'t'),(1248,'like fttest');
analyze table t1;
let $q=
select count(0) from t1
where id=15066 and (match s against ('+"fttest"' in boolean mode));
eval explain extended $q;
eval $q;
drop table t1;
set global innodb_stats_persistent= @stats.save;
...@@ -318,8 +318,8 @@ ...@@ -318,8 +318,8 @@
FROM information_schema.global_status FROM information_schema.global_status
WHERE variable_name = 'innodb_instant_alter_column'; WHERE variable_name = 'innodb_instant_alter_column';
instants instants
-187 -193
+189 +195
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency; SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
# #
# MDEV-18266: Changing an index comment unnecessarily rebuilds index # MDEV-18266: Changing an index comment unnecessarily rebuilds index
...@@ -912,6 +912,19 @@ SELECT * FROM t1; ...@@ -912,6 +912,19 @@ SELECT * FROM t1;
b a vb b a vb
NULL barf NULL NULL barf NULL
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (a INT, b TEXT, PRIMARY KEY(b(9))) ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
ALTER TABLE t1 ADD COLUMN c TEXT FIRST;
ALTER TABLE t1 ADD COLUMN d TEXT GENERATED ALWAYS AS (SUBSTR(b,1,499)) FIRST;
DROP TABLE t1;
CREATE TABLE t1(a CHAR(5), b INT, c CHAR(1), d CHAR(1), PRIMARY KEY(a,b))
DEFAULT CHARACTER SET utf8 ENGINE=InnoDB ROW_FORMAT=REDUNDANT;
INSERT INTO t1 SET a='fubar',b=42;
ALTER TABLE t1 DROP c, DROP d, ALGORITHM=INSTANT;
ALTER TABLE t1 ADD vb INT AS (b);
SELECT * FROM t1;
a b vb
fubar 42 42
DROP TABLE t1;
CREATE TABLE t1 CREATE TABLE t1
(id INT PRIMARY KEY, c2 INT UNIQUE, (id INT PRIMARY KEY, c2 INT UNIQUE,
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'), c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
...@@ -1769,6 +1782,19 @@ SELECT * FROM t1; ...@@ -1769,6 +1782,19 @@ SELECT * FROM t1;
b a vb b a vb
NULL barf NULL NULL barf NULL
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (a INT, b TEXT, PRIMARY KEY(b(9))) ENGINE=InnoDB ROW_FORMAT=COMPACT;
ALTER TABLE t1 ADD COLUMN c TEXT FIRST;
ALTER TABLE t1 ADD COLUMN d TEXT GENERATED ALWAYS AS (SUBSTR(b,1,499)) FIRST;
DROP TABLE t1;
CREATE TABLE t1(a CHAR(5), b INT, c CHAR(1), d CHAR(1), PRIMARY KEY(a,b))
DEFAULT CHARACTER SET utf8 ENGINE=InnoDB ROW_FORMAT=COMPACT;
INSERT INTO t1 SET a='fubar',b=42;
ALTER TABLE t1 DROP c, DROP d, ALGORITHM=INSTANT;
ALTER TABLE t1 ADD vb INT AS (b);
SELECT * FROM t1;
a b vb
fubar 42 42
DROP TABLE t1;
CREATE TABLE t1 CREATE TABLE t1
(id INT PRIMARY KEY, c2 INT UNIQUE, (id INT PRIMARY KEY, c2 INT UNIQUE,
c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'), c3 POINT NOT NULL DEFAULT ST_GeomFromText('POINT(3 4)'),
...@@ -2626,12 +2652,25 @@ SELECT * FROM t1; ...@@ -2626,12 +2652,25 @@ SELECT * FROM t1;
b a vb b a vb
NULL barf NULL NULL barf NULL
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t1 (a INT, b TEXT, PRIMARY KEY(b(9))) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
ALTER TABLE t1 ADD COLUMN c TEXT FIRST;
ALTER TABLE t1 ADD COLUMN d TEXT GENERATED ALWAYS AS (SUBSTR(b,1,499)) FIRST;
DROP TABLE t1;
CREATE TABLE t1(a CHAR(5), b INT, c CHAR(1), d CHAR(1), PRIMARY KEY(a,b))
DEFAULT CHARACTER SET utf8 ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
INSERT INTO t1 SET a='fubar',b=42;
ALTER TABLE t1 DROP c, DROP d, ALGORITHM=INSTANT;
ALTER TABLE t1 ADD vb INT AS (b);
SELECT * FROM t1;
a b vb
fubar 42 42
DROP TABLE t1;
disconnect analyze; disconnect analyze;
SELECT variable_value-@old_instant instants SELECT variable_value-@old_instant instants
FROM information_schema.global_status FROM information_schema.global_status
WHERE variable_name = 'innodb_instant_alter_column'; WHERE variable_name = 'innodb_instant_alter_column';
instants instants
187 193
SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency; SET GLOBAL innodb_purge_rseg_truncate_frequency= @saved_frequency;
# #
# MDEV-18266: Changing an index comment unnecessarily rebuilds index # MDEV-18266: Changing an index comment unnecessarily rebuilds index
......
...@@ -799,6 +799,21 @@ ALTER TABLE t1 ADD vb INT AS (b); ...@@ -799,6 +799,21 @@ ALTER TABLE t1 ADD vb INT AS (b);
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
# MDEV-21148 Assertion failure index->n_core_fields + n_add >= index->n_fields
eval CREATE TABLE t1 (a INT, b TEXT, PRIMARY KEY(b(9))) $engine;
ALTER TABLE t1 ADD COLUMN c TEXT FIRST;
ALTER TABLE t1 ADD COLUMN d TEXT GENERATED ALWAYS AS (SUBSTR(b,1,499)) FIRST;
DROP TABLE t1;
eval CREATE TABLE t1(a CHAR(5), b INT, c CHAR(1), d CHAR(1), PRIMARY KEY(a,b))
DEFAULT CHARACTER SET utf8 $engine;
INSERT INTO t1 SET a='fubar',b=42;
ALTER TABLE t1 DROP c, DROP d, ALGORITHM=INSTANT;
# this evicts and reloads the table definition until MDEV-17468 is fixed
ALTER TABLE t1 ADD vb INT AS (b);
SELECT * FROM t1;
DROP TABLE t1;
dec $format; dec $format;
let $redundant_4k= 0; let $redundant_4k= 0;
} }
......
...@@ -1644,6 +1644,12 @@ bool JOIN::make_range_rowid_filters() ...@@ -1644,6 +1644,12 @@ bool JOIN::make_range_rowid_filters()
{ {
if (!tab->range_rowid_filter_info) if (!tab->range_rowid_filter_info)
continue; continue;
DBUG_ASSERT(!(tab->ref.key >= 0 &&
tab->ref.key == (int) tab->range_rowid_filter_info->key_no));
DBUG_ASSERT(!(tab->ref.key == -1 && tab->quick &&
tab->quick->index == tab->range_rowid_filter_info->key_no));
int err; int err;
SQL_SELECT *sel= NULL; SQL_SELECT *sel= NULL;
Rowid_filter_container *filter_container= NULL; Rowid_filter_container *filter_container= NULL;
...@@ -7704,7 +7710,8 @@ best_access_path(JOIN *join, ...@@ -7704,7 +7710,8 @@ best_access_path(JOIN *join,
found_ref); found_ref);
} /* not ft_key */ } /* not ft_key */
if (records < DBL_MAX) if (records < DBL_MAX &&
(found_part & 1)) // start_key->key can be used for index access
{ {
double rows= record_count * records; double rows= record_count * records;
double access_cost_factor= MY_MIN(tmp / rows, 1.0); double access_cost_factor= MY_MIN(tmp / rows, 1.0);
......
...@@ -499,28 +499,32 @@ static dberr_t btr_cur_instant_init_low(dict_index_t* index, mtr_t* mtr) ...@@ -499,28 +499,32 @@ static dberr_t btr_cur_instant_init_low(dict_index_t* index, mtr_t* mtr)
we must duplicate some logic here. */ we must duplicate some logic here. */
if (trx_id_offset) { if (trx_id_offset) {
} else if (index->table->not_redundant()) { } else if (index->table->not_redundant()) {
/* PRIMARY KEY columns can never be NULL. /* The PRIMARY KEY contains variable-length columns.
We can skip the null flag bitmap. */ For the metadata record, variable-length columns are
const byte* lens = rec - (REC_N_NEW_EXTRA_BYTES + 1) always written with zero length. The DB_TRX_ID will
- index->n_core_null_bytes; start right after any fixed-length columns. */
unsigned n_add = rec_get_n_add_field(lens);
ut_ad(index->n_core_fields + n_add >= index->n_fields); /* OK, before MDEV-21088 was fixed, for
lens -= n_add; variable-length encoded PRIMARY KEY column of
type CHAR, we wrote more than zero bytes. In
order to allow affected tables to be accessed,
it would be nice to determine the actual
length of each PRIMARY KEY column. However, to
be able to do that, we should determine the
size of the null-bit bitmap in the metadata
record. And we cannot know that before reading
the metadata BLOB, whose starting point we are
trying to find here. (Although the PRIMARY KEY
columns cannot be NULL, we would have to know
where the lengths of variable-length PRIMARY KEY
columns start.)
So, unfortunately we cannot help users who
were affected by MDEV-21088 on a ROW_FORMAT=COMPACT
or ROW_FORMAT=DYNAMIC table. */
for (uint i = index->n_uniq; i--; ) { for (uint i = index->n_uniq; i--; ) {
const dict_field_t& f = index->fields[i]; trx_id_offset += index->fields[i].fixed_len;
unsigned len = f.fixed_len;
if (!len) {
len = *lens--;
if ((len & 0x80)
&& DATA_BIG_COL(f.col)) {
/* 1exxxxxxx xxxxxxxx */
len &= 0x3f;
len <<= 8;
len |= *lens--;
}
}
trx_id_offset += len;
} }
} else if (rec_get_1byte_offs_flag(rec)) { } else if (rec_get_1byte_offs_flag(rec)) {
trx_id_offset = rec_1_get_field_end_info( trx_id_offset = rec_1_get_field_end_info(
......
...@@ -21,8 +21,6 @@ IF(MSVC_VERSION LESS 1600) ...@@ -21,8 +21,6 @@ IF(MSVC_VERSION LESS 1600)
RETURN() RETURN()
ENDIF() ENDIF()
SET(MANUFACTURER "MariaDB Corporation Ab") SET(MANUFACTURER "MariaDB Corporation Ab")
SET(WIX_BIN_PATHS) SET(WIX_BIN_PATHS)
FOREACH(WIX_VER 3.9 3.10 3.11) FOREACH(WIX_VER 3.9 3.10 3.11)
......
...@@ -32,6 +32,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ ...@@ -32,6 +32,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#include <shellapi.h> #include <shellapi.h>
#include <stdlib.h> #include <stdlib.h>
#include <winservice.h> #include <winservice.h>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
using namespace std;
#define ONE_MB 1048576 #define ONE_MB 1048576
UINT ExecRemoveDataDirectory(wchar_t *dir) UINT ExecRemoveDataDirectory(wchar_t *dir)
...@@ -56,7 +64,7 @@ UINT ExecRemoveDataDirectory(wchar_t *dir) ...@@ -56,7 +64,7 @@ UINT ExecRemoveDataDirectory(wchar_t *dir)
} }
extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall) extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
...@@ -72,13 +80,13 @@ extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall) ...@@ -72,13 +80,13 @@ extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall)
er= ExecRemoveDataDirectory(dir); er= ExecRemoveDataDirectory(dir);
WcaLog(LOGMSG_STANDARD, "SHFileOperation returned %d", er); WcaLog(LOGMSG_STANDARD, "SHFileOperation returned %d", er);
LExit: LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
/* /*
Escape command line parameter fpr pass to CreateProcess(). Escape command line parameter fpr pass to CreateProcess().
We assume out has enough space to include encoded string We assume out has enough space to include encoded string
2*wcslen(in) is enough. 2*wcslen(in) is enough.
It is assumed that called will add double quotation marks before and after It is assumed that called will add double quotation marks before and after
...@@ -106,21 +114,21 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen) ...@@ -106,21 +114,21 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen)
} }
pos= 0; pos= 0;
for(int i = 0 ; ; i++) for(int i = 0 ; ; i++)
{ {
size_t n_backslashes = 0; size_t n_backslashes = 0;
wchar_t c; wchar_t c;
while (in[i] == L'\\') while (in[i] == L'\\')
{ {
i++; i++;
n_backslashes++; n_backslashes++;
} }
c= in[i]; c= in[i];
if (c == 0) if (c == 0)
{ {
/* /*
Escape all backslashes, but let the terminating double quotation mark Escape all backslashes, but let the terminating double quotation mark
that caller adds be interpreted as a metacharacter. that caller adds be interpreted as a metacharacter.
*/ */
for(size_t j= 0; j < 2*n_backslashes;j++) for(size_t j= 0; j < 2*n_backslashes;j++)
...@@ -129,7 +137,7 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen) ...@@ -129,7 +137,7 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen)
} }
break; break;
} }
else if (c == L'"') else if (c == L'"')
{ {
/* /*
Escape all backslashes and the following double quotation mark. Escape all backslashes and the following double quotation mark.
...@@ -140,7 +148,7 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen) ...@@ -140,7 +148,7 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen)
} }
out[pos++]= L'"'; out[pos++]= L'"';
} }
else else
{ {
/* Backslashes aren't special here. */ /* Backslashes aren't special here. */
for (size_t j=0; j < n_backslashes; j++) for (size_t j=0; j < n_backslashes; j++)
...@@ -151,11 +159,11 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen) ...@@ -151,11 +159,11 @@ static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen)
} }
out[pos++]= 0; out[pos++]= 0;
} }
/* /*
Check for if directory is empty during install, Check for if directory is empty during install,
sets "<PROPERTY>_NOT_EMPTY" otherise sets "<PROPERTY>_NOT_EMPTY" otherise
*/ */
extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall,
const wchar_t *PropertyName) const wchar_t *PropertyName)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
...@@ -165,14 +173,14 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, ...@@ -165,14 +173,14 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall,
WIN32_FIND_DATAW data; WIN32_FIND_DATAW data;
HANDLE h; HANDLE h;
bool empty; bool empty;
hr = WcaInitialize(hInstall, __FUNCTION__); hr = WcaInitialize(hInstall, __FUNCTION__);
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized."); WcaLog(LOGMSG_STANDARD, "Initialized.");
MsiGetPropertyW(hInstall, PropertyName, buf, &len); MsiGetPropertyW(hInstall, PropertyName, buf, &len);
wcscat_s(buf, MAX_PATH, L"*.*"); wcscat_s(buf, MAX_PATH, L"*.*");
WcaLog(LOGMSG_STANDARD, "Checking files in %S", buf); WcaLog(LOGMSG_STANDARD, "Checking files in %S", buf);
h= FindFirstFile(buf, &data); h= FindFirstFile(buf, &data);
...@@ -198,7 +206,7 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, ...@@ -198,7 +206,7 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall,
} }
if(empty) if(empty)
WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent", WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent",
PropertyName); PropertyName);
else else
WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName); WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName);
...@@ -208,7 +216,7 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall, ...@@ -208,7 +216,7 @@ extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall,
WcaSetProperty(buf, empty? L"":L"1"); WcaSetProperty(buf, empty? L"":L"1");
LExit: LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
extern "C" UINT __stdcall CheckDataDirectoryEmpty(MSIHANDLE hInstall) extern "C" UINT __stdcall CheckDataDirectoryEmpty(MSIHANDLE hInstall)
...@@ -220,12 +228,12 @@ bool CheckServiceExists(const wchar_t *name) ...@@ -220,12 +228,12 @@ bool CheckServiceExists(const wchar_t *name)
{ {
SC_HANDLE manager =0, service=0; SC_HANDLE manager =0, service=0;
manager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT); manager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT);
if (!manager) if (!manager)
{ {
return false; return false;
} }
service = OpenService(manager, name, SC_MANAGER_CONNECT); service = OpenService(manager, name, SC_MANAGER_CONNECT);
if(service) if(service)
CloseServiceHandle(service); CloseServiceHandle(service);
CloseServiceHandle(manager); CloseServiceHandle(manager);
...@@ -239,11 +247,11 @@ bool ExecRemoveService(const wchar_t *name) ...@@ -239,11 +247,11 @@ bool ExecRemoveService(const wchar_t *name)
SC_HANDLE manager =0, service=0; SC_HANDLE manager =0, service=0;
manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS); manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
bool ret; bool ret;
if (!manager) if (!manager)
{ {
return false; return false;
} }
service = OpenService(manager, name, DELETE); service = OpenService(manager, name, DELETE);
if(service) if(service)
{ {
ret= DeleteService(service); ret= DeleteService(service);
...@@ -289,7 +297,7 @@ bool IsPortFree(short port) ...@@ -289,7 +297,7 @@ bool IsPortFree(short port)
} }
/* /*
Helper function used in filename normalization. Helper function used in filename normalization.
Removes leading quote and terminates string at the position of the next one Removes leading quote and terminates string at the position of the next one
(if applicable, does not change string otherwise). Returns modified string (if applicable, does not change string otherwise). Returns modified string
...@@ -310,23 +318,23 @@ wchar_t *strip_quotes(wchar_t *s) ...@@ -310,23 +318,23 @@ wchar_t *strip_quotes(wchar_t *s)
/* /*
Checks for consistency of service configuration. Checks for consistency of service configuration.
It can happen that SERVICENAME or DATADIR It can happen that SERVICENAME or DATADIR
MSI properties are in inconsistent state after somebody upgraded database MSI properties are in inconsistent state after somebody upgraded database
We catch this case during uninstall. In particular, either service is not We catch this case during uninstall. In particular, either service is not
removed even if SERVICENAME was set (but this name is reused by someone else) removed even if SERVICENAME was set (but this name is reused by someone else)
or data directory is not removed (if it is used by someone else). To find out or data directory is not removed (if it is used by someone else). To find out
whether service name and datadirectory are in use For every service, whether service name and datadirectory are in use For every service,
configuration is read and checked as follows: configuration is read and checked as follows:
- look if a service has to do something with mysql - look if a service has to do something with mysql
- If so, check its name against SERVICENAME. if match, check binary path - If so, check its name against SERVICENAME. if match, check binary path
against INSTALLDIR\bin. If binary path does not match, then service runs against INSTALLDIR\bin. If binary path does not match, then service runs
under different installation and won't be removed. under different installation and won't be removed.
- Check options file for datadir and look if this is inside this - Check options file for datadir and look if this is inside this
installation's datadir don't remove datadir if this is the case. installation's datadir don't remove datadir if this is the case.
"Don't remove" in this context means that custom action is removing "Don't remove" in this context means that custom action is removing
SERVICENAME property or CLEANUPDATA property, which later on in course of SERVICENAME property or CLEANUPDATA property, which later on in course of
installation mean, that either datadir or service is kept. installation mean, that either datadir or service is kept.
*/ */
...@@ -367,15 +375,15 @@ void CheckServiceConfig( ...@@ -367,15 +375,15 @@ void CheckServiceConfig(
if(!is_my_service) if(!is_my_service)
{ {
WcaLog(LOGMSG_STANDARD, "service does not match current service"); WcaLog(LOGMSG_STANDARD, "service does not match current service");
/* /*
TODO probably the best thing possible would be to add temporary TODO probably the best thing possible would be to add temporary
row to MSI ServiceConfig table with remove on uninstall row to MSI ServiceConfig table with remove on uninstall
*/ */
} }
else if (!same_bindir) else if (!same_bindir)
{ {
WcaLog(LOGMSG_STANDARD, WcaLog(LOGMSG_STANDARD,
"Service name matches, but not the executable path directory, mine is %S", "Service name matches, but not the executable path directory, mine is %S",
bindir); bindir);
WcaSetProperty(L"SERVICENAME", L""); WcaSetProperty(L"SERVICENAME", L"");
} }
...@@ -392,10 +400,10 @@ void CheckServiceConfig( ...@@ -392,10 +400,10 @@ void CheckServiceConfig(
WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file); WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file);
if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir, if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir,
MAX_PATH, defaults_file) == 0) MAX_PATH, defaults_file) == 0)
{ {
WcaLog(LOGMSG_STANDARD, WcaLog(LOGMSG_STANDARD,
"Cannot find datadir in ini file '%S'", defaults_file); "Cannot find datadir in ini file '%S'", defaults_file);
goto end; goto end;
} }
...@@ -404,18 +412,18 @@ void CheckServiceConfig( ...@@ -404,18 +412,18 @@ void CheckServiceConfig(
strip_quotes(current_datadir); strip_quotes(current_datadir);
/* Convert to Windows path */ /* Convert to Windows path */
if (GetFullPathNameW(current_datadir, MAX_PATH, normalized_current_datadir, if (GetFullPathNameW(current_datadir, MAX_PATH, normalized_current_datadir,
NULL)) NULL))
{ {
/* Add backslash to be compatible with directory formats in MSI */ /* Add backslash to be compatible with directory formats in MSI */
wcsncat(normalized_current_datadir, L"\\", MAX_PATH+1); wcsncat(normalized_current_datadir, L"\\", MAX_PATH+1);
WcaLog(LOGMSG_STANDARD, "normalized current datadir is '%S'", WcaLog(LOGMSG_STANDARD, "normalized current datadir is '%S'",
normalized_current_datadir); normalized_current_datadir);
} }
if (_wcsicmp(datadir, normalized_current_datadir) == 0 && !same_bindir) if (_wcsicmp(datadir, normalized_current_datadir) == 0 && !same_bindir)
{ {
WcaLog(LOGMSG_STANDARD, WcaLog(LOGMSG_STANDARD,
"database directory from current installation, but different mysqld.exe"); "database directory from current installation, but different mysqld.exe");
WcaSetProperty(L"CLEANUPDATA", L""); WcaSetProperty(L"CLEANUPDATA", L"");
} }
...@@ -427,13 +435,13 @@ void CheckServiceConfig( ...@@ -427,13 +435,13 @@ void CheckServiceConfig(
/* /*
Checks if database directory or service are modified by user Checks if database directory or service are modified by user
For example, service may point to different mysqld.exe that it was originally For example, service may point to different mysqld.exe that it was originally
installed, or some different service might use this database directory. This installed, or some different service might use this database directory. This
would normally mean user has done an upgrade of the database and in this case would normally mean user has done an upgrade of the database and in this case
uninstall should neither delete service nor database directory. uninstall should neither delete service nor database directory.
If this function find that service is modified by user (mysqld.exe used by If this function find that service is modified by user (mysqld.exe used by
service does not point to the installation bin directory), MSI public variable service does not point to the installation bin directory), MSI public variable
SERVICENAME is removed, if DATADIR is used by some other service, variables SERVICENAME is removed, if DATADIR is used by some other service, variables
DATADIR and CLEANUPDATA are removed. DATADIR and CLEANUPDATA are removed.
The effect of variable removal is that service does not get uninstalled and The effect of variable removal is that service does not get uninstalled and
...@@ -453,11 +461,11 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) ...@@ -453,11 +461,11 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
wchar_t *datadir= NULL; wchar_t *datadir= NULL;
wchar_t *bindir=NULL; wchar_t *bindir=NULL;
SC_HANDLE scm = NULL; SC_HANDLE scm = NULL;
ULONG bufsize = sizeof(buf); ULONG bufsize = sizeof(buf);
ULONG bufneed = 0x00; ULONG bufneed = 0x00;
ULONG num_services = 0x00; ULONG num_services = 0x00;
LPENUM_SERVICE_STATUS_PROCESS info = NULL; LPENUM_SERVICE_STATUS_PROCESS info = NULL;
BOOL ok; BOOL ok;
hr = WcaInitialize(hInstall, __FUNCTION__); hr = WcaInitialize(hInstall, __FUNCTION__);
...@@ -467,48 +475,49 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) ...@@ -467,48 +475,49 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
WcaGetProperty(L"SERVICENAME", &servicename); WcaGetProperty(L"SERVICENAME", &servicename);
WcaGetProperty(L"DATADIR", &datadir); WcaGetProperty(L"DATADIR", &datadir);
WcaGetFormattedString(L"[INSTALLDIR]bin\\", &bindir); WcaGetFormattedString(L"[INSTALLDIR]bin\\", &bindir);
WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S", WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S",
servicename, datadir, bindir); servicename, datadir, bindir);
scm = OpenSCManager(NULL, NULL, scm = OpenSCManager(NULL, NULL,
SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
if (scm == NULL) if (scm == NULL)
{ {
ExitOnFailure(E_FAIL, "OpenSCManager failed"); ExitOnFailure(E_FAIL, "OpenSCManager failed");
} }
ok = EnumServicesStatusExW( scm, ok = EnumServicesStatusExW( scm,
SC_ENUM_PROCESS_INFO, SC_ENUM_PROCESS_INFO,
SERVICE_WIN32, SERVICE_WIN32,
SERVICE_STATE_ALL, SERVICE_STATE_ALL,
buf, buf,
bufsize, bufsize,
&bufneed, &bufneed,
&num_services, &num_services,
NULL, NULL,
NULL); NULL);
if(!ok) if(!ok)
{ {
WcaLog(LOGMSG_STANDARD, "last error %d", GetLastError()); WcaLog(LOGMSG_STANDARD, "last error %d", GetLastError());
ExitOnFailure(E_FAIL, "EnumServicesStatusExW failed"); ExitOnFailure(E_FAIL, "EnumServicesStatusExW failed");
} }
info = (LPENUM_SERVICE_STATUS_PROCESS)buf; info = (LPENUM_SERVICE_STATUS_PROCESS)buf;
for (ULONG i=0; i < num_services; i++) for (ULONG i=0; i < num_services; i++)
{ {
SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName, SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName,
SERVICE_QUERY_CONFIG); SERVICE_QUERY_CONFIG);
if (!service) if (!service)
continue; continue;
WcaLog(LOGMSG_VERBOSE, "Checking Service %S", info[i].lpServiceName); WcaLog(LOGMSG_VERBOSE, "Checking Service %S", info[i].lpServiceName);
QUERY_SERVICE_CONFIGW *config= QUERY_SERVICE_CONFIGW *config=
(QUERY_SERVICE_CONFIGW *)(void *)config_buffer; (QUERY_SERVICE_CONFIGW *)(void *)config_buffer;
DWORD needed; DWORD needed;
BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer), BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
&needed); &needed);
CloseServiceHandle(service); CloseServiceHandle(service);
if (ok) if (ok)
{ {
CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName, CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName,
config); config);
} }
} }
...@@ -520,18 +529,18 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall) ...@@ -520,18 +529,18 @@ extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
ReleaseStr(servicename); ReleaseStr(servicename);
ReleaseStr(datadir); ReleaseStr(datadir);
ReleaseStr(bindir); ReleaseStr(bindir);
return WcaFinalize(er); return WcaFinalize(er);
} }
/* /*
Get maximum size of the buffer process can allocate. Get maximum size of the buffer process can allocate.
this is calculated as min(RAM,virtualmemorylimit) this is calculated as min(RAM,virtualmemorylimit)
For 32bit processes, virtual address memory is 2GB (x86 OS) For 32bit processes, virtual address memory is 2GB (x86 OS)
or 4GB(x64 OS). or 4GB(x64 OS).
Fragmentation due to loaded modules, heap and stack Fragmentation due to loaded modules, heap and stack
limit maximum size of continuous memory block further, limit maximum size of continuous memory block further,
so that limit for 32 bit process is about 1200 on 32 bit OS so that limit for 32 bit process is about 1200 on 32 bit OS
or 2000 MB on 64 bit OS(found experimentally). or 2000 MB on 64 bit OS(found experimentally).
*/ */
unsigned long long GetMaxBufferSize(unsigned long long totalPhys) unsigned long long GetMaxBufferSize(unsigned long long totalPhys)
...@@ -549,9 +558,9 @@ unsigned long long GetMaxBufferSize(unsigned long long totalPhys) ...@@ -549,9 +558,9 @@ unsigned long long GetMaxBufferSize(unsigned long long totalPhys)
/* /*
Checks SERVICENAME, PORT and BUFFERSIZE parameters Checks SERVICENAME, PORT and BUFFERSIZE parameters
*/ */
extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
{ {
wchar_t ServiceName[MAX_PATH]={0}; wchar_t ServiceName[MAX_PATH]={0};
wchar_t SkipNetworking[MAX_PATH]={0}; wchar_t SkipNetworking[MAX_PATH]={0};
...@@ -574,7 +583,7 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) ...@@ -574,7 +583,7 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
ExitOnFailure(hr, "Failed to initialize"); ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized."); WcaLog(LOGMSG_STANDARD, "Initialized.");
MsiGetPropertyW (hInstall, L"SERVICENAME", ServiceName, &ServiceNameLen); MsiGetPropertyW (hInstall, L"SERVICENAME", ServiceName, &ServiceNameLen);
if(ServiceName[0]) if(ServiceName[0])
{ {
...@@ -585,10 +594,10 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) ...@@ -585,10 +594,10 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
} }
for(DWORD i=0; i< ServiceNameLen;i++) for(DWORD i=0; i< ServiceNameLen;i++)
{ {
if(ServiceName[i] == L'\\' || ServiceName[i] == L'/' if(ServiceName[i] == L'\\' || ServiceName[i] == L'/'
|| ServiceName[i]=='\'' || ServiceName[i] ==L'"') || ServiceName[i]=='\'' || ServiceName[i] ==L'"')
{ {
ErrorMsg = ErrorMsg =
L"Invalid service name. Forward slash and back slash are forbidden." L"Invalid service name. Forward slash and back slash are forbidden."
L"Single and double quotes are also not permitted."; L"Single and double quotes are also not permitted.";
goto LExit; goto LExit;
...@@ -608,10 +617,10 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) ...@@ -608,10 +617,10 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
sizeof(EscapedPassword)/sizeof(EscapedPassword[0])); sizeof(EscapedPassword)/sizeof(EscapedPassword[0]));
MsiSetPropertyW(hInstall,L"ESCAPEDPASSWORD",EscapedPassword); MsiSetPropertyW(hInstall,L"ESCAPEDPASSWORD",EscapedPassword);
MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking, MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking,
&SkipNetworkingLen); &SkipNetworkingLen);
MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen); MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen);
if(SkipNetworking[0]==0 && Port[0] != 0) if(SkipNetworking[0]==0 && Port[0] != 0)
{ {
/* Strip spaces */ /* Strip spaces */
...@@ -644,13 +653,13 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) ...@@ -644,13 +653,13 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
short port = (short)_wtoi(Port); short port = (short)_wtoi(Port);
if (!IsPortFree(port)) if (!IsPortFree(port))
{ {
ErrorMsg = ErrorMsg =
L"The TCP Port you selected is already in use. " L"The TCP Port you selected is already in use. "
L"Please choose a different port."; L"Please choose a different port.";
goto LExit; goto LExit;
} }
} }
MsiGetPropertyW (hInstall, L"STDCONFIG", QuickConfig, &QuickConfigLen); MsiGetPropertyW (hInstall, L"STDCONFIG", QuickConfig, &QuickConfigLen);
if(QuickConfig[0] !=0) if(QuickConfig[0] !=0)
{ {
...@@ -660,7 +669,7 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) ...@@ -660,7 +669,7 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
if (!GlobalMemoryStatusEx(&memstatus)) if (!GlobalMemoryStatusEx(&memstatus))
{ {
WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx", WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx",
GetLastError()); GetLastError());
er= ERROR_INSTALL_FAILURE; er= ERROR_INSTALL_FAILURE;
goto LExit; goto LExit;
...@@ -714,7 +723,7 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall) ...@@ -714,7 +723,7 @@ extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
return WcaFinalize(er); return WcaFinalize(er);
} }
/* /*
Sets Innodb buffer pool size (1/8 of RAM by default), if not already specified Sets Innodb buffer pool size (1/8 of RAM by default), if not already specified
via command line. via command line.
Calculates innodb log file size as min(100, innodb buffer pool size/4) Calculates innodb log file size as min(100, innodb buffer pool size/4)
...@@ -745,7 +754,7 @@ extern "C" UINT __stdcall PresetDatabaseProperties(MSIHANDLE hInstall) ...@@ -745,7 +754,7 @@ extern "C" UINT __stdcall PresetDatabaseProperties(MSIHANDLE hInstall)
memstatus.dwLength = sizeof(memstatus); memstatus.dwLength = sizeof(memstatus);
if (!GlobalMemoryStatusEx(&memstatus)) if (!GlobalMemoryStatusEx(&memstatus))
{ {
WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx", WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx",
GetLastError()); GetLastError());
er= ERROR_INSTALL_FAILURE; er= ERROR_INSTALL_FAILURE;
goto LExit; goto LExit;
...@@ -754,14 +763,14 @@ extern "C" UINT __stdcall PresetDatabaseProperties(MSIHANDLE hInstall) ...@@ -754,14 +763,14 @@ extern "C" UINT __stdcall PresetDatabaseProperties(MSIHANDLE hInstall)
/* Give innodb 12.5% of available physical memory. */ /* Give innodb 12.5% of available physical memory. */
InnodbBufferPoolSize= totalPhys/ONE_MB/8; InnodbBufferPoolSize= totalPhys/ONE_MB/8;
#ifdef _M_IX86 #ifdef _M_IX86
/* /*
For 32 bit processes, take virtual address space limitation into account. For 32 bit processes, take virtual address space limitation into account.
Do not try to use more than 3/4 of virtual address space, even if there Do not try to use more than 3/4 of virtual address space, even if there
is plenty of physical memory. is plenty of physical memory.
*/ */
InnodbBufferPoolSize= min(GetMaxBufferSize(totalPhys)/ONE_MB*3/4, InnodbBufferPoolSize= min(GetMaxBufferSize(totalPhys)/ONE_MB*3/4,
InnodbBufferPoolSize); InnodbBufferPoolSize);
#endif #endif
swprintf_s(buff, L"%llu",InnodbBufferPoolSize); swprintf_s(buff, L"%llu",InnodbBufferPoolSize);
MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff); MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff);
} }
...@@ -816,7 +825,7 @@ static void DumpErrorLog(const wchar_t *dir) ...@@ -816,7 +825,7 @@ static void DumpErrorLog(const wchar_t *dir)
} }
/* Remove service and data directory created by CreateDatabase operation */ /* Remove service and data directory created by CreateDatabase operation */
extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall) extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
...@@ -857,17 +866,17 @@ extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall) ...@@ -857,17 +866,17 @@ extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall)
ExecRemoveDataDirectory(dir); ExecRemoveDataDirectory(dir);
} }
LExit: LExit:
return WcaFinalize(er); return WcaFinalize(er);
} }
/* /*
Enables/disables optional "Launch upgrade wizard" checkbox at the end of Enables/disables optional "Launch upgrade wizard" checkbox at the end of
installation installation
*/ */
#define MAX_VERSION_PROPERTY_SIZE 64 #define MAX_VERSION_PROPERTY_SIZE 64
extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS; UINT er = ERROR_SUCCESS;
...@@ -884,7 +893,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -884,7 +893,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
hr = WcaInitialize(hInstall, __FUNCTION__); hr = WcaInitialize(hInstall, __FUNCTION__);
WcaLog(LOGMSG_STANDARD, "Initialized."); WcaLog(LOGMSG_STANDARD, "Initialized.");
if (MsiGetPropertyW(hInstall, L"ProductVersion", installerVersion, &size) if (MsiGetPropertyW(hInstall, L"ProductVersion", installerVersion, &size)
!= ERROR_SUCCESS) != ERROR_SUCCESS)
{ {
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
...@@ -903,12 +912,12 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -903,12 +912,12 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
ExitOnFailure(hr, "MsiGetPropertyW failed"); ExitOnFailure(hr, "MsiGetPropertyW failed");
} }
scm = OpenSCManager(NULL, NULL,
SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT); scm = OpenSCManager(NULL, NULL,
if (scm == NULL) SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
{ if (scm == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
ExitOnFailure(hr,"OpenSCManager failed"); ExitOnFailure(hr,"OpenSCManager failed");
} }
...@@ -921,7 +930,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -921,7 +930,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
DWORD num_services; DWORD num_services;
ok= EnumServicesStatusExW(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, ok= EnumServicesStatusExW(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL); SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL);
if(!ok) if(!ok)
{ {
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
ExitOnFailure(hr,"EnumServicesStatusEx failed"); ExitOnFailure(hr,"EnumServicesStatusEx failed");
...@@ -931,11 +940,11 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -931,11 +940,11 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
index=-1; index=-1;
for (ULONG i=0; i < num_services; i++) for (ULONG i=0; i < num_services; i++)
{ {
SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName, SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName,
SERVICE_QUERY_CONFIG); SERVICE_QUERY_CONFIG);
if (!service) if (!service)
continue; continue;
QUERY_SERVICE_CONFIGW *config= QUERY_SERVICE_CONFIGW *config=
(QUERY_SERVICE_CONFIGW*)(void *)config_buffer; (QUERY_SERVICE_CONFIGW*)(void *)config_buffer;
DWORD needed; DWORD needed;
ok= QueryServiceConfigW(service, config,sizeof(config_buffer), ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
...@@ -946,7 +955,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -946,7 +955,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
mysqld_service_properties props; mysqld_service_properties props;
if (get_mysql_service_properties(config->lpBinaryPathName, &props)) if (get_mysql_service_properties(config->lpBinaryPathName, &props))
continue; continue;
/* /*
Only look for services that have mysqld.exe outside of the current Only look for services that have mysqld.exe outside of the current
installation directory. installation directory.
*/ */
...@@ -955,7 +964,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -955,7 +964,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d", WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d",
info[i].lpServiceName, props.version_major, props.version_minor); info[i].lpServiceName, props.version_major, props.version_minor);
if(props.version_major < installerMajorVersion if(props.version_major < installerMajorVersion
|| (props.version_major == installerMajorVersion && || (props.version_major == installerMajorVersion &&
props.version_minor <= installerMinorVersion)) props.version_minor <= installerMinorVersion))
{ {
upgradableServiceFound= true; upgradableServiceFound= true;
...@@ -979,7 +988,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall) ...@@ -979,7 +988,7 @@ extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
LExit: LExit:
if(scm) if(scm)
CloseServiceHandle(scm); CloseServiceHandle(scm);
return WcaFinalize(er); return WcaFinalize(er);
} }
...@@ -1003,3 +1012,305 @@ extern "C" BOOL WINAPI DllMain( ...@@ -1003,3 +1012,305 @@ extern "C" BOOL WINAPI DllMain(
return TRUE; return TRUE;
} }
// check if file exists
inline bool checkIfFileExists (wstring& name) {
string sLog = "checkIfFileExists, ";
string sName(name.begin(), name.end() );
ifstream f(sName);
sLog.append(sName);
sLog.append(", result: ");
bool fileExists = f.good();
if (f.good())
sLog.append("true");
else
sLog.append("false");
WcaLog(LOGMSG_STANDARD, sLog.c_str());
return f.good();
}
// string to wstring
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
/* MDEV-19781 MariaDB symlinks on Windows */
extern "C" UINT __stdcall CreateSymlinks(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
wchar_t customActionData[10000];
wchar_t installDir[MAX_PATH];
DWORD len = 10000;
wchar_t installerVersion[MAX_VERSION_PROPERTY_SIZE];
DWORD size = MAX_VERSION_PROPERTY_SIZE;
wchar_t binDir[MAX_PATH];
size_t blen = NULL;
hr = WcaInitialize(hInstall, __FUNCTION__);
WcaLog(LOGMSG_STANDARD, "Initialized.");
if (MsiGetPropertyW(hInstall, L"CustomActionData", customActionData, &len) != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(GetLastError());
MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hr);
}
if (MsiGetPropertyW(hInstall, L"ProductVersion", installerVersion, &size) != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(GetLastError());
MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hr);
}
MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hr);
wstring wsCustomActionData(customActionData);
string sCustomActionData(wsCustomActionData.begin(), wsCustomActionData.end());
WcaLog(LOGMSG_STANDARD, sCustomActionData.c_str());
stringstream ss;
ss << sCustomActionData;
vector<string> vCustomActionData;
while( ss.good() )
{
string substr;
getline( ss, substr, '|' );
WcaLog(LOGMSG_STANDARD, substr.c_str());
vCustomActionData.push_back( substr );
}
int i = 0;
string sBinPath = "";
string sPathFrom = "";
string sPathTo = "";
for(auto const& value: vCustomActionData) {
if (i == 0) {
sBinPath = value;
sBinPath.append("bin\\");
} else if (i == 1) {
sPathFrom = value;
} else if (i == 2) {
sPathTo = value;
}
WcaLog(LOGMSG_STANDARD, value.c_str());
i++;
}
WcaLog(LOGMSG_STANDARD, sBinPath.c_str());
WcaLog(LOGMSG_STANDARD, sPathFrom.c_str());
WcaLog(LOGMSG_STANDARD, sPathTo.c_str());
stringstream ssPathFrom, ssPathTo;
ssPathFrom << sPathFrom;
ssPathTo << sPathTo;
vector<string> vPathFrom;
vector<string> vPathTo;
while(ssPathFrom.good()) {
string substr = "";
getline(ssPathFrom, substr, ';');
substr = substr.insert(0, sBinPath);
WcaLog(LOGMSG_STANDARD, substr.c_str());
vPathFrom.push_back(substr);
}
while(ssPathTo.good()) {
string substr = "";
getline(ssPathTo, substr, ';');
substr = substr.insert(0, sBinPath);
WcaLog(LOGMSG_STANDARD, substr.c_str());
vPathTo.push_back(substr);
}
i = 0;
for(auto const& value: vPathTo) {
string &sTmpPathFrom = vPathFrom[i];
string &sTmpPathTo = vPathTo[i];
sTmpPathFrom.append(".exe");
sTmpPathTo.append(".exe");
wstring wsPathFrom = s2ws(sTmpPathFrom);
LPCWSTR lPathFrom = wsPathFrom.c_str();
wstring wsPathTo = s2ws(sTmpPathTo);
LPCWSTR lPathTo = wsPathTo.c_str();
int createdSymlink = -1;
if (checkIfFileExists(wsPathFrom))
createdSymlink = CreateSymbolicLinkW(wsPathTo.c_str(), wsPathFrom.c_str(), 0);
string created = "Created symlink: ";
created.append(sTmpPathTo);
created.append(" --> ");
created.append(sTmpPathFrom);
created.append(", result= ");
created.append(to_string(createdSymlink));
WcaLog(LOGMSG_STANDARD, created.c_str());
i++;
}
ReleaseStr(installDir);
ReleaseStr(installerVersion);
ReleaseStr(binDir);
return WcaFinalize(er);
}
/* MDEV-19781 MariaDB symlinks on Windows */
extern "C" UINT __stdcall DeleteSymlinks(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
wchar_t customActionData[10000];
wchar_t installDir[MAX_PATH];
DWORD len = 10000;
DWORD size = MAX_VERSION_PROPERTY_SIZE;
wchar_t binDir[MAX_PATH];
size_t blen = NULL;
hr = WcaInitialize(hInstall, __FUNCTION__);
WcaLog(LOGMSG_STANDARD, "DeleteSymlinks initialized.");
if (MsiGetPropertyW(hInstall, L"CustomActionData", customActionData, &len) != ERROR_SUCCESS)
{
hr = HRESULT_FROM_WIN32(GetLastError());
MsiProcessMessage(hInstall, INSTALLMESSAGE_INFO, hr);
}
wstring wsCustomActionData(customActionData);
string sCustomActionData(wsCustomActionData.begin(), wsCustomActionData.end());
WcaLog(LOGMSG_STANDARD, sCustomActionData.c_str());
stringstream ss;
ss << sCustomActionData;
vector<string> vCustomActionData;
while( ss.good() )
{
string substr;
getline( ss, substr, '|' );
WcaLog(LOGMSG_STANDARD, substr.c_str());
vCustomActionData.push_back( substr );
}
int i = 0;
string sBinPath = "";
string sPathFrom = "";
string sPathTo = "";
for(auto const& value: vCustomActionData) {
if (i == 0) {
sBinPath = value;
sBinPath.append("bin\\");
} else if (i == 1) {
sPathFrom = value;
} else if (i == 2) {
sPathTo = value;
}
WcaLog(LOGMSG_STANDARD, value.c_str());
i++;
}
WcaLog(LOGMSG_STANDARD, sBinPath.c_str());
WcaLog(LOGMSG_STANDARD, sPathFrom.c_str());
WcaLog(LOGMSG_STANDARD, sPathTo.c_str());
stringstream ssPathFrom, ssPathTo;
ssPathFrom << sPathFrom;
ssPathTo << sPathTo;
vector<string> vPathFrom;
vector<string> vPathTo;
while(ssPathFrom.good()) {
string substr = "";
getline(ssPathFrom, substr, ';');
substr = substr.insert(0, sBinPath);
WcaLog(LOGMSG_STANDARD, substr.c_str());
vPathFrom.push_back(substr);
}
while(ssPathTo.good()) {
string substr = "";
getline(ssPathTo, substr, ';');
substr = substr.insert(0, sBinPath);
WcaLog(LOGMSG_STANDARD, substr.c_str());
vPathTo.push_back(substr);
}
i = 0;
for(auto const& value: vPathTo) {
string &sTmpPathFrom = vPathFrom[i];
string &sTmpPathTo = vPathTo[i];
sTmpPathFrom.append(".exe");
sTmpPathTo.append(".exe");
wstring wsPathFrom = s2ws(sTmpPathFrom);
LPCWSTR lPathFrom = wsPathFrom.c_str();
wstring wsPathTo = s2ws(sTmpPathTo);
LPCWSTR lPathTo = wsPathTo.c_str();
int deletedSymlink = -1;
if (checkIfFileExists(wsPathTo))
deletedSymlink = DeleteFileW(wsPathTo.c_str());
string deleted = "Deleted symlink: ";
deleted.append(sTmpPathTo);
deleted.append(" --> ");
deleted.append(sTmpPathFrom);
deleted.append(", result= ");
deleted.append(to_string(deletedSymlink));
WcaLog(LOGMSG_STANDARD, deleted.c_str());
i++;
}
/*
for(size_t i = 0; i < std::size(symlink_to); i++) {
wchar_t pathTo[MAX_PATH];
wcscpy(pathTo, binDir);
wcscat(pathTo, symlink_to[i].data());
wstring wsPathTo(pathTo);
if ( checkIfFileExists(wsPathTo) ) {
DeleteFileW(pathTo);
} else {
hr = HRESULT_FROM_WIN32(GetLastError());
ExitOnFailure(hr, "Could not delete symlink");
}
}
*/
ReleaseStr(installDir);
ReleaseStr(binDir);
return WcaFinalize(er);
}
\ No newline at end of file
...@@ -8,3 +8,5 @@ CheckDatabaseProperties ...@@ -8,3 +8,5 @@ CheckDatabaseProperties
CheckDataDirectoryEmpty CheckDataDirectoryEmpty
CheckDBInUse CheckDBInUse
CheckServiceUpgrades CheckServiceUpgrades
CreateSymlinks
DeleteSymlinks
# get the symlink lists
#INCLUDE(${CMAKE_SOURCE_DIR}/../../cmake/symlinks.cmake)
#INCLUDE(symlinks)
INCLUDE(${CMAKE_CURRENT_LIST_DIR}/../../cmake/symlinks.cmake)
MACRO(MAKE_WIX_IDENTIFIER str varname) MACRO(MAKE_WIX_IDENTIFIER str varname)
STRING(REPLACE "/" "." ${varname} "${str}") STRING(REPLACE "/" "." ${varname} "${str}")
STRING(REGEX REPLACE "[^a-zA-Z_0-9.]" "_" ${varname} "${${varname}}") STRING(REGEX REPLACE "[^a-zA-Z_0-9.]" "_" ${varname} "${${varname}}")
......
...@@ -59,6 +59,9 @@ ...@@ -59,6 +59,9 @@
<Property Id="BUFFERPOOLSIZE" Secure="yes"/> <Property Id="BUFFERPOOLSIZE" Secure="yes"/>
<!-- Innodb page size --> <!-- Innodb page size -->
<Property Id="PAGESIZE" Secure="yes" Value="16K"/> <Property Id="PAGESIZE" Secure="yes" Value="16K"/>
<!-- Symlinks -->
<Property Id="SYMLINK_TOS" Value="@MARIADB_SYMLINK_TOS@" />
<Property Id="SYMLINK_FROMS" Value="@MARIADB_SYMLINK_FROMS@" />
<CustomAction Id="LaunchUrl" BinaryKey="WixCA" DllEntry="WixShellExec" Execute="immediate" Return="check" Impersonate="yes" /> <CustomAction Id="LaunchUrl" BinaryKey="WixCA" DllEntry="WixShellExec" Execute="immediate" Return="check" Impersonate="yes" />
...@@ -597,10 +600,23 @@ ...@@ -597,10 +600,23 @@
<?endif ?> <?endif ?>
<!-- Custom action, call mysql_install_db --> <!-- Custom action, create symlinks -->
<SetProperty Sequence='execute' Before='CreateDatabaseCommand' Id="SKIPNETWORKING" Value="--skip-networking" >SKIPNETWORKING</SetProperty> <CustomAction Id="CreateSymlinks.SetProperty" Return="check" Property="CreateSymlinks" Value="[INSTALLDIR]|[SYMLINK_FROMS]|[SYMLINK_TOS]" />
<SetProperty Sequence='execute' Before='CreateDatabaseCommand' Id="ALLOWREMOTEROOTACCESS" Value="--allow-remote-root-access">ALLOWREMOTEROOTACCESS</SetProperty> <CustomAction Id="CreateSymlinks" BinaryKey="wixca.dll" DllEntry="CreateSymlinks" Execute="deferred" Impersonate="no" Return="ignore" />
<SetProperty Sequence='execute' Before='CreateDatabaseCommand' Id="DEFAULTUSER" Value="--default-user">DEFAULTUSER</SetProperty> <CustomAction Id="DeleteSymlinks.SetProperty" Return="check" Property="DeleteSymlinks" Value="[INSTALLDIR]|[SYMLINK_FROMS]|[SYMLINK_TOS]" />
<CustomAction Id='DeleteSymlinks' BinaryKey="wixca.dll" DllEntry="DeleteSymlinks" Execute="deferred" Impersonate="no" Return="ignore" />
<InstallExecuteSequence>
<Custom Action="CreateSymlinks.SetProperty" Before="CreateSymlinks">NOT Installed</Custom>
<Custom Action="CreateSymlinks" Before="InstallFinalize">NOT Installed</Custom>
<Custom Action="DeleteSymlinks.SetProperty" Before="DeleteSymlinks">Installed AND NOT REINSTALL</Custom>
<Custom Action="DeleteSymlinks" Before="RemoveFiles">Installed AND NOT REINSTALL</Custom>
</InstallExecuteSequence>
<!-- Custom action, call mysql_install_db -->
<SetProperty Sequence='execute' Before='CreateDatabaseCommand' Id="SKIPNETWORKING" Value="--skip-networking" >SKIPNETWORKING</SetProperty>
<SetProperty Sequence='execute' Before='CreateDatabaseCommand' Id="ALLOWREMOTEROOTACCESS" Value="--allow-remote-root-access">ALLOWREMOTEROOTACCESS</SetProperty>
<SetProperty Sequence='execute' Before='CreateDatabaseCommand' Id="DEFAULTUSER" Value="--default-user">DEFAULTUSER</SetProperty>
<CustomAction Id='CheckDatabaseProperties' BinaryKey='wixca.dll' DllEntry='CheckDatabaseProperties' /> <CustomAction Id='CheckDatabaseProperties' BinaryKey='wixca.dll' DllEntry='CheckDatabaseProperties' />
<CustomAction Id='PresetDatabaseProperties' BinaryKey='wixca.dll' DllEntry='PresetDatabaseProperties' /> <CustomAction Id='PresetDatabaseProperties' BinaryKey='wixca.dll' DllEntry='PresetDatabaseProperties' />
<CustomAction Id="CreateDatabaseCommand" Property="CreateDatabase" <CustomAction Id="CreateDatabaseCommand" Property="CreateDatabase"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment