717 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			717 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # ==================================================================
 | |
| # Gossamer Threads Module Library - http://gossamer-threads.com/
 | |
| #
 | |
| #   GT::SQL
 | |
| #   CVS Info : 087,071,086,086,085      
 | |
| #   $Id: SQL.pm,v 1.112 2007/08/30 00:14:38 brewt Exp $
 | |
| #
 | |
| # Copyright (c) 2004 Gossamer Threads Inc.  All Rights Reserved.
 | |
| # ==================================================================
 | |
| #
 | |
| # Description: A general purpose perl interface to a RDBMS.
 | |
| #
 | |
| 
 | |
| package GT::SQL;
 | |
| # ==================================================================
 | |
| use GT::Base;
 | |
| use GT::AutoLoader;
 | |
| use GT::Config;
 | |
| use GT::SQL::Base;
 | |
| use GT::SQL::Table;
 | |
| use GT::SQL::Driver;
 | |
| use strict;
 | |
| use vars qw(@ISA $DEBUG $ERRORS $VERSION %OBJ_CACHE $error $errcode);
 | |
| 
 | |
| @ISA = qw(GT::SQL::Base);
 | |
| $DEBUG              = 0;
 | |
| $VERSION            = sprintf "%d.%03d", q$Revision: 1.112 $ =~ /(\d+)\.(\d+)/;
 | |
| $ERRORS = {
 | |
|     # Common Errors
 | |
|     UNIQUE       => "The column '%s' must be unique, and already has an entry '%s'",
 | |
|     NOTABLE      => 'No table defined -- call $db->table($table) before accessing',
 | |
|     CANTOPEN     => "Cannot open file '%s': %s",
 | |
|     CANTOPENDIR  => "Cannot read directory '%s': %s",
 | |
|     FILENOEXISTS => "File '%s' does not exist or the permissions are set incorrectly",
 | |
|     # GT::SQL Errors
 | |
|     NODRIVER     => "Database driver %s is not installed. Available drivers: %s",
 | |
|     CANTLOAD     => "Unable to load driver '%s': %s",
 | |
|     BADPREFIX    => "Invalid prefix: '%s'",
 | |
|     NODATABASE   => 'No database def file -- create def file with ->set_connect before calling $obj->%s',
 | |
|     CANTCONNECT  => "Could not connect to database: %s",
 | |
|     CANTPREPARE  => "Failed to prepare query: '%s': %s",
 | |
|     CANTEXECUTE  => "Failed to execute query: '%s': %s",
 | |
|     BADSUBCLASS  => "Unable to load subclass: '%s': %s",
 | |
|     NEEDDEBUG    => "You must turn on debug in order to access query logs",
 | |
|     NOORACLEHOME => "The environment variable ORACLE_HOME is not defined.  It must be defined for the script to connect properly",
 | |
|     NONLSDATE    => "Unable to set NLS_DATE_FORMAT: %s",
 | |
|     # Table Errors
 | |
|     BADNAME        => "Invalid table name '%s'",
 | |
|     NOTNULL        => "Column %s cannot be left blank",
 | |
|     NORECMOD       => "The record you are attempting to modify no longer exists in the current table",
 | |
|     NOVALUES       => "You did not pass any valid column names to %s",
 | |
|     BADMULTVALUES  => "One or more of the value groups passed to %s contained an incorrect number of values",
 | |
|     NOPKTOMOD      => "Cannot modify record, no primary key specified",
 | |
|     DEPENDENCY     => "Table %s has dependencies. Aborting",
 | |
|     ILLEGALVAL     => "%s cannot contain the value '%s'",
 | |
|     ALREADYCHANGED => "The record you are attempting to modify has changed since you last accessed it",
 | |
|     REGEXFAIL      => "The regular expressions %s for this column is not properly formed",
 | |
|     FKNOTABLE      => "A foreign key is referencing a non existant table: %s. GT::SQL load error: %s",
 | |
|     FKNOEXISTS     => "You attempted to remove non-existent foreign key '%s' from table '%s'",
 | |
|     FKMISSING      => "The '%s' table has a relationship with the '%s' table, but the foreign key information from the '%s' table is missing.",
 | |
|     CIRCULAR       => "Circular reference detected in the foreign key schema. Already seen column: %s",
 | |
|     CIRCULARLIMIT  => "Loop detected in circular reference check, hit maximum recursion depth of 100",
 | |
|     # Relation Errors
 | |
|     BADCOLS => "Bad columns / column clash: columns named '%s' have been found in current relation, please qualify your expression",
 | |
|     # Creator Errors
 | |
|     BADTYPE     => "%s is not a supported type",
 | |
|     AINOTPK     => "Column %s defined as auto_increment but is not an INT",
 | |
|     TBLEXISTS   => "Could not create table '%s': It already exists",
 | |
|     NOTABLEDEFS => "You must define your table before creating it",
 | |
|     NOPOS       => "No position column was found in definition for column: %s",
 | |
|     # Editor Errors
 | |
|     NOCOL          => "There is no column %s in this table",
 | |
|     REFCOL         => "You cannot alter column %s, as table %s still has references to it. Remove those references first",
 | |
|     NOPK           => "There is no primary key for this table",
 | |
|     COLREF         => "You cannot alter column %s, as it is a foreign key. Remove the foreign key first",
 | |
|     NOINDEX        => "You are trying to modify an index that does not exist",
 | |
|     NOUNIQUE       => "You are trying to drop a unique column '%s', but it is not unique",
 | |
|     INDXQTEXT      => "Cannot create index on '%s' as it is a text/blob field",
 | |
|     COLEXISTS      => "Unable to add column '%s' - already exists",
 | |
|     NOTUNIQUE      => "Cannot create unique index on '%s', data is not unique",
 | |
|     INDXEXISTS     => "Unable to add index '%s' - already exists",
 | |
|     PKTEXT         => "Column %s specified as a primary key but is a text or a blob type",
 | |
|     UNIQTEXT       => "Column %s specified as a unique but is a text or blob column type",
 | |
|     TABLEREFD      => "%s cannot be dropped as table still has references to it",
 | |
|     NOFILESAVEIN   => "Column %s must have file_save_in set if is to be File type",
 | |
|     NODIRPRIV      => "Privileges on directory %s do not allow write or directory does not exist",
 | |
|     SAMEDRIVER     => "Search Driver '%s' is unchanged",
 | |
|     NOTNULLDEFAULT => "Column %s was specified as not null, but has no default value",
 | |
|     # Admin Error
 | |
|     NOACTION => "The CGI object passed in did not contain a valid action. %s",
 | |
|     # Tree errors
 | |
|     NOTREE      => "No tree object exists for table '%s'. Create a tree first with \$editor->add_tree",
 | |
|     NOTREEOBJ   => "You attempted to call '%s' without a valid tree object. Call \$table->tree() first",
 | |
|     TREEEXISTS  => "A tree already exists for table '%s'",
 | |
|     TREENOCANDO => "You attempted to call '%s' on table '%s', but that table has a tree attached and does not support the command",
 | |
|     TREENOIDS   => "You did not pass any ID's to %s",
 | |
|     TREEBADPK   => "You tried to create a tree on table '%s', but that table doesn't have a primary key, or has multiple primary keys",
 | |
|     TREEBADJOIN => "Joining more than 2 tables with a tree is not supported. You attempted to join: %s",
 | |
|     TREEFATHER  => "Unable to update a tree record to a descendant of itself",
 | |
|     # Driver errors
 | |
|     DRIVERPROTOCOL => "Driver implements wrong protocol: protocol v%d required, driver is v%d",
 | |
| };
 | |
| 
 | |
| use constant DEF_HEADER => <<'HEADER';
 | |
| # Database access & configuration file
 | |
| # Last updated: [localtime]
 | |
| # Created by GT::SQL $Revision: 1.112 $
 | |
| HEADER
 | |
| 
 | |
| sub new {
 | |
| # -------------------------------------------------------------------
 | |
| # GT::SQL constructor. Takes:
 | |
| #       my $db = new GT::SQL '/path/to/def';
 | |
| #       my $db = new GT::SQL { def_path => '/defpath', debug => 1 };
 | |
| #
 | |
|     my $this    = shift;
 | |
|     my $class   = ref $this || $this;
 | |
|     my $self    = bless { _err_pkg => __PACKAGE__, _debug => $DEBUG }, $class;
 | |
| 
 | |
| # Get our arguments into a hash ref
 | |
|     my $opts = {};
 | |
|     if    (@_ == 0)                         { $opts = {};    }
 | |
|     elsif (@_ == 1 and ref $_[0] eq 'HASH') { $opts = shift; }
 | |
|     elsif (@_ > 1 and !(@_ % 2))            { $opts = {@_};  }
 | |
|     else {
 | |
|         $opts->{def_path} = shift;
 | |
|     }
 | |
| 
 | |
| # Set debugging level, caching options and whether to allow subclassing.
 | |
|     $self->{_debug}   = exists $opts->{debug} ? $opts->{debug} : $DEBUG;
 | |
|     $self->{cache}    = exists $opts->{cache} ? $opts->{cache} : 1;
 | |
|     $self->{subclass} = exists $opts->{subclass} ? $opts->{subclass} : 1;
 | |
| 
 | |
| # Def path must exist and be a directory
 | |
|     exists $opts->{def_path}    or return $self->fatal(BADARGS => "$class->new(HASH_REF). def_path must be defined and a directory path in the hash");
 | |
|     -d $opts->{def_path}        or return $self->fatal(BADARGS => "The defs directory '$opts->{def_path}' does not exist, or is not a directory");
 | |
| 
 | |
| # Load the database def file if it exists
 | |
| 
 | |
| # Some old programs would sometimes erroneously leave an invalid blank
 | |
| # database.def file in the def_path; if such a file exists, make GT::Config
 | |
| # ignore it.
 | |
|     my $empty = (-f "$opts->{def_path}/database.def" and !-s _);
 | |
| 
 | |
|     $self->{connect} = GT::Config->load(
 | |
|         "$opts->{def_path}/database.def" => {
 | |
|             create_ok => 1,
 | |
|             chmod => 0666,
 | |
|             debug => $self->{_debug},
 | |
|             header => DEF_HEADER,
 | |
|             ($empty ? (empty => 1) : ()),
 | |
|         }
 | |
|     );
 | |
| 
 | |
|     $self->{connect}->{PREFIX} = '' unless defined $self->{connect}->{PREFIX};
 | |
| # Heavily deprecated.  Not guaranteed to always be correct:
 | |
|     $GT::SQL::PREFIX = $self->{connect}->{PREFIX};
 | |
|     $self->{connect}->{def_path} = $opts->{def_path};
 | |
|     $self->{connect}->{obj_cache} = $self->{cache};
 | |
| 
 | |
|     $self->debug("OBJECT CREATED") if $self->{_debug} and $self->{_debug} > 2;
 | |
|     return $self;
 | |
| }
 | |
| 
 | |
| $COMPILE{set_connect} = __LINE__ . <<'END_OF_SUB';
 | |
| sub set_connect {
 | |
| # -------------------------------------------------------------------
 | |
| # Sets the connection info, only needed to setup the database.def file.
 | |
| #     $db->set_connect({
 | |
| #         driver => 'mysql',
 | |
| #         host   => 'localhost',
 | |
| #         port   => 2323,
 | |
| #         database => 'mydatabase',
 | |
| #         login    => 'user',
 | |
| #         password => 'foo',
 | |
| #     }) or die "Can't connect: $GT::SQL::error";
 | |
| #
 | |
|     my $self = shift;
 | |
|     my $connect = $self->{connect};
 | |
|     my %old_connect = %$connect;
 | |
| # Parse our arguments.
 | |
|     if (!@_) { return $self->fatal(BADARGS => '$obj->set_connect(HASH_REF)') }
 | |
|     elsif (@_ == 1 and ref $_[0] eq 'HASH') { %$connect = %{+shift} }
 | |
|     elsif (@_ % 2 == 0) { %$connect = @_ }
 | |
|     else { return $self->fatal(BADARGS => '$obj->set_connect(HASH_REF)') }
 | |
| 
 | |
|     if (keys %old_connect) {
 | |
|         for (keys %old_connect) {
 | |
|             $connect->{$_} = $old_connect{$_} unless exists $connect->{$_};
 | |
|         }
 | |
|     }
 | |
|     $connect->{PREFIX} = '' unless defined $connect->{PREFIX};
 | |
| 
 | |
| # Fix the connect string for test connecting
 | |
|     $connect->{driver} ||= 'mysql';
 | |
| 
 | |
| # Make sure DBI has been loaded
 | |
|     eval { require DBI };
 | |
|     $@ and return $self->warn(CANTCONNECT => "DBI module not installed.  You must install the perl database module DBI from: http://www.perl.com/CPAN/modules/by-module/DBI");
 | |
| 
 | |
| # Make sure the requested driver exists
 | |
|     my @drivers = GT::SQL::Driver->available_drivers;
 | |
|     unless (grep $_ eq uc $connect->{driver}, @drivers, 'ODBC') {
 | |
|         return $self->warn(NODRIVER => $connect->{driver}, join ", ", @drivers);
 | |
|     }
 | |
| 
 | |
|     my $raiseerror = delete $connect->{RaiseError};
 | |
|     my $printerror = delete $connect->{PrintError};
 | |
|     $connect->{RaiseError} = 0;
 | |
|     $connect->{PrintError} = 0;
 | |
| 
 | |
| # Get our driver.
 | |
|     my $table = GT::SQL::Table->new(connect => $connect, debug => $self->{_debug});
 | |
|     $table->connect or return;
 | |
| 
 | |
| # Put things back the way they were.
 | |
|     $connect->{RaiseError} = defined $raiseerror ? $raiseerror : 1;
 | |
|     $connect->{PrintError} = defined $printerror ? $printerror : 0;
 | |
| 
 | |
|     $self->{connect} = $connect;
 | |
| 
 | |
| # Use this connect string from now on.
 | |
|     $self->write_db_config;
 | |
| 
 | |
|     return 1;
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{write_db_config} = __LINE__ . <<'END_OF_SUB';
 | |
| sub write_db_config {
 | |
| # -------------------------------------------------------------------
 | |
| # Saves the database.def file. Takes no arguments.
 | |
| #
 | |
|     my $self = shift;
 | |
|     $self->{connect}->save;
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| # ============================================================================ #
 | |
| #  DATABASE INFO ACCESSORS                                                     #
 | |
| # ============================================================================ #
 | |
| $COMPILE{driver} = __LINE__ . <<'END_OF_SUB';
 | |
| sub driver {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns the name of the driver being used.
 | |
| #
 | |
|     my $self = shift;
 | |
|     return $self->{connect}->{driver};
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{host} = __LINE__ . <<'END_OF_SUB';
 | |
| sub host {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns the name of the host being used.
 | |
| #
 | |
|     my $self = shift;
 | |
|     return $self->{connect}->{host};
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{port} = __LINE__ . <<'END_OF_SUB';
 | |
| sub port {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns the port currently being used, undef if default.
 | |
| #
 | |
|     my $self = shift;
 | |
|     return $self->{connect}->{port};
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{database} = __LINE__ . <<'END_OF_SUB';
 | |
| sub database {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns the name of the database being used.
 | |
| #
 | |
|     my $self = shift;
 | |
|     return $self->{connect}->{database};
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{login} = __LINE__ . <<'END_OF_SUB';
 | |
| sub login {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns the login username for the current connection.
 | |
| #
 | |
|     my $self = shift;
 | |
|     return $self->{connect}->{login};
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{password} = __LINE__ . <<'END_OF_SUB';
 | |
| sub password {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns the login password for the current connection.
 | |
| #
 | |
|     my $self = shift;
 | |
|     return $self->{connect}->{password};
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| # ============================================================================ #
 | |
| #  HTML ACCESSSOR                                                              #
 | |
| # ============================================================================ #
 | |
| 
 | |
| $COMPILE{html} = __LINE__ . <<'END_OF_SUB';
 | |
| sub html {
 | |
| # -------------------------------------------------------------------
 | |
| # Return an html object. Takes an array ref of table names, or a, and a cgi
 | |
| # object.
 | |
| #       my $html = $db->html(['Links'], $in);
 | |
| #           or
 | |
| #       my $html = $db->html($table_obj, $in);
 | |
| #
 | |
|     my ($self, $tables, $cgi) = @_;
 | |
|     ref $tables or return $self->fatal(BADARGS => 'Error: no table array ref passed to html');
 | |
|     ref $cgi    or return $self->fatal(BADARGS => 'Error: no cgi object/hash ref passed to html');
 | |
| 
 | |
| # If already passed a table object, use it, otherwise create a new one
 | |
|     my ($table);
 | |
|     if (ref $tables eq 'ARRAY') {
 | |
|         $table = $self->table(@$tables);
 | |
|     }
 | |
|     elsif (UNIVERSAL::isa($tables, 'GT::SQL::Table') or UNIVERSAL::isa($tables, 'GT::SQL::Relation')) {
 | |
|         $table = $tables;
 | |
|     }
 | |
|     else {
 | |
|         return $self->fatal(BADARGS => "Error: '$tables' must be either an array ref or a table object");
 | |
|     }
 | |
| 
 | |
|     my $meth = @{[$table->name]} > 1 ? "_html_relation" : "_html_table";
 | |
|     $self->$meth($table, $cgi);
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{_html_relation} = __LINE__ . <<'END_OF_SUB';
 | |
| sub _html_relation {
 | |
|     my ($self, $rel, $cgi) = @_;
 | |
| 
 | |
|     my $class;
 | |
|     my $key = join "\0", map { s/^$self->{connect}->{PREFIX}//; $_ } sort keys %{$rel->{tables}};
 | |
|     foreach my $table (values %{$rel->{tables}}) {
 | |
|         my $subclass = $table->subclass;
 | |
|         if ($self->{subclass} and exists $subclass->{html}->{$self->{connect}->{PREFIX} . $key}) {
 | |
|             $class = $subclass->{html}->{$self->{connect}->{PREFIX} . $key};
 | |
|             $self->_load_module($class) or return;
 | |
|             last;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (!$class) {
 | |
|         require GT::SQL::Display::HTML::Relation;
 | |
|         $class = 'GT::SQL::Display::HTML::Relation';
 | |
|     }
 | |
|     return $class->new(
 | |
|         db    => $rel,
 | |
|         input => $cgi
 | |
|     );
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{_html_table} = __LINE__ . <<'END_OF_SUB';
 | |
| sub _html_table {
 | |
|     my ($self, $table, $cgi) = @_;
 | |
|     my $class;
 | |
|     if ($self->{subclass} and $table->{schema}->{subclass}->{html}->{$table->name}) {
 | |
|         $class = $table->{schema}->{subclass}->{html}->{$table->name};
 | |
|         $self->_load_module($class) or return;
 | |
|     }
 | |
|     if (!$class) {
 | |
|         require GT::SQL::Display::HTML::Table;
 | |
|         $class = 'GT::SQL::Display::HTML::Table';
 | |
|     }
 | |
|     return $class->new(
 | |
|         db    => $table,
 | |
|         input => $cgi
 | |
|     );
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| sub query_stack {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns raw query stack (as array/array ref).
 | |
| #
 | |
|     return wantarray ? @GT::SQL::Driver::debug::QUERY_STACK : \@GT::SQL::Driver::debug::QUERY_STACK;
 | |
| }
 | |
| 
 | |
| sub query_stack_disp {
 | |
| # -------------------------------------------------------------------
 | |
| # Returns formatted query stack (handled in Driver.pm).
 | |
| #
 | |
|     my ($out, $i) = ('', 0);
 | |
|     foreach (reverse 0 .. $#GT::SQL::Driver::debug::QUERY_STACK) {
 | |
|         my $query = $GT::SQL::Driver::debug::QUERY_STACK[$_];
 | |
|         my $stack = $GT::SQL::Driver::debug::STACK_TRACE[$_] || '';
 | |
|         $i++;
 | |
|         chomp $query;
 | |
|         $query =~ s/^[\s]*(.*?)[\s]*$/$1/mg;
 | |
|         $query =~ s/\n/\n        /mg;
 | |
|         $out .= "$i: $query\n$stack";
 | |
|     }
 | |
|     return $out;
 | |
| }
 | |
| 
 | |
| 
 | |
| $COMPILE{prefix} = __LINE__ . <<'END_OF_SUB';
 | |
| sub prefix {
 | |
| # -------------------------------------------------------------------
 | |
| # Set/Get the database prefix to be attached to all tables.  Calling this as a
 | |
| # class accessor method is extremely deprecated (it returns $GT::SQL::PREFIX,
 | |
| # which is itself extremely deprecated); calling this to *set* a prefix is not
 | |
| # permitted.
 | |
| #
 | |
| 
 | |
|     my $self = shift;
 | |
| 
 | |
|     if (@_) {
 | |
|         ref $self or $self->fatal(BADARGS => 'Usage: $obj->prefix(...) not CLASS->prefix(...)');
 | |
|         my $prefix = shift;
 | |
|         if ($prefix =~ /\W/) {
 | |
|             return $self->fatal(BADPREFIX => $prefix);
 | |
|         }
 | |
|         $self->{connect}->{PREFIX} = $prefix;
 | |
|     }
 | |
|     else {
 | |
|         return ref $self ? $self->{connect}->{PREFIX} : $GT::SQL::PREFIX;
 | |
|     }
 | |
|     return 1;
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| $COMPILE{reset_env} = __LINE__ . <<'END_OF_SUB';
 | |
| sub reset_env {
 | |
| # -------------------------------------------------------------------
 | |
| # Reset globals.
 | |
| #
 | |
|     GT::SQL::Driver->reset_env(); # Shut down database connections.
 | |
|     %OBJ_CACHE = ();
 | |
|     $error     = '';
 | |
|     $errcode   = '';
 | |
| }
 | |
| END_OF_SUB
 | |
| 
 | |
| 1;
 | |
| 
 | |
| __END__
 | |
| 
 | |
| =head1 NAME
 | |
| 
 | |
| GT::SQL - A database independent perl interface
 | |
| 
 | |
| =head1 SYNOPSIS
 | |
| 
 | |
|     use GT::SQL;
 | |
| 
 | |
|     my $db      = GT::SQL->new('/path/to/def');
 | |
|     my $table   = $db->table('Links');
 | |
|     my $editor  = $db->editor('Links');
 | |
|     my $creator = $db->creator('NewTable');
 | |
|     my $html    = $db->html('Links', new CGI);
 | |
| 
 | |
| =head1 DESCRIPTION
 | |
| 
 | |
| GT::SQL is a perl database abstraction layer to relational databases, providing
 | |
| a native Perl interface rather than a query-based interface.
 | |
| 
 | |
| A GT::SQL object provides the interface to the entire database by providing
 | |
| objects that are able to perform the work needed.
 | |
| 
 | |
| =head2 Creating a new GT::SQL object
 | |
| 
 | |
| There are two ways to get a GT::SQL object. First, you can simply provide the
 | |
| path to the def file directory where GT::SQL stores all it's information:
 | |
| 
 | |
|     $db = GT::SQL->new('/path/to/def');
 | |
| 
 | |
| or you can pass in a hash or hash ref and specify options:
 | |
| 
 | |
|     $db = GT::SQL->new(
 | |
|         def_path => '/path/to/def',
 | |
|         cache    => 1,
 | |
|         debug    => 1,
 | |
|         subclass => 1
 | |
|     );
 | |
| 
 | |
| You must specify def_path. Setting C<cache =E<gt> 1> will result in all table
 | |
| and relation objects being cached, which provides a performance improvement in
 | |
| any situation where the same table or relation is used again.
 | |
| 
 | |
| Specifying C<subclass =E<gt> 0> or C<subclass =E<gt> 1> will enable or disable
 | |
| the ability to subclass any of the objects GT::SQL creates. The default
 | |
| value is C<1>, and should not normally be changed.
 | |
| 
 | |
| GT::SQL has significant amounts of debugging output that can be enabled by
 | |
| specifying a value of C<1> to the C<debug> option.  Larger values can be
 | |
| specified for more detailed debugging output, however a level of C<1> is almost
 | |
| always more than sufficient.  The accepted values are as follows:
 | |
| 
 | |
| =over 4
 | |
| 
 | |
| =item Level 0
 | |
| 
 | |
| This is the default, no debugging information is printed to stderr. All errors
 | |
| can be obtained in $GT::SQL::error.
 | |
| 
 | |
| =item Level 1
 | |
| 
 | |
| All queries will be displayed to stderr.  This is the recommended value if
 | |
| query debugging is desired.
 | |
| 
 | |
| =item Level 2
 | |
| 
 | |
| Same as level 1, but includes more detailed information.  Also, when calling
 | |
| query_stack you get a stack trace on what generated each query.  Not
 | |
| recommended except when working directly on GT::SQL.
 | |
| 
 | |
| =item Level 3
 | |
| 
 | |
| Very detailed debug logs including creation and destruction of objects.
 | |
| query_stack generates a javascript page with query, stack trace, and data dump
 | |
| of arguments, but can be extremely large.  Not recommended except for debugging
 | |
| GT::SQL internals.
 | |
| 
 | |
| =back
 | |
| 
 | |
| B<Pass in a def path>
 | |
| 
 | |
|     $obj = GT::SQL->new('/path/to/def/directory');
 | |
| 
 | |
| This method of calling new is also supported, however has the drawback that
 | |
| none of the above options can be provided.
 | |
| 
 | |
| =head2 Getting Connected
 | |
| 
 | |
| GT::SQL loads the database connection info from database.def which is located
 | |
| in the defs directory.
 | |
| 
 | |
| To create this file, you call set_connect() as follows:
 | |
| 
 | |
|     $obj->set_connect({
 | |
|         driver     => 'mysql',
 | |
|         host       => 'localhost',
 | |
|         port       => 3243,
 | |
|         database   => 'databasename',
 | |
|         login      => 'username',
 | |
|         password   => 'password',
 | |
|         PREFIX     => 'prefix_'
 | |
|     });
 | |
| 
 | |
| This will test the database information, and save it to the def file. All
 | |
| future connections will automatically use this connection information.
 | |
| 
 | |
| Not all of the arguments in this hash are necessary; some have reasonable
 | |
| defaults for the connection.
 | |
| 
 | |
| =over 4
 | |
| 
 | |
| =item driver
 | |
| 
 | |
| This needs to be the driver that is being used for the connection. The default
 | |
| for this is C<mysql>.  Driver names are case-insensitive.  Available drivers
 | |
| are:
 | |
| 
 | |
| =over 4
 | |
| 
 | |
| =item MySQL
 | |
| 
 | |
| Driver for MySQL databases.  Requires that the DBD::mysql module be installed.
 | |
| 
 | |
| =item Pg
 | |
| 
 | |
| Driver for PostgreSQL databases.  Requires that the DBD::Pg module be
 | |
| installed.
 | |
| 
 | |
| =item MSSQL
 | |
| 
 | |
| Driver for MSSQL 7.0 and above.  Requires that the DBD::ODBC module be
 | |
| installed.
 | |
| 
 | |
| =item Oracle
 | |
| 
 | |
| Driver for Oracle 8 and above.  Requires the DBD::Oracle module.
 | |
| 
 | |
| =back
 | |
| 
 | |
| =item host
 | |
| 
 | |
| This will specify the host to connect to. The default, which is acceptable for
 | |
| most installations, is C<localhost>.
 | |
| 
 | |
| =item port
 | |
| 
 | |
| This is the port on which to connect to the SQL server.  The default for this
 | |
| is to allow the DBI driver to choose the default, which is almost always the
 | |
| appropriate choice.
 | |
| 
 | |
| =item database
 | |
| 
 | |
| This is the database name to use on the SQL server.  This is required to
 | |
| connect.  For MSSQL, this is the I<Data Source> name.
 | |
| 
 | |
| =item PREFIX
 | |
| 
 | |
| This specifies a prefix to use for table names.  See the L</"Table Prefixes">
 | |
| section below for more information.
 | |
| 
 | |
| =back
 | |
| 
 | |
| =head2 Supported Objects
 | |
| 
 | |
| The following objects can be obtained through a GT::SQL object:
 | |
| 
 | |
| =over 4
 | |
| 
 | |
| =item Table/Relation
 | |
| 
 | |
| To get a table or relation object for working with SQL tables, you should call:
 | |
| 
 | |
|     my $table = $db->table('table_name');
 | |
| 
 | |
| or for a table join:
 | |
| 
 | |
|     my $relation = $db->table('table_name', 'other_table');
 | |
| 
 | |
| See L<GT::SQL::Table> for more information on how to use a table object.
 | |
| 
 | |
| =item Creator
 | |
| 
 | |
| To create new tables, you need to use a creator. You can get one by calling:
 | |
| 
 | |
|     my $creator = $db->creator('new_table');
 | |
| 
 | |
| where C<new_table> is the name of the table you wish to create.  See
 | |
| L<GT::SQL::Creator> for more information on how to use a creator object.
 | |
| 
 | |
| =item Editor
 | |
| 
 | |
| To edit existing tables (i.e. add/drop/change columns, add/drop indexes, etc.)
 | |
| you need an editor object:
 | |
| 
 | |
|     my $editor = $db->editor('existing_table');
 | |
| 
 | |
| where C<existing_table> is the name of the table you wish the modify.  See
 | |
| L<GT::SQL::Editor> for more information on how to use an editor object.
 | |
| 
 | |
| =item HTML
 | |
| 
 | |
| To get an html object for generating forms and html output, you need to pass in
 | |
| the table/relation object you want to work with, and a cgi object:
 | |
| 
 | |
|     my $html = $db->html($table, $cgi);
 | |
| 
 | |
| The html object uses information found in CGI to set values, etc.  See
 | |
| L<GT::SQL::Display::HTML> for more information on how to use a html object.
 | |
| 
 | |
| =back
 | |
| 
 | |
| =head2 Table Prefixes
 | |
| 
 | |
| GT::SQL supports the concept of table prefixes. If you specify a prefix using
 | |
| the accessor, it is saved in the database.def file and will be used in all
 | |
| future calls to table(), editor() and creator().
 | |
| 
 | |
| To set a prefix:
 | |
| 
 | |
|     $db->prefix("foo");
 | |
| 
 | |
| to get the current prefix:
 | |
| 
 | |
|     my $prefix = $db->prefix;
 | |
| 
 | |
| What this will do is transparently prepend C<foo> to the beginning of every
 | |
| table name.  This means anywhere you access the table C<bar>, the actual table
 | |
| stored on the SQL server will be C<foobar>.  Note that the prefix should B<not>
 | |
| be included when getting table/creator/editor/etc. objects - the prefix is
 | |
| handled completely transparently to all public GT::SQL functionality.
 | |
| 
 | |
| =head2 Query Stack
 | |
| 
 | |
| To display a list of all raw SQL queries sent to the database you can use:
 | |
| 
 | |
|     my @queries = $db->query_stack;
 | |
| 
 | |
| or to have them formatted try
 | |
| 
 | |
|     print $db->query_stack_disp;
 | |
| 
 | |
| which will join them up, displayed nicely. This is also available as a class
 | |
| method:
 | |
| 
 | |
|     print GT::SQL->query_stack_disp;
 | |
| 
 | |
| =head1 SEE ALSO
 | |
| 
 | |
| L<GT::SQL::Table>
 | |
| 
 | |
| L<GT::SQL::Editor>
 | |
| 
 | |
| L<GT::SQL::Creator>
 | |
| 
 | |
| L<GT::SQL::Types>
 | |
| 
 | |
| L<GT::SQL::Admin>
 | |
| 
 | |
| L<GT::SQL::Display::HTML>
 | |
| 
 | |
| =head1 COPYRIGHT
 | |
| 
 | |
| Copyright (c) 2004 Gossamer Threads Inc.  All Rights Reserved.
 | |
| http://www.gossamer-threads.com/
 | |
| 
 | |
| =head1 VERSION
 | |
| 
 | |
| Revision: $Id: SQL.pm,v 1.112 2007/08/30 00:14:38 brewt Exp $
 | |
| 
 | |
| =cut
 | 
