Add db_prefix configuration option in place of db_table_*/db_sequence_* options

Make possible to use db_prefix for schema initialization in Installer (#1489067)
Fix updatedb.sh script so it recognizes also table prefix for external DDL files
pull/63/merge
Aleksander Machniak 11 years ago
parent d7fcd8ce42
commit 399db1b647

@ -1,6 +1,9 @@
CHANGELOG Roundcube Webmail CHANGELOG Roundcube Webmail
=========================== ===========================
- Add db_prefix configuration option in place of db_table_*/db_sequence_* options
- Make possible to use db_prefix for schema initialization in Installer (#1489067)
- Fix updatedb.sh script so it recognizes also table prefix for external DDL files
- Fix possible collision in generated thumbnail cache key (#1489069) - Fix possible collision in generated thumbnail cache key (#1489069)
- Fix exit code on bootsrap errors in CLI mode (#1489044) - Fix exit code on bootsrap errors in CLI mode (#1489044)
- Fix error handling in CLI mode, use STDERR and non-empty exit code (#1489043) - Fix error handling in CLI mode, use STDERR and non-empty exit code (#1489043)

@ -1,11 +1,11 @@
-- Roundcube Webmail initial database structure -- Roundcube Webmail initial database structure
-- --
-- Sequence "user_ids" -- Sequence "users_seq"
-- Name: user_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- Name: users_seq; Type: SEQUENCE; Schema: public; Owner: postgres
-- --
CREATE SEQUENCE user_ids CREATE SEQUENCE users_seq
INCREMENT BY 1 INCREMENT BY 1
NO MAXVALUE NO MAXVALUE
NO MINVALUE NO MINVALUE
@ -17,7 +17,7 @@ CREATE SEQUENCE user_ids
-- --
CREATE TABLE users ( CREATE TABLE users (
user_id integer DEFAULT nextval('user_ids'::text) PRIMARY KEY, user_id integer DEFAULT nextval('users_seq'::text) PRIMARY KEY,
username varchar(128) DEFAULT '' NOT NULL, username varchar(128) DEFAULT '' NOT NULL,
mail_host varchar(128) DEFAULT '' NOT NULL, mail_host varchar(128) DEFAULT '' NOT NULL,
created timestamp with time zone DEFAULT now() NOT NULL, created timestamp with time zone DEFAULT now() NOT NULL,
@ -45,11 +45,11 @@ CREATE INDEX session_changed_idx ON session (changed);
-- --
-- Sequence "identity_ids" -- Sequence "identities_seq"
-- Name: identity_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- Name: identities_seq; Type: SEQUENCE; Schema: public; Owner: postgres
-- --
CREATE SEQUENCE identity_ids CREATE SEQUENCE identities_seq
START WITH 1 START WITH 1
INCREMENT BY 1 INCREMENT BY 1
NO MAXVALUE NO MAXVALUE
@ -62,7 +62,7 @@ CREATE SEQUENCE identity_ids
-- --
CREATE TABLE identities ( CREATE TABLE identities (
identity_id integer DEFAULT nextval('identity_ids'::text) PRIMARY KEY, identity_id integer DEFAULT nextval('identities_seq'::text) PRIMARY KEY,
user_id integer NOT NULL user_id integer NOT NULL
REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
changed timestamp with time zone DEFAULT now() NOT NULL, changed timestamp with time zone DEFAULT now() NOT NULL,
@ -82,11 +82,11 @@ CREATE INDEX identities_email_idx ON identities (email, del);
-- --
-- Sequence "contact_ids" -- Sequence "contacts_seq"
-- Name: contact_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- Name: contacts_seq; Type: SEQUENCE; Schema: public; Owner: postgres
-- --
CREATE SEQUENCE contact_ids CREATE SEQUENCE contacts_seq
START WITH 1 START WITH 1
INCREMENT BY 1 INCREMENT BY 1
NO MAXVALUE NO MAXVALUE
@ -99,7 +99,7 @@ CREATE SEQUENCE contact_ids
-- --
CREATE TABLE contacts ( CREATE TABLE contacts (
contact_id integer DEFAULT nextval('contact_ids'::text) PRIMARY KEY, contact_id integer DEFAULT nextval('contacts_seq'::text) PRIMARY KEY,
user_id integer NOT NULL user_id integer NOT NULL
REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
changed timestamp with time zone DEFAULT now() NOT NULL, changed timestamp with time zone DEFAULT now() NOT NULL,
@ -115,11 +115,11 @@ CREATE TABLE contacts (
CREATE INDEX contacts_user_id_idx ON contacts (user_id, del); CREATE INDEX contacts_user_id_idx ON contacts (user_id, del);
-- --
-- Sequence "contactgroups_ids" -- Sequence "contactgroups_seq"
-- Name: contactgroups_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- Name: contactgroups_seq; Type: SEQUENCE; Schema: public; Owner: postgres
-- --
CREATE SEQUENCE contactgroups_ids CREATE SEQUENCE contactgroups_seq
INCREMENT BY 1 INCREMENT BY 1
NO MAXVALUE NO MAXVALUE
NO MINVALUE NO MINVALUE
@ -131,7 +131,7 @@ CREATE SEQUENCE contactgroups_ids
-- --
CREATE TABLE contactgroups ( CREATE TABLE contactgroups (
contactgroup_id integer DEFAULT nextval('contactgroups_ids'::text) PRIMARY KEY, contactgroup_id integer DEFAULT nextval('contactgroups_seq'::text) PRIMARY KEY,
user_id integer NOT NULL user_id integer NOT NULL
REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE, REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
changed timestamp with time zone DEFAULT now() NOT NULL, changed timestamp with time zone DEFAULT now() NOT NULL,
@ -238,11 +238,11 @@ CREATE TABLE dictionary (
); );
-- --
-- Sequence "searches_ids" -- Sequence "searches_seq"
-- Name: searches_ids; Type: SEQUENCE; Schema: public; Owner: postgres -- Name: searches_seq; Type: SEQUENCE; Schema: public; Owner: postgres
-- --
CREATE SEQUENCE search_ids CREATE SEQUENCE searches_seq
INCREMENT BY 1 INCREMENT BY 1
NO MAXVALUE NO MAXVALUE
NO MINVALUE NO MINVALUE
@ -254,7 +254,7 @@ CREATE SEQUENCE search_ids
-- --
CREATE TABLE searches ( CREATE TABLE searches (
search_id integer DEFAULT nextval('search_ids'::text) PRIMARY KEY, search_id integer DEFAULT nextval('searches_seq'::text) PRIMARY KEY,
user_id integer NOT NULL user_id integer NOT NULL
REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE, REFERENCES users (user_id) ON DELETE CASCADE ON UPDATE CASCADE,
"type" smallint DEFAULT 0 NOT NULL, "type" smallint DEFAULT 0 NOT NULL,
@ -274,4 +274,4 @@ CREATE TABLE "system" (
value text value text
); );
INSERT INTO system (name, value) VALUES ('roundcube-version', '2013011700'); INSERT INTO system (name, value) VALUES ('roundcube-version', '2013042700');

@ -35,7 +35,7 @@ if (!$db->is_connected() || $db->is_error()) {
} }
// iterate over all users // iterate over all users
$sql_result = $db->query("SELECT user_id FROM " . $RCMAIL->config->get('db_table_users', 'users')." WHERE 1=1"); $sql_result = $db->query("SELECT user_id FROM " . $db->table_name('users') . " ORDER BY user_id");
while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) { while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) {
echo "Indexing contacts for user " . $sql_arr['user_id'] . "..."; echo "Indexing contacts for user " . $sql_arr['user_id'] . "...";

@ -61,7 +61,7 @@ if ($args['user'])
$query = 'user_id=' . intval($args['user']); $query = 'user_id=' . intval($args['user']);
// iterate over all users // iterate over all users
$sql_result = $db->query("SELECT * FROM " . $rcmail->config->get('db_table_users', 'users')." WHERE $query"); $sql_result = $db->query("SELECT * FROM " . $db->table_name('users') . " WHERE $query");
while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) { while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) {
echo "Updating prefs for user " . $sql_arr['user_id'] . "..."; echo "Updating prefs for user " . $sql_arr['user_id'] . "...";

@ -180,13 +180,46 @@ function update_db_schema($package, $version, $file)
function fix_table_names($sql) function fix_table_names($sql)
{ {
global $DB; global $DB, $RC, $dir;
static $tables;
static $sequences;
$prefix = $RC->config->get('db_prefix');
$engine = $DB->db_provider;
if (empty($prefix)) {
return $sql;
}
if ($tables === null) {
$tables = array();
$sequences = array();
// read complete schema (initial) file
$filename = "$dir/../$engine.initial.sql";
$schema = @file_get_contents($filename);
// find table names
if (preg_match_all('/CREATE TABLE (\[dbo\]\.|IF NOT EXISTS )?[`"\[\]]*([^`"\[\] \r\n]+)/i', $schema, $matches)) {
foreach ($matches[2] as $table) {
$tables[$table] = $prefix . $table;
}
}
// find sequence names
if ($engine == 'postgres' && preg_match_all('/CREATE SEQUENCE (IF NOT EXISTS )?"?([^" \n\r]+)/i', $schema, $matches)) {
foreach ($matches[2] as $sequence) {
$sequences[$sequence] = $prefix . $sequence;
}
}
}
foreach (array('users','identities','contacts','contactgroups','contactgroupmembers','session','cache','cache_index','cache_index','cache_messages','dictionary','searches','system') as $table) { // replace table names
$real_table = $DB->table_name($table); foreach ($tables as $table => $real_table) {
if ($real_table != $table) { $sql = preg_replace("/([^a-zA-Z0-9_])$table([^a-zA-Z0-9_])/", "\\1$real_table\\2", $sql);
$sql = preg_replace("/([^a-z0-9_])$table([^a-z0-9_])/i", "\\1$real_table\\2", $sql);
} }
// replace sequence names
foreach ($sequences as $sequence => $real_sequence) {
$sql = preg_replace("/([^a-zA-Z0-9_])$sequence([^a-zA-Z0-9_])/", "\\1$real_sequence\\2", $sql);
} }
return $sql; return $sql;

@ -36,27 +36,7 @@ $rcmail_config['db_dsnr'] = '';
// see: http://www.php.net/manual/en/features.persistent-connections.php // see: http://www.php.net/manual/en/features.persistent-connections.php
$rcmail_config['db_persistent'] = FALSE; $rcmail_config['db_persistent'] = FALSE;
// you can define specific table names used to store webmail data // you can define specific table (and sequence) names prefix
$rcmail_config['db_table_users'] = 'users'; $rcmail_config['db_prefix'] = '';
$rcmail_config['db_table_identities'] = 'identities';
$rcmail_config['db_table_contacts'] = 'contacts';
$rcmail_config['db_table_contactgroups'] = 'contactgroups';
$rcmail_config['db_table_contactgroupmembers'] = 'contactgroupmembers';
$rcmail_config['db_table_session'] = 'session';
$rcmail_config['db_table_cache'] = 'cache';
$rcmail_config['db_table_cache_index'] = 'cache_index';
$rcmail_config['db_table_cache_thread'] = 'cache_thread';
$rcmail_config['db_table_cache_messages'] = 'cache_messages';
$rcmail_config['db_table_dictionary'] = 'dictionary';
$rcmail_config['db_table_searches'] = 'searches';
$rcmail_config['db_table_system'] = 'system';
// you can define specific sequence names used in PostgreSQL
$rcmail_config['db_sequence_users'] = 'user_ids';
$rcmail_config['db_sequence_identities'] = 'identity_ids';
$rcmail_config['db_sequence_contacts'] = 'contact_ids';
$rcmail_config['db_sequence_contactgroups'] = 'contactgroups_ids';
$rcmail_config['db_sequence_searches'] = 'search_ids';
// end db config file // end db config file

@ -301,6 +301,18 @@ echo '<label for="cfgdbpass">Database password (omit for sqlite)</label><br />';
?> ?>
</dd> </dd>
<dt class="propname">db_prefix</dt>
<dd>
<?php
$input_prefix = new html_inputfield(array('name' => '_db_prefix', 'size' => 20, 'id' => "cfgdbprefix"));
echo $input_prefix->show($RCI->getprop('db_prefix'));
?>
<div>Optional prefix that will be added to database object names (tables and sequences).</div>
</dd>
</dl> </dl>
</fieldset> </fieldset>

@ -372,7 +372,7 @@ class rcube_install
$existing_tables = $DB->list_tables(); $existing_tables = $DB->list_tables();
foreach ($db_schema as $table => $cols) { foreach ($db_schema as $table => $cols) {
$table = !empty($this->config['db_table_'.$table]) ? $this->config['db_table_'.$table] : $table; $table = $this->config['db_prefix'] . $table;
if (!in_array($table, $existing_tables)) { if (!in_array($table, $existing_tables)) {
$errors[] = "Missing table '".$table."'"; $errors[] = "Missing table '".$table."'";
} }
@ -655,6 +655,7 @@ class rcube_install
*/ */
function exec_sql($sql, $DB) function exec_sql($sql, $DB)
{ {
$sql = $this->fix_table_names($sql, $DB);
$buff = ''; $buff = '';
foreach (explode("\n", $sql) as $line) { foreach (explode("\n", $sql) as $line) {
if (preg_match('/^--/', $line) || trim($line) == '') if (preg_match('/^--/', $line) || trim($line) == '')
@ -673,6 +674,35 @@ class rcube_install
} }
/**
* Parse SQL file and fix table names according to db_prefix
* Note: This need to be a complete database initial file
*/
private function fix_table_names($sql, $DB)
{
if (empty($this->config['db_prefix'])) {
return $sql;
}
// replace table names
if (preg_match_all('/CREATE TABLE (\[dbo\]\.|IF NOT EXISTS )?[`"\[\]]*([^`"\[\] \r\n]+)/i', $sql, $matches)) {
foreach ($matches[2] as $table) {
$real_table = $this->config['db_prefix'] . $table;
$sql = preg_replace("/([^a-zA-Z0-9_])$table([^a-zA-Z0-9_])/", "\\1$real_table\\2", $sql);
}
}
// replace sequence names
if ($DB->db_provider == 'postgres' && preg_match_all('/CREATE SEQUENCE (IF NOT EXISTS )?"?([^" \n\r]+)/i', $sql, $matches)) {
foreach ($matches[2] as $sequence) {
$real_sequence = $this->config['db_prefix'] . $sequence;
$sql = preg_replace("/([^a-zA-Z0-9_])$sequence([^a-zA-Z0-9_])/", "\\1$real_sequence\\2", $sql);
}
}
return $sql;
}
/** /**
* Handler for Roundcube errors * Handler for Roundcube errors
*/ */

@ -24,7 +24,7 @@ else if (!$read_main) {
} }
echo '<br />'; echo '<br />';
if ($read_db && !empty($RCI->config['db_table_users'])) { if ($read_db && !empty($RCI->config['db_dsnw'])) {
$RCI->pass('db.inc.php'); $RCI->pass('db.inc.php');
} }
else if ($read_db) { else if ($read_db) {
@ -171,7 +171,7 @@ else if ($db_working && $_POST['updatedb']) {
// test database // test database
if ($db_working) { if ($db_working) {
$db_read = $DB->query("SELECT count(*) FROM {$RCI->config['db_table_users']}"); $db_read = $DB->query("SELECT count(*) FROM {$RCI->config['db_prefix']}users");
if ($DB->is_error()) { if ($DB->is_error()) {
$RCI->fail('DB Schema', "Database not initialized"); $RCI->fail('DB Schema', "Database not initialized");
echo '<p><input type="submit" name="initdb" value="Initialize database" /></p>'; echo '<p><input type="submit" name="initdb" value="Initialize database" /></p>';
@ -195,11 +195,11 @@ if ($db_working) {
if ($db_working) { if ($db_working) {
// write test // write test
$insert_id = md5(uniqid()); $insert_id = md5(uniqid());
$db_write = $DB->query("INSERT INTO {$RCI->config['db_table_session']} (sess_id, created, ip, vars) VALUES (?, ".$DB->now().", '127.0.0.1', 'foo')", $insert_id); $db_write = $DB->query("INSERT INTO {$RCI->config['db_prefix']}session (sess_id, created, ip, vars) VALUES (?, ".$DB->now().", '127.0.0.1', 'foo')", $insert_id);
if ($db_write) { if ($db_write) {
$RCI->pass('DB Write'); $RCI->pass('DB Write');
$DB->query("DELETE FROM {$RCI->config['db_table_session']} WHERE sess_id=?", $insert_id); $DB->query("DELETE FROM {$RCI->config['db_prefix']}session WHERE sess_id=?", $insert_id);
} }
else { else {
$RCI->fail('DB Write', $RCI->get_error()); $RCI->fail('DB Write', $RCI->get_error());

@ -846,11 +846,9 @@ class rcube_db
{ {
$rcube = rcube::get_instance(); $rcube = rcube::get_instance();
// return table name if configured // add prefix to the table name if configured
$config_key = 'db_table_'.$table; if ($prefix = $rcube->config->get('db_prefix')) {
return $prefix . $table;
if ($name = $rcube->config->get($config_key)) {
return $name;
} }
return $table; return $table;

@ -53,19 +53,20 @@ class rcube_db_pgsql extends rcube_db
/** /**
* Return correct name for a specific database sequence * Return correct name for a specific database sequence
* *
* @param string $sequence Secuence name * @param string $table Table name
* *
* @return string Translated sequence name * @return string Translated sequence name
*/ */
protected function sequence_name($sequence) protected function sequence_name($table)
{ {
// Note: we support only one sequence per table
// Note: The sequence name must be <table_name>_seq
$sequence = $table . '_seq';
$rcube = rcube::get_instance(); $rcube = rcube::get_instance();
// return sequence name if configured // return sequence name if configured
$config_key = 'db_sequence_'.$sequence; if ($prefix = $rcube->config->get('db_prefix')) {
return $prefix . $sequence;
if ($name = $rcube->config->get($config_key)) {
return $name;
} }
return $sequence; return $sequence;

Loading…
Cancel
Save