= $target_version) { # already up to date echo "up to date"; return true; } echo "

Updating database:

old version: $current_version; target version: $target_version"; for ($i = $current_version +1; $i <= $target_version; $i++) { $function = "upgrade_$i"; $function_mysql = $function . "_mysql"; $function_pgsql = $function . "_pgsql"; if (function_exists($function)) { echo "

updating to version $i (all databases)..."; $function(); echo "   done"; } if ($CONF['database_type'] == 'mysql' || $CONF['database_type'] == 'mysqli' ) { if (function_exists($function_mysql)) { echo "

updating to version $i (MySQL)..."; $function_mysql(); echo "   done"; } } elseif($CONF['database_type'] == 'pgsql') { if (function_exists($function_pgsql)) { echo "

updating to version $i (PgSQL)..."; $function_pgsql(); echo "   done"; } } # TODO: update version in config table after each change # TODO: this avoids problems in case the script hits the max_execution_time, # TODO: simply rerunning it will continue where it was stopped }; } /** * Replaces database specific parts in a query * @param String sql query with placeholders * @param String (optional) MySQL specific code to attach, useful for COMMENT= on CREATE TABLE * @return String sql query */ function db_query_parsed($sql, $ignore_errors = 0, $attach_mysql = "") { global $CONF; if ($CONF['database_type'] == 'mysql' || $CONF['database_type'] == 'mysqli' ) { $replace = array( '{AUTOINCREMENT}' => 'int(11) not null auto_increment', '{PRIMARY}' => 'primary key', '{UNSIGNED}' => 'unsigned' , '{FULLTEXT}' => 'FULLTEXT', '{BOOLEAN}' => 'tinyint(1) NOT NULL', '{UTF-8}' => '/*!40100 CHARACTER SET utf8 COLLATE utf8_unicode_ci */', '{LATIN1}' => '/*!40100 CHARACTER SET latin1 COLLATE latin1_swedish_ci */', '{IF_NOT_EXISTS}' => 'IF NOT EXISTS', '{RENAME_COLUMN}' => 'CHANGE COLUMN', ); $sql = "$sql $attach_mysql"; } elseif($CONF['database_type'] == 'pgsql') { static $replace = array( '{AUTOINCREMENT}' => 'SERIAL', '{PRIMARY}' => 'primary key', '{UNSIGNED}' => '', '{FULLTEXT}' => '', '{BOOLEAN}' => 'BOOLEAN NOT NULL', '{UTF-8}' => '', # TODO: UTF-8 is simply ignored. '{LATIN1}' => '', # TODO: same for latin1 '{IF_NOT_EXISTS}' => 'IF NOT EXISTS', # TODO: does this work with PgSQL? '{RENAME_COLUMN}' => 'CHANGE COLUMN', # TODO: probably wrong 'int(1)' => 'int2', 'int(10)' => 'int4', 'int(11)' => 'int4', 'int(4)' => 'int4', ); } else { echo "Sorry, unsupported database type " . $conf['database_type']; exit; } $replace['{BOOL_TRUE}'] = db_get_boolean(True); $replace['{BOOL_FALSE}'] = db_get_boolean(False); $query = trim(str_replace(array_keys($replace), $replace, $sql)); $result = db_query($query, $ignore_errors); if (safeget('debug') != "") { print "

$query"; print "

" . $result['error'] . "
"; } return $result; } function _drop_index ($table, $index) { global $CONF; $tabe = table_by_key ($table); if ($CONF['database_type'] == 'mysql' || $CONF['database_type'] == 'mysqli' ) { return "ALTER TABLE $table DROP INDEX $index"; } elseif($CONF['database_type'] == 'pgsql') { return "DROP INDEX $index"; # TODO: on which table?! } else { echo "Sorry, unsupported database type " . $conf['database_type']; exit; } } function upgrade_1() { # inserting the version number is a good start ;-) db_insert( 'config', array( 'name' => 'version', 'value' => '1', ) ); echo "upgrade_1"; } function upgrade_2() { # upgrade pre-2.1 database # from TABLE_BACKUP_MX.TXT $table_domain = table_by_key ('domain'); $result = db_query_parsed("ALTER TABLE $table_domain ADD COLUMN transport VARCHAR(255) AFTER maxquota;", TRUE); $result = db_query_parsed("ALTER TABLE $table_domain ADD COLUMN backupmx {BOOLEAN} DEFAULT {BOOL_FALSE} AFTER transport;", TRUE); } function upgrade_3() { # upgrade pre-2.1 database # from TABLE_CHANGES.TXT $table_admin = table_by_key ('admin'); $table_alias = table_by_key ('alias'); $table_domain = table_by_key ('domain'); $table_mailbox = table_by_key ('mailbox'); $table_vacation = table_by_key ('vacation'); $all_sql = split("\n", trim(" ALTER TABLE $table_admin {RENAME_COLUMN} create_date created DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_admin {RENAME_COLUMN} change_date modified DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_alias {RENAME_COLUMN} create_date created DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_alias {RENAME_COLUMN} change_date modified DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_domain {RENAME_COLUMN} create_date created DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_domain {RENAME_COLUMN} change_date modified DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_domain ADD COLUMN aliases INT(10) DEFAULT '-1' NOT NULL AFTER description; ALTER TABLE $table_domain ADD COLUMN mailboxes INT(10) DEFAULT '-1' NOT NULL AFTER aliases; ALTER TABLE $table_domain ADD COLUMN maxquota INT(10) DEFAULT '-1' NOT NULL AFTER mailboxes; ALTER TABLE $table_domain ADD COLUMN transport VARCHAR(255) AFTER maxquota; ALTER TABLE $table_domain ADD COLUMN backupmx TINYINT(1) DEFAULT '0' NOT NULL AFTER transport; ALTER TABLE $table_mailbox {RENAME_COLUMN} create_date created DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_mailbox {RENAME_COLUMN} change_date modified DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL; ALTER TABLE $table_mailbox ADD COLUMN quota INT(10) DEFAULT '-1' NOT NULL AFTER maildir; ALTER TABLE $table_vacation ADD COLUMN domain VARCHAR(255) DEFAULT '' NOT NULL AFTER cache; ALTER TABLE $table_vacation ADD COLUMN created DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL AFTER domain; ALTER TABLE $table_vacation ADD COLUMN active TINYINT(1) DEFAULT '1' NOT NULL AFTER created; ALTER TABLE $table_vacation DROP PRIMARY KEY ALTER TABLE $table_vacation ADD PRIMARY KEY(email) UPDATE $table_vacation SET domain=SUBSTRING_INDEX(email, '@', -1) WHERE email=email; ")); foreach ($all_sql as $sql) { $result = db_query_parsed($sql, TRUE); } } function upgrade_4_mysql() { # MySQL only # changes between 2.1 and moving to sourceforge $table_domain = table_by_key ('domain'); $result = db_query_parsed("ALTER TABLE $table_domain ADD COLUMN quota int(10) NOT NULL default '0' AFTER maxquota", TRUE); } function upgrade_4_pgsql() { # PgSQL only # changes between 2.1 and moving to sourceforge /* TODO Changes in DATABASE_PGSQL.TXT: (in diff format - "-" means removed, "+" means added) TABLE domain - domain character varying(255) NOT NULL default '', + domain character varying(255) NOT NULL, + quota integer NOT NULL default 0, +CREATE INDEX domain_domain_active ON domain(domain,active); TABLE "admin" - "username" character varying(255) NOT NULL default '', + "username" character varying(255) NOT NULL, TABLE alias - address character varying(255) NOT NULL default '', + address character varying(255) NOT NULL, - domain character varying(255) NOT NULL default '', + domain character varying(255) NOT NULL REFERENCES domain, +CREATE INDEX alias_address_active ON alias(address,active); TABLE domain_admins - username character varying(255) NOT NULL default '', + username character varying(255) NOT NULL, - domain character varying(255) NOT NULL default '', + domain character varying(255) NOT NULL REFERENCES domain, TABLE log - data character varying(255) NOT NULL default '' + data text NOT NULL default '' TABLE mailbox - username character varying(255) NOT NULL default '', + username character varying(255) NOT NULL, - domain character varying(255) NOT NULL default '', + domain character varying(255) NOT NULL REFERENCES domain, +CREATE INDEX mailbox_username_active ON mailbox(username,active); TABLE vacation - email character varying(255) NOT NULL default '', + email character varying(255) PRIMARY KEY, - body text NOT NULL, + body text NOT NULL DEFAULT '', - cache text NOT NULL, + cache text NOT NULL DEFAULT '', - domain character varying(255) NOT NULL default '', + "domain" character varying(255) NOT NULL REFERENCES "domain", - active boolean NOT NULL default true, + active boolean DEFAULT true NOT NULL - Constraint "vacation_key" Primary Key ("email") -COMMENT ON TABLE vacation IS 'Postfix Admin - Virtual Vacation'; +CREATE INDEX vacation_email_active ON vacation(email,active); +CREATE TABLE vacation_notification ( + on_vacation character varying(255) NOT NULL REFERENCES vacation(email) ON DELETE CASCADE, + notified character varying(255) NOT NULL, + notified_at timestamp with time zone NOT NULL DEFAULT now(), + CONSTRAINT vacation_notification_pkey primary key(on_vacation,notified) +); */ } function upgrade_79_mysql() { # MySQL only # drop useless indicies (already available as primary key) $result = db_query_parsed(_drop_index('admin', 'username'), True); $result = db_query_parsed(_drop_index('alias', 'address'), True); $result = db_query_parsed(_drop_index('domain', 'domain'), True); $result = db_query_parsed(_drop_index('mailbox', 'username'), True); } function upgrade_81_mysql() { # MySQL only $table_vacation = table_by_key ('vacation'); $table_vacation_notification = table_by_key('vacation_notification'); $all_sql = split("\n", trim(" ALTER TABLE `$table_vacation` CHANGE `email` `email` VARCHAR( 255 ) {LATIN1} NOT NULL ALTER TABLE `$table_vacation` CHANGE `subject` `subject` VARCHAR( 255 ) {UTF-8} NOT NULL ALTER TABLE `$table_vacation` CHANGE `body` `body` TEXT {UTF-8} NOT NULL ALTER TABLE `$table_vacation` CHANGE `cache` `cache` TEXT {LATIN1} NOT NULL ALTER TABLE `$table_vacation` CHANGE `domain` `domain` VARCHAR( 255 ) {LATIN1} NOT NULL ALTER TABLE `$table_vacation` CHANGE `active` `active` TINYINT( 1 ) NOT NULL DEFAULT '1' ALTER TABLE `$table_vacation` DEFAULT {LATIN1} ALTER TABLE `$table_vacation` ENGINE = INNODB ")); foreach ($all_sql as $sql) { $result = db_query_parsed($sql, TRUE); } db_query_parsed( " CREATE TABLE {IF_NOT_EXISTS} $table_vacation_notification ( on_vacation varchar(255) NOT NULL, notified varchar(255) NOT NULL, notified_at timestamp NOT NULL default now(), CONSTRAINT vacation_notification_pkey PRIMARY KEY(on_vacation, notified), FOREIGN KEY (on_vacation) REFERENCES vacation(email) ON DELETE CASCADE ) ", TRUE, " ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci TYPE=InnoDB COMMENT='Postfix Admin - Virtual Vacation Notifications' " ); } function upgrade_90() { # translatable logging # old format: "create alias" # new format: "create_alias" $result = db_query_parsed("UPDATE " . table_by_key ('log') . " SET action = REPLACE(action,' ','_')", TRUE); # change edit_alias_state to edit_alias_active $result = db_query_parsed("UPDATE " . table_by_key ('log') . " SET action = 'edit_alias_state' WHERE action = 'edit_alias_active'", TRUE); } function upgrade_169_mysql() { # MySQL only # allow quota > 2 GB $table_domain = table_by_key ('domain'); $table_mailbox = table_by_key ('mailbox'); $result = db_query_parsed("ALTER TABLE $table_domain MODIFY COLUMN `quota` bigint(20) NOT NULL default '0'", TRUE); $result = db_query_parsed("ALTER TABLE $table_domain MODIFY COLUMN `maxquota` bigint(20) NOT NULL default '0'", TRUE); $result = db_query_parsed("ALTER TABLE $table_mailbox MODIFY COLUMN `quota` bigint(20) NOT NULL default '0'", TRUE); } /* TODO Database changes that should be done: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MySQL: * vacation: - DROP INDEX email - 'cache' field might be obsolete with vacation_notification - needs to be checked! * vacation_notification: - DEFAULT CHARSET and COLLATE should be changed - change all varchar fields to latin1 (email addresses don't contain utf8 characters) */ /* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */