<?php include_once($INC . "/sql.inc.php"); include_once($INC . "/state.inc.php"); include_once($INC . "/util.inc.php"); define("RG_DROP_TABLES", 1); define("RG_IGNORE_ERRORS", 1 << 1); $rg_sql_struct_slaves = array(); $rg_sql_struct = array(); $rg_sql_struct[1] = array(); $rg_sql_struct[1]['tables'] = array( "repos" => "CREATE TABLE repos" . " (repo_id SERIAL PRIMARY KEY" . ", name TEXT UNIQUE NOT NULL" . ", uid INTEGER NOT NULL" . ", itime INTEGER NOT NULL" . ", disk_quota_mb INTEGER DEFAULT 0" . ", disk_used_mb INTEGER NOT NULL DEFAULT 0" . ", max_commit_size INTEGER DEFAULT 0" . ", master INTEGER NOT NULL DEFAULT 0" . ", description TEXT NOT NULL DEFAULT ''" . ", git_dir_done INTEGER NOT NULL DEFAULT 0" . ", default_rights TEXT NOT NULL DEFAULT ''" . ", deleted INTEGER NOT NULL DEFAULT 0" . ", max_users INTEGER NOT NULL DEFAULT 0" . ")", "rights" => "CREATE TABLE rights" . " (type TEXT NOT NULL" . ", obj_id INTEGER NOT NULL" . ", uid INTEGER NOT NULL" . ", rights TEXT NOT NULL" . ", itime INTEGER NOT NULL)", "state" => "CREATE TABLE state" . " (var TEXT PRIMARY KEY" . ", value TEXT NOT NULL)", "keys" => "CREATE TABLE keys" . " (key_id SERIAL PRIMARY KEY" . ", itime INTEGER NOT NULL" . ", uid INTEGER NOT NULL" . ", key TEXT UNIQUE NOT NULL)", "users" => "CREATE TABLE users" . " (uid SERIAL PRIMARY KEY" . ", username TEXT UNIQUE NOT NULL" . ", realname TEXT NOT NULL" . ", salt TEXT NOT NULL" . ", pass TEXT NOT NULL" . ", email TEXT NOT NULL" . ", itime INTEGER NOT NULL" . ", suspended INTEGER NOT NULL DEFAULT 0" . ", session_time INTEGER NOT NULL DEFAULT 3600" . ", last_seen INTEGER NOT NULL DEFAULT 0" . ", is_admin INTEGER NOT NULL DEFAULT 0" . ", disk_quota_mb INTEGER NOT NULL DEFAULT 0" . ", disk_used_mb INTEGER NOT NULL DEFAULT 0" . ", rights TEXT NOT NULL" . ", confirmed INTEGER NOT NULL DEFAULT 0" . ", confirm_token TEXT NOT NULL DEFAULT ''" . ")", "sess" => "CREATE TABLE sess" . " (sid TEXT PRIMARY KEY" . ", uid INTEGER NOT NULL" . ", expire INTEGER NOT NULL" . ", session_time INTEGER NOT NULL" . ", ip TEXT NOT NULL)", "forgot_pass" => "CREATE TABLE forgot_pass" . " (token TEXT PRIMARY KEY" . ", uid INTEGER NOT NULL" . ", expire INTEGER NOT NULL)", "tokens" => "CREATE TABLE tokens" . " (token TEXT PRIMARY KEY" . ", sid TEXT NOT NULL" . ", expire INTEGER NOT NULL)" ); $rg_sql_struct[1]['other'] = array(); $rg_sql_struct[2] = array(); $rg_sql_struct[2]['tables'] = array( "suggestions" => "CREATE TABLE suggestions" . " (suggestion_id SERIAL PRIMARY KEY" . ", uid INTEGER NOT NULL" . ", email TEXT NOT NULL" . ", suggestion TEXT NOT NULL)" ); $rg_sql_struct[2]['other'] = array(); $rg_sql_struct[3] = array(); $rg_sql_struct[3]['tables'] = array(); $rg_sql_struct[3]['other'] = array( "add organization field to user" => "ALTER TABLE users" . " ADD organization SMALLINT NOT NULL DEFAULT 0" ); $rg_sql_struct[4] = array(); $rg_sql_struct[4]['tables'] = array( "merge_requests" => "CREATE TABLE merge_requests (" . "repo_id INT NOT NULL" . ", itime INT NOT NULL" . ", namespace TEXT NOT NULL" . ", refname TEXT NOT NULL" . ", old_rev CHAR(40) NOT NULL" . ", new_rev CHAR(40) NOT NULL" . ", done INT NOT NULL DEFAULT 0" . ", ip TEXT NOT NULL" . ")" ); $rg_sql_struct[4]['other'] = array( "merge_request_index_repo_id" => "CREATE INDEX merge_requests_i_repo_id" . " ON merge_requests (repo_id)" ); $rg_sql_struct[5] = array(); $rg_sql_struct[5]['tables'] = array( "history_push" => "CREATE TABLE history_push (" . "itime INT NOT NULL" . ", repo_id INT NOT NULL" . ", ip TEXT NOT NULL" . ", refname TEXT NOT NULL" . ", old_rev CHAR(40) NOT NULL" . ", new_rev CHAR(40) NOT NULL" . ")" ); $rg_sql_struct[6] = array(); $rg_sql_struct[6]['tables'] = array( "bugs_max" => "CREATE TABLE bugs_max (" . "repo_id INT NOT NULL PRIMARY KEY" . ", last_bug_id INT NOT NULL" . ")", "bugs" => "CREATE TABLE bugs (" . "repo_id INT NOT NULL" . ", bug_id INT NOT NULL" . ", itime INT NOT NULL" . ", utime INT NOT NULL" . ", uid INT NOT NULL" . ", ip TEXT NOT NULL" . ", title TEXT NOT NULL" . ", body TEXT NOT NULL" . ", state SMALLINT NOT NULL" . ", assigned_uid INT NOT NULL" . ", deleted INT NOT NULL" . ")", "bug_notes" => "CREATE TABLE bug_notes (" . "repo_id INT NOT NULL" . ", bug_id INT NOT NULL" . ", note TEXT NOT NULL" . ", itime INT NOT NULL" . ", uid INT NOT NULL" . ", ip TEXT NOT NULL" . ")", "bug_labels" => "CREATE TABLE bug_labels (" . "repo_id INT NOT NULL" . ", bug_id INT NOT NULL" . ", label TEXT NOT NULL" . ")" ); $rg_sql_struct[6]['other'] = array( "bugs_index_repo_id_bug_id" => "CREATE UNIQUE INDEX bugs_i_repo_id_bug_id" . " ON bugs (repo_id, bug_id)", "bugs_index_itime" => "CREATE INDEX bugs_i_itime" . " ON bugs (itime)", "bug_notes_index_repo_id_bug_id" => "CREATE INDEX bug_notes_i_repo_id_bug_id" . " ON bug_notes (repo_id, bug_id)", "bug_labels_index_repo_id_bug_id" => "CREATE INDEX bug_labels_i_repo_id_bug_id" . " ON bug_labels (repo_id, bug_id)" ); $rg_sql_struct[7] = array(); $rg_sql_struct[7]['tables'] = array( "bug_search" => "CREATE TABLE bug_search (" . "repo_id INT NOT NULL" . ", uid INT NOT NULL" . ", name TEXT NOT NULL" . ", data TEXT NOT NULL" . ", for_all_users SMALLINT NOT NULL" . ")" ); $rg_sql_struct[7]['other'] = array( "bug_search_repo_id_uid" => "CREATE INDEX bug_search_i_repo_id_uid" . " ON bug_search(repo_id, uid)" ); $rg_sql_struct[8] = array(); $rg_sql_struct[8]['tables'] = array(); $rg_sql_struct[8]['other'] = array( "more info for keys: last_use" => "ALTER TABLE keys" . " ADD last_use INT NOT NULL DEFAULT 0", "more info for keys: last_ip" => "ALTER TABLE keys" . " ADD last_ip TEXT NOT NULL DEFAULT ''" ); $rg_sql_struct[9] = array(); $rg_sql_struct[9]['tables'] = array(); $rg_sql_struct[9]['other'] = array( "allow duplicate ssh keys" => "ALTER TABLE keys" . " DROP CONSTRAINT IF EXISTS keys_pkey", "allow duplicate ssh keys2" => "ALTER TABLE keys" . " DROP CONSTRAINT IF EXISTS keys_key_key", "index on key_id" => "CREATE INDEX keys_i_key_id" . " ON keys(key_id)", "allow duplicate repos names" => "ALTER TABLE repos" . " DROP CONSTRAINT IF EXISTS repos_pkey", "allow duplicate repos names2" => "ALTER TABLE repos" . " DROP CONSTRAINT IF EXISTS repos_name_key", "index on repos names" => "CREATE INDEX repos_i_name" . " ON repos(name)" ); $rg_sql_struct[10] = array(); $rg_sql_struct[10]['tables'] = array( "repo_history" => "CREATE TABLE repo_history (" . "itime INT NOT NULL" . ", repo_id INT NOT NULL" . ", category SMALLINT NOT NULL" . ", message TEXT NOT NULL)" ); $rg_sql_struct[10]['other'] = array(); $rg_sql_struct_slaves['repo_history'] = "repo_history"; $rg_sql_struct[11] = array(); $rg_sql_struct[11]['tables'] = array( "events" => "CREATE TABLE events (" . "id BIGSERIAL PRIMARY KEY" . ", itime INT NOT NULL" . ", prio SMALLINT NOT NULL" . ", data TEXT NOT NULL)" ); $rg_sql_struct[11]['other'] = array(); $rg_sql_struct[12] = array(); $rg_sql_struct[12]['tables'] = array(); $rg_sql_struct[12]['other'] = array( "index repos-uid" => "CREATE INDEX repos_i_uid ON repos(uid)", "index on repos-repo_id" => "CREATE INDEX repos_i_repo_id ON repos(repo_id)" ); $rg_sql_struct[13] = array(); $rg_sql_struct[13]['tables'] = array( "repos_renames" => "CREATE TABLE repos_renames (" . "uid INT NOT NULL" . ", old_name TEXT NOT NULL" . ", itime INT NOT NULL" . ", repo_id INT NOT NULL)" ); $rg_sql_struct[13]['other'] = array( "repos_renames_index_ui_old_name" => "CREATE INDEX repos_renames_i_uid_old_name" . " ON repos_renames(uid, old_name)" ); $rg_sql_struct[14] = array(); $rg_sql_struct[14]['tables'] = array( "users_renames" => "CREATE TABLE users_renames (" . "uid INT NOT NULL" . ", old_name TEXT NOT NULL" . ", itime INT NOT NULL)" ); $rg_sql_struct[14]['other'] = array( "users_renames_index_old_name" => "CREATE INDEX users_renames_i_old_name ON repos_renames(old_name)" ); $rg_sql_struct[15] = array(); $rg_sql_struct[15]['tables'] = array( "watch_repo" => "CREATE TABLE watch_repo (" . "uid INT NOT NULL" . ", repo_id INT NOT NULL)", "watch_bug" => "CREATE TABLE watch_bug (" . "uid INT NOT NULL" . ", repo_id INT NOT NULL" . ", bug_id INT NOT NULL)" ); $rg_sql_struct[16] = array(); $rg_sql_struct[16]['other'] = array( "ssh_usage_count" => "ALTER TABLE keys" . " ADD count INT NOT NULL DEFAULT 0" ); $rg_sql_struct[17] = array(); $rg_sql_struct[17]['other'] = array( "misc_field_for_rights" => "ALTER TABLE rights" . " ADD misc TEXT NOT NULL DEFAULT ''" ); $rg_sql_struct[18] = array(); $rg_sql_struct[18]['tables'] = array( "plans" => "CREATE TABLE plans (id SERIAL PRIMARY KEY" . ", name TEXT NOT NULL DEFAULT ''" . ", disk_mb INT NOT NULL DEFAULT 0" . ", users INT NOT NULL DEFAULT 0" . ", bw INT NOT NULL DEFAULT 0" . ", position INT NOT NULL DEFAULT 0" . ")" ); $rg_sql_struct[18]['other'] = array( "plan_id" => "ALTER TABLE users ADD plan_id INT NOT NULL DEFAULT 0", "commit_size" => "ALTER TABLE plans ADD commit_size INT NOT NULL DEFAULT 0", "disk_quota" => "ALTER TABLE users DROP disk_quota_mb", "plan_desc" => "ALTER TABLE plans ADD description TEXT NOT NULL DEFAULT ''", "plan_speed" => "ALTER TABLE plans ADD speed INT NOT NULL DEFAULT 0", "standard plan" => "INSERT INTO plans (name, disk_mb, users, bw" . ", speed, commit_size, description, position)" . " VALUES ('Standard', 0, 0, 0, 0, 0, 'Standard plan', 1000)", "key_first" => "ALTER TABLE keys ADD first_use INT NOT NULL DEFAULT 0" ); $rg_sql_struct[19] = array(); $rg_sql_struct[19]['tables'] = array(); $rg_sql_struct[19]['other'] = array( "rights_prio" => "ALTER TABLE rights ADD prio INT NOT NULL DEFAULT 0" ); $rg_sql_struct[20] = array(); $rg_sql_struct[20]['tables'] = array(); $rg_sql_struct[20]['other'] = array( "repo_drop_max_users" => "ALTER TABLE repos DROP max_users" ); $rg_sql_struct[21] = array(); $rg_sql_struct[21]['tables'] = array(); $rg_sql_struct[21]['other'] = array( "plans_max_public_repos" => "ALTER TABLE plans ADD max_public_repos INT NOT NULL DEFAULT 0", "plans_max_private_repos" => "ALTER TABLE plans ADD max_private_repos INT NOT NULL DEFAULT 0" ); $rg_sql_struct[22] = array(); $rg_sql_struct[22]['tables'] = array(); $rg_sql_struct[22]['other'] = array( "users_last_ip" => "ALTER TABLE users" . " ADD last_ip TEXT NOT NULL DEFAULT '?'" ); $rg_sql_struct[23] = array(); $rg_sql_struct[23]['tables'] = array(); $rg_sql_struct[23]['other'] = array( "repo_public_private" => "ALTER TABLE repos" . " ADD public INT NOT NULL DEFAULT 0" ); $rg_sql_struct[24] = array(); $rg_sql_struct[24]['tables'] = array(); $rg_sql_struct[24]['other'] = array( "default_rights are not used anymore" => "ALTER TABLE repos" . " DROP default_rights" ); $rg_sql_struct[25] = array(); $rg_sql_struct[25]['tables'] = array(); $rg_sql_struct[25]['other'] = array( "we must record who gave rights" => "ALTER TABLE rights" . " ADD who INTEGER NOT NULL DEFAULT 0", "we need an int id for rights" => "ALTER TABLE rights" . " ADD right_id SERIAL" ); $rg_sql_struct[26] = array(); $rg_sql_struct[26]['tables'] = array(); $rg_sql_struct[26]['other'] = array( "we implement IP access in generic rights" => "ALTER TABLE rights" . " ADD ip TEXT NOT NULL DEFAULT ''", "we need a new misc field for path" => "ALTER TABLE rights" . " ADD misc2 TEXT NOT NULL DEFAULT ''" ); $rg_sql_struct[27] = array(); $rg_sql_struct[27]['tables'] = array(); $rg_sql_struct[27]['other'] = array( "we need to lookup rights fast" => "CREATE INDEX rights_i_type_obj_id ON rights(type, obj_id)", "record who deleted a bug" => "ALTER TABLE bugs ADD deleted_who INTEGER NOT NULL DEFAULT 0" ); $rg_sql_struct[28] = array(); $rg_sql_struct[28]['tables'] = array(); $rg_sql_struct[28]['other'] = array( "add repos.last_bug_id" => "ALTER TABLE repos ADD last_bug_id INTEGER NOT NULL DEFAULT 0" ); $rg_sql_struct[29] = array(); $rg_sql_struct[29]['tables'] = array(); $rg_sql_struct[29]['other'] = array( "add rights.description" => "ALTER TABLE rights ADD description TEXT NOT NULL DEFAULT ''", "index users table" => "CREATE UNIQUE INDEX users_username ON users(username)" ); $rg_sql_struct[30] = array(); $rg_sql_struct[30]['tables'] = array(); $rg_sql_struct[30]['other'] = array( "events.fail" => "ALTER TABLE events ADD fail SMALLINT NOT NULL DEFAULT 0", "events.tries" => "ALTER TABLE events ADD tries SMALLINT NOT NULL DEFAULT 0", "events.next_try" => "ALTER TABLE events ADD next_try INTEGER NOT NULL DEFAULT 0" ); $rg_sql_struct[31] = array(); $rg_sql_struct[31]['tables'] = array(); $rg_sql_struct[31]['other'] = array( "repo_history" => "ALTER TABLE repo_history ADD uid INTEGER NOT NULL DEFAULT 0" ); $rg_sql_struct[32] = array(); $rg_sql_struct[32]['tables'] = array(); $rg_sql_struct[32]['other'] = array( "repo_license" => "ALTER TABLE repos ADD license TEXT NOT NULL DEFAULT ''" ); $rg_sql_struct[33] = array(); $rg_sql_struct[33]['tables'] = array( "login_tokens" => "CREATE TABLE login_tokens (" . "id SERIAL" . ", uid INT NOT NULL DEFAULT 0" . ", itime INT NOT NULL DEFAULT 0" . ", used INT NOT NULL DEFAULT 0" . ", name TEXT NOT NULL DEFAULT ''" . ", secret TEXT NOT NULL DEFAULT ''" . ", ip TEXT NOT NULL DEFAULT ''" . ", conf BOOLEAN NOT NULL DEFAULT 't'" . ", last_used_tc INT NOT NULL DEFAULT 0)", "login_tokens_ip" => "CREATE TABLE login_tokens_ip (" . "uid INT NOT NULL DEFAULT 0" . ", ip TEXT NOT NULL DEFAULT ''" . ", itime INT NOT NULL DEFAULT 0" . ", expire INT NOT NULL DEFAULT 0" . ", token_id INT NOT NULL DEFAULT 0)" ); $rg_sql_struct[33]['other'] = array( "suggestion_itime" => "ALTER TABLE suggestions ADD itime INTEGER NOT NULL DEFAULT 0", "suggestion_email" => "ALTER TABLE suggestions DROP email", "login_tokens_i_uid" => "CREATE INDEX login_tokens_i_uid ON login_tokens(uid)", "login_tokens_ip_i_uid" => "CREATE INDEX login_tokens_ip_i_uid ON login_tokens_ip(uid)" ); $rg_sql_struct[34] = array(); $rg_sql_struct[34]['tables'] = array( "scratch_codes" => "CREATE TABLE scratch_codes (uid INT NOT NULL DEFAULT 0" . ", itime INT NOT NULL DEFAULT 0" . ", sc TEXT NOT NULL DEFAULT '')" ); $rg_sql_struct[34]['other'] = array( "scratch_codes_i_uid" => "CREATE INDEX scratch_codes_i_uid ON scratch_codes(uid)" ); $rg_sql_struct[35] = array(); $rg_sql_struct[35]['tables'] = array( "webhooks" => "CREATE TABLE webhooks (id SERIAL" . ", uid INT NOT NULL DEFAULT 0" . ", repo_id INT NOT NULL DEFAULT 0" . ", itime INT NOT NULL DEFAULT 0" . ", events TEXT NOT NULL DEFAULT ''" . ", url TEXT NOT NULL DEFAULT ''" . ", type SMALLINT NOT NULL DEFAULT 0" . ", client_cert TEXT NOT NULL DEFAULT ''" . ", client_ca_cert TEXT NOT NULL DEFAULT ''" . ", flags TEXT NOT NULL DEFAULT ''" . ", add_ip TEXT NOT NULL DEFAULT ''" . ", description TEXT NOT NULL DEFAULT ''" . ")" ); $rg_sql_struct[35]['other'] = array( "webhooks_i_uid" => "CREATE INDEX webhooks_i_uid ON webhooks(uid)", 'history_push not needed' => 'DROP TABLE history_push', 'add_key_for_webhooks' => "ALTER TABLE webhooks ADD key TEXT NOT NULL DEFAULT ''", 'add_opaque_for_webhooks' => "ALTER TABLE webhooks ADD opaque TEXT NOT NULL DEFAULT ''" ); $rg_sql_struct[36]['tables'] = array( 'watch_user' => "CREATE TABLE watch_user (" . "uid INT NOT NULL" . ", watch_uid INT NOT NULL)", 'repo_locks' => "CREATE TABLE repo_locks" . " (repo_id INT PRIMARY KEY, itime INT NOT NULL DEFAULT 0" . ", uid INT NOT NULL DEFAULT 0)" ); $rg_sql_struct[36]['other'] = array( 'users drop double unique constriaint' => "ALTER TABLE users DROP CONSTRAINT users_username_key", 'last_cmd' => "ALTER TABLE keys ADD last_cmd TEXT NOT NULL DEFAULT ''", 'wh_last_output' => "ALTER TABLE webhooks ADD last_output TEXT NOT NULL DEFAULT ''", 'wh_htype' => "ALTER TABLE webhooks ADD htype TEXT NOT NULL DEFAULT 'http'", 'wh_idata' => "ALTER TABLE webhooks ADD idata TEXT NOT NULL DEFAULT ''", 'watch_repo_i_uid' => "CREATE INDEX watch_repo_i_uid on watch_repo(uid)", 'watch_bug_i_uid' => "CREATE INDEX watch_bug_i_uid on watch_repo(uid)", 'watch_user_i_uid' => "CREATE INDEX watch_user_i_uid on watch_repo(uid)" ); $rg_sql_struct[37]['other'] = array( 'webhooks - add refname match' => "ALTER TABLE webhooks ADD refname TEXT NOT NULL DEFAULT ''", 'webhooks - repo_id not needed' => "ALTER TABLE webhooks DROP repo_id", 'webhooks - repo' => "ALTER TABLE webhooks ADD repo TEXT NOT NULL DEFAULT '';" ); $rg_sql_struct[38]['other'] = array( 'repo - template' => "ALTER TABLE repos ADD template TEXT NOT NULL DEFAULT ''", 'repo - pr id' => "ALTER TABLE repos ADD last_mr_id INT NOT NULL DEFAULT 0", 'merge_requests - id' => "ALTER TABLE merge_requests ADD id INT NOT NULL DEFAULT 0", 'merge_requests - who' => "ALTER TABLE merge_requests ADD who INT NOT NULL DEFAULT 0" ); // This must be the last line $rg_sql_schema_ver = count($rg_sql_struct); /* * Generate structure */ function rg_sql_struct_run($db, $flags, $old_schema_ver) { global $rg_sql_struct; global $rg_sql_schema_ver; $ignore_errors = ($flags & RG_IGNORE_ERRORS) ? TRUE : FALSE; $drop_tables = ($flags & RG_DROP_TABLES) ? TRUE : FALSE; rg_log_enter("sql_struct_run: flags=$flags" . " ignore_errors=" . ($ignore_errors ? "Yes" : "No") . " drop_tables=" . ($drop_tables ? "Yes" : "No") . " old_schema_ver=$old_schema_ver..."); $ret = TRUE; for ($i = $old_schema_ver + 1; $i <= $rg_sql_schema_ver; $i++) { foreach ($rg_sql_struct[$i] as $type => $sqls) { if (count($sqls) == 0) continue; foreach ($sqls as $id => $sql) { rg_log("Applying schema $i, type $type, id $id..."); if ((strcmp($type, "tables") == 0) && ($drop_tables === TRUE)) { rg_log("Dropping table [$id]..."); $sql2 = "DROP TABLE IF EXISTS $id CASCADE"; $res = rg_sql_query($db, $sql2); if ($res === FALSE) { rg_log("WARN: Cannot run sql ($sql2) (" . rg_sql_error() . ")!"); if (!$ignore_errors) { $ret = FALSE; break; } } rg_sql_free_result($res); } rg_log("Running [$sql]..."); $res = rg_sql_query($db, $sql); if ($res === FALSE) { rg_log("WARN: Cannot run sql ($sql) (" . rg_sql_error() . ")!"); if (!$ignore_errors) { $ret = FALSE; break; } } rg_sql_free_result($res); } if (!$ret) break; } if (!$ret) break; } rg_log_exit(); return $ret; } /* * Returns current version of the schema. */ function rg_sql_struct_get_current_ver($db) { global $rg_sql_schema_ver; rg_log_enter("sql_struct_get_current_ver"); $ret = FALSE; while (1) { $ret = rg_state_get($db, "schema_version"); if ($ret === FALSE) break; if (empty($ret)) $ret = 0; break; } rg_log_exit(); return $ret; } /* * Tests if an update of the structure is needed. * Returns FALSE on error, 0 if update is not needed, else 1. */ function rg_sql_struct_update_needed($db) { global $rg_sql_schema_ver; $old = rg_sql_struct_get_current_ver($db); if ($old === FALSE) return 1; if ($rg_sql_schema_ver == $old) return 0; return 1; } /* * Update schema if needed * Returns FALSE in case of error. * This must _not_ be run by web user because of the owner of the locking file. */ function rg_sql_struct_update($db, $flags) { global $rg_sql_schema_ver; rg_log_enter("sql_struct_update: flags=$flags"); $ret = FALSE; $rollback = 0; while (1) { $old = rg_sql_struct_get_current_ver($db); if ($old === FALSE) $old = 0; if ($rg_sql_schema_ver == $old) { $ret = TRUE; break; } // If we cannot lock, return error if (rg_lock("schema_update.lock") === FALSE) break; if (rg_sql_begin($db) !== TRUE) break; $rollback = 1; $r = rg_sql_struct_run($db, $flags, $old); if ($r !== TRUE) { rg_log("Cannot update schema (" . rg_sql_error() . ")"); break; } $r = rg_state_set($db, "schema_version", $rg_sql_schema_ver); if ($r !== TRUE) { rg_log("Cannot update schema ver (" . rg_state_error() . ")"); break; } if (rg_sql_commit($db) !== TRUE) break; $rollback = 0; $ret = TRUE; break; } if ($rollback == 1) rg_sql_rollback($db); rg_unlock("schema_update.lock"); rg_log_exit(); return $ret; } /* * Update slaves (postgresql partitions) * Returns FALSE in case of error */ function rg_sql_struct_slaves_update($db) { global $rg_sql_struct_slaves; rg_prof_start("sql_struct_slaves_update"); rg_log_enter("sql_struct_slaves_update"); $ret = FALSE; $rollback = 0; $locked = FALSE; while (1) { if (empty($rg_sql_struct_slaves)) { $ret = TRUE; break; } // If we cannot lock, return error if (rg_lock("slave_create.lock") === FALSE) break; $locked = TRUE; $last_ts = rg_state_get($db, "slaves_create_last_ts"); if ($last_ts === FALSE) break; $last_ts = sprintf("%u", $last_ts); if ($last_ts == 0) $last_ts = gmmktime(0, 0, 0, gmdate("m") - 1, 1, gmdate("Y")); rg_log("last_ts=$last_ts (" . gmdate("Y-m-d", $last_ts) . ")"); // First second of current month $current_month_ts = gmmktime(0, 0, 0, gmdate("m"), 1, gmdate("Y")); rg_log("current_month_ts=$current_month_ts (" . gmdate("Y-m-d", $current_month_ts) . ")"); // Do we have current month and the next one covered? if ($current_month_ts < $last_ts) { rg_log("No update needed!"); $ret = TRUE; break; } if (rg_sql_begin($db) !== TRUE) break; $rollback = 1; $ok = TRUE; $month = gmdate("m", $last_ts) + 1; $year = gmdate("Y", $last_ts); $ts = gmmktime(0, 0, 0, $month, 1, $year); $stop_ts = gmmktime(0, 0, 0, gmdate("m") + 1, 1, gmdate("Y")); while ($ts <= $stop_ts) { $month++; $next_ts = gmmktime(0, 0, 0, $month, 1, $year); foreach ($rg_sql_struct_slaves as $table) { $slave_table = $table . "_" . gmdate("Y_m", $ts); // First, check if exists. It is possible that // we did the update but we could not set the cache. $sql = "SELECT 1 FROM pg_class" . " WHERE relname = '" . $slave_table . "'"; $res = rg_sql_query($db, $sql); if ($res === FALSE) { $ok = FALSE; break; } $rows = rg_sql_num_rows($res); rg_sql_free_result($res); if ($rows > 0) continue; $sql = "CREATE TABLE " . $slave_table . " (CHECK(itime >= $ts AND itime <= " . ($next_ts - 1) . "))" . " INHERITS (" . $table . ")"; $res = rg_sql_query($db, $sql); if ($res === FALSE) { $ok = FALSE; break; } rg_sql_free_result($res); $sql = "CREATE INDEX " . $slave_table . "_i_itime" . " ON " . $slave_table . " (itime)"; $res = rg_sql_query($db, $sql); if ($res === FALSE) { $ok = FALSE; break; } rg_sql_free_result($res); } $ts = $next_ts; } if ($ok === FALSE) break; if (rg_sql_commit($db) !== TRUE) break; $r = rg_state_set($db, "slaves_create_last_ts", $stop_ts); if ($r !== TRUE) { rg_log("Cannot create slave (" . rg_state_error() . ")"); break; } $rollback = 0; $ret = TRUE; break; } if ($rollback == 1) rg_sql_rollback($db); if ($locked === TRUE) rg_unlock("slave_create.lock"); rg_log_exit(); rg_prof_end("sql_struct_slaves_update"); return $ret; } ?>