< Script for Building Schema   (Previous) Table of Contents (Next)   More Information >

Include File for Building Schema

<?php

/**
 * Functions for creating and destroying a database schema's contents
 *
 * @package    Portability
 * @author     Daniel Convissor <danielc@analysisandsolutions.com>
 * @copyright  2002-2005 The Analysis and Solutions Company 
 * @license    http://www.analysisandsolutions.com/software/license.txt  Simple Public License
 * @link       http://www.analysisandsolutions.com/presentations/portability/
 * @see        buildSchema.php, viewSchema.php
 */

/**
 * Creates the tables and inserts the initial data
 *
 * Goes through each file in the <kbd>./schema</kbd> directory,
 * extracting the <kbd>CREATE TABLE</kbd> query therein, perform the
 * column type searches/replaces necessary for the current DBMS type
 * and then execute the query.
 *
 * Then pull the queries from each file in the <kbd>./data</kbd>
 * directory and execute those.
 *
 * @param object $p   the portability object
 * @param object $db  the PEAR::DB object
 *
 * @return void
 */
function buildSchema($p, $db) {
    set_time_limit(0);
    $dir  = dirname(__FILE__);
    $warn = 0;  // Take note of attempts to create something that exists.

    if (!is_dir("$dir/schema")) {
        echo "Could not read the $dir/schema directory.\n"
             . "Creation process terminated.\n";
        exit;
    }

    if (!is_dir("$dir/data")) {
        echo "Could not read the $dir/data directory.\n"
             . "Creation process terminated.\n";
        exit;
    }

    /*
     * preg search and replace pairs for create table statements
     */
    $search_create = array(
        '/REPLSQL-boolean/',
        '/REPLSQL-clob/',
        '/REPLSQL-dateliteral/',
        '/REPLSQL-date/',
        '/REPLSQL-decimal(\(\d+, *\d+\))/',
        '/REPLSQL-null/',
        '/REPLSQL-timestampliteral/',
        '/REPLSQL-timestamp/',
    );

    $replace_create = array(
        $p->getBooleanType(),
        $p->getClobType(),
        $p->getDateLiteralType(),
        $p->getDateType(),
        $p->getDecimalType(),
        $p->getNullKeyword(),
        $p->getTimestampLiteralType(),
        $p->getTimestampType(),
    );

    /*
     * preg search and replace pairs for data files
     */
    $search_data = array(
        '/REPLSQL-dateliteral/',
        '/REPLSQL-false/',
        '/REPLSQL-true/',
        '/REPLSQL-timestampliteral/',
    );

    $replace_data = array(
        $p->getDateLiteralType(),
        $db->quoteSmart(false),
        $db->quoteSmart(true),
        $p->getTimestampLiteralType(),
    );

    /*
     * Process each schema file
     */
    $dh = opendir("$dir/schema");
    while ($file = readdir($dh)) {
        if (substr($file, -3) == 'sql') {
            $new_table   = true;
            $schema_file = "$dir/schema/$file";
            $fh          = fopen($schema_file, 'r');
            $contents    = '';
            while (!feof($fh)) {
                $line = fgets($fh, 5000);
                if (substr($line, 0, 2) != '--') {
                    $contents .= $line;
                }
            }
            $contents    = preg_replace($search_create, $replace_create,
                                        $contents);
            $queries     = preg_split('/;\s*$/m', $contents);

            foreach ($queries as $query) {
                if (trim($query) == '') {
                    continue;
                }
                $res =& $db->query($query);
                if (DB::isError($res)) {
                    switch ($res->getCode()) {
                        case DB_ERROR_ALREADY_EXISTS:
                            echo "TABLE OR INDEX ALREADY EXISTS: $file\n";
                            $warn++;
                            $new_table = false;
                            break;
                        default:
                            echo "---------\nCREATION ERROR in $file\n\n"
                                 . $res->getDebugInfo()
                                 . "\n---------\nCreation process has been"
                                 . " terminated before completion!\n";
                            exit;
                    }
                }
            }
            if ($new_table) {
                echo "Executed create: $file\n";
            }

            /*
             * Process the data file
             */
            $data_file = "$dir/data/$file";
            if (file_exists($data_file) && $new_table) {
                $fh = fopen($data_file, 'r');

                while ($line = fgets($fh, 50000)) {
                    if (!preg_match('/^(\s*--|[\r\n]+)/', $line)) {
                        $line = preg_replace('/; *[\r\n]*$/', '', $line);
                        $line = preg_replace($search_data, $replace_data, $line);

                        switch ($db->phptype) {
                            case 'mysql':
                            case 'mysqli':
                            case 'pgsql':
                                $line = str_replace('\\', '\\\\', $line);
                                break;
                        }

                        $res =& $db->query($line);
                        if (DB::isError($res)) {
                            echo "---------\nDATA ERROR in $file\n\n"
                                 . $res->getDebugInfo()
                                 . "\n---------\nCreation process has been"
                                 . " terminated before completion!\n";
                            exit;
                        }
                    }
                }
                echo "Executed data:   $file\n";
            }
        }
    }

    if ($warn) {
        echo "\nCompleted table creation.\n\n";
        echo "$warn tables or indexes already existed.\n";
        echo "They were NOT overwritten.\n\n";
    } else {
        echo "\nSucessfully completed table creation.\n\n";
    }
}

/**
 * Drops the tables
 *
 * Goes through the list of files in the <kbd>./schema</kbd> directory
 * and drops each table listed there.  This works by using the file
 * names, so don't put multiple create table statements in a schema file.
 *
 * This is a simplistic routine, so it doesn't directly drop indexes.
 * So, if the DBMS doesn't drop indexes automatically when the table they
 * relate to are dropped, you'll have to do that manually.
 *
 * @param object $p   the portability object
 * @param object $db  the PEAR::DB object
 *
 * @return void
 */
function dropSchema($p, $db) {
    set_time_limit(0);
    $dir  = dirname(__FILE__);
    $warn = 0;  // Take note of attempts to drop things that don't exist.

    if (!is_dir("$dir/schema")) {
        echo "Could not read the $dir/schema directory.\n"
             . "Drop process terminated.\n";
        exit;
    }

    $dh = opendir("$dir/schema");
    while ($file = readdir($dh)) {
        if (substr($file, -3) == 'sql') {
            $table = basename($file, '.sql');
            $res =& $db->query("DROP TABLE $table " . $p->getDropRestrict());
            if (DB::isError($res)) {
                switch ($res->getCode()) {
                    case DB_ERROR_NOSUCHTABLE:
                    case DB_ERROR_NOT_FOUND:
                        echo "TABLE DIDN'T EXIST: $table\n";
                        break;
                    default:
                        echo "-----\nCOULDN'T DELETE: $table\n";
                        echo $res->getDebugInfo();
                        echo "-----\n\n";
                }
                $warn++;
                continue;
            }
            echo 'Dropped: ' . $table . "\n";
        }
    }

    if ($warn) {
        echo "\n$warn tables or indexes could not be deleted.\n\n";
    } else {
        echo "\nSucessfully completed table drops.\n\n";
    }
}