123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- <?php
- /**
- * User: sourcefabric
- * Date: 02/12/14
- * Class DatabaseSetup
- * Wrapper class for validating and installing the Airtime database during the installation process
- */
- class DatabaseSetup extends Setup {
- // airtime.conf section header
- const SECTION = "[database]";
- // Constant form field names for passing errors back to the front-end
- const DB_USER = "dbUser",
- DB_PASS = "dbPass",
- DB_NAME = "dbName",
- DB_HOST = "dbHost";
- // Form field values
- private $user, $pass, $name, $host;
- // Array of key->value pairs for airtime.conf
- static $properties;
- static $dbh = null;
- public function __construct($settings) {
- $this->user = $settings[self::DB_USER];
- $this->pass = $settings[self::DB_PASS];
- $this->name = $settings[self::DB_NAME];
- $this->host = $settings[self::DB_HOST];
- self::$properties = array(
- "host" => $this->host,
- "dbname" => $this->name,
- "dbuser" => $this->user,
- "dbpass" => $this->pass,
- );
- }
- private function setNewDatabaseConnection($dbName) {
- self::$dbh = new PDO("pgsql:host=" . $this->host . ";dbname=" . $dbName . ";port=5432"
- . ";user=" . $this->user . ";password=" . $this->pass);
- $err = self::$dbh->errorInfo();
- if ($err[1] != null) {
- throw new PDOException();
- }
- }
- /**
- * Runs various database checks against the given settings. If a database with the given name already exists,
- * we attempt to install the Airtime schema. If not, we first check if the user can create databases, then try
- * to create the database. If we encounter errors, the offending fields are returned in an array to the browser.
- * @return array associative array containing a display message and fields with errors
- * @throws AirtimeDatabaseException
- */
- public function runSetup() {
- try {
- $this->setNewDatabaseConnection("postgres");
- if ($this->checkDatabaseExists()) {
- $this->installDatabaseTables();
- } else {
- $this->checkUserCanCreateDb();
- $this->createDatabase();
- $this->installDatabaseTables();
- }
- } catch (PDOException $e) {
- throw new AirtimeDatabaseException("Couldn't establish a connection to the database! "
- . "Please check your credentials and try again. "
- . "PDO Exception: " . $e->getMessage(),
- array(
- self::DB_NAME,
- self::DB_USER,
- self::DB_PASS,
- ));
- }
- $this->writeToTemp();
- self::$dbh = null;
- return array(
- "message" => "Airtime database was created successfully!",
- "errors" => array(),
- );
- }
- protected function writeToTemp() {
- parent::writeToTemp(self::SECTION, self::$properties);
- }
- private function installDatabaseTables() {
- $this->checkDatabaseEncoding();
- $this->setNewDatabaseConnection($this->name);
- $this->checkSchemaExists();
- $this->createDatabaseTables();
- }
- /**
- * Check if the database settings and credentials given are valid
- * @return boolean true if the database given exists and the user is valid and can access it
- */
- private function checkDatabaseExists() {
- $statement = self::$dbh->prepare("SELECT datname FROM pg_database WHERE datname = :dbname");
- $statement->execute(array(":dbname" => $this->name));
- $result = $statement->fetch();
- return isset($result[0]);
- }
- /**
- * Check if the database schema has already been set up
- * @throws AirtimeDatabaseException
- */
- private function checkSchemaExists() {
- $statement = self::$dbh->prepare("SELECT EXISTS (SELECT relname FROM pg_class WHERE relname='cc_files')");
- $statement->execute();
- $result = $statement->fetch();
- if (isset($result[0]) && $result[0] == "t") {
- throw new AirtimeDatabaseException("Airtime is already installed in this database!", array());
- }
- }
- /**
- * Check if the given user has access on the given host to create a new database
- * @throws AirtimeDatabaseException
- */
- private function checkUserCanCreateDb() {
- $statement = self::$dbh->prepare("SELECT 1 FROM pg_roles WHERE rolname=:dbuser AND rolcreatedb='t'");
- $statement->execute(array(":dbuser" => $this->user));
- $result = $statement->fetch();
- if (!isset($result[0])) {
- throw new AirtimeDatabaseException("No database " . $this->name . " exists; user '" . $this->user
- . "' does not have permission to create databases on " . $this->host,
- array(
- self::DB_NAME,
- self::DB_USER,
- self::DB_PASS,
- ));
- }
- }
- /**
- * Creates the Airtime database using the given credentials
- * @throws AirtimeDatabaseException
- */
- private function createDatabase() {
- $statement = self::$dbh->prepare("CREATE DATABASE " . pg_escape_string($this->name)
- . " WITH ENCODING 'UTF8' TEMPLATE template0"
- . " OWNER " . pg_escape_string($this->user));
- if (!$statement->execute()) {
- throw new AirtimeDatabaseException("There was an error creating the database!",
- array(self::DB_NAME,));
- }
- }
- /**
- * Creates the Airtime database schema using the given credentials
- * @throws AirtimeDatabaseException
- */
- private function createDatabaseTables() {
- $sqlDir = dirname(dirname(__DIR__)) . "/build/sql/";
- $files = array("schema.sql", "sequences.sql", "views.sql", "triggers.sql", "defaultdata.sql");
- foreach ($files as $f) {
- try {
- /*
- * Unfortunately, we need to use exec here due to PDO's lack of support for importing
- * multi-line .sql files. PDO->exec() almost works, but any SQL errors stop the import,
- * so the necessary DROPs on non-existent tables make it unusable. Prepared statements
- * have multiple issues; they similarly die on any SQL errors, fail to read in multi-line
- * commands, and fail on any unescaped ? or $ characters.
- */
- exec("export PGPASSWORD=" . $this->pass . " && psql -U " . $this->user . " --dbname "
- . $this->name . " -h " . $this->host . " -f $sqlDir$f 2>/dev/null", $out, $status);
- } catch (Exception $e) {
- throw new AirtimeDatabaseException("There was an error setting up the Airtime schema!",
- array(self::DB_NAME,));
- }
- }
- }
- /**
- * Checks whether the newly-created database's encoding was properly set to UTF8
- * @throws AirtimeDatabaseException
- */
- private function checkDatabaseEncoding() {
- $statement = self::$dbh->prepare("SELECT pg_encoding_to_char(encoding) "
- . "FROM pg_database WHERE datname = :dbname");
- $statement->execute(array(":dbname" => $this->name));
- $encoding = $statement->fetch();
- if (!($encoding && $encoding[0] == "UTF8")) {
- throw new AirtimeDatabaseException("The database was installed with an incorrect encoding type!",
- array(self::DB_NAME,));
- }
- }
- }
|