cvsuser 02/01/04 08:41:05
Added: P5EEx/Blue/P5EEx/Blue/Repository DBI.pm SOAP.pm Sample.pm
Log:
added more missing files
Revision Changes Path
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/Repository/DBI.pm
Index: DBI.pm
===================================================================
######################################################################
## File: $Id: DBI.pm,v 1.1 2002/01/04 16:41:05 spadkins Exp $
######################################################################
use Repository;
package Repository::DBI;
$VERSION = do { my @r=(q$Revision: 1.1 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
use Repository::Base;
@ISA = ( "Repository::Base" );
use strict;
=head1 NAME
Repository::DBI - a repository which relies on a DBI interface to a relational
database
=head1 SYNOPSIS
use Repository::DBI;
(see man page for Repository::Base for additional methods)
$rep = Repository::DBI->new(); # looks for %ENV, then config file
$rep = Repository::DBI->new("mysql","mydb","user001","pass001");
$rep = Repository::DBI->new("mysql","mydb","user001","pass001","port=3307");
$rep =
Repository::DBI->new("mysql","mydb","user001","pass001","port=3307","user001");
###################################################################
# methods not defined in the Repository::Base interface
###################################################################
# UTILITY FUNCTIONS: to support the next level of functionality
$select_sql = $rep->mk_select_rows_sql($table, \@cols, \@params,
\%paramvalues, \@ordercols);
$insert_sql = $rep->mk_insert_row_sql ($table, \@cols, \@colvalues);
$update_sql = $rep->mk_update_row_sql ($table, \@cols, \@colvalues, \@keycolidx);
$update_sql = $rep->mk_update_rows_sql($table, \@cols, \@colvalues, \@params,
\%paramvalues);
$delete_sql = $rep->mk_delete_row_sql ($table, \@cols, \@colvalues, \@keycolidx);
$delete_sql = $rep->mk_delete_rows_sql($table, \@params,
\%paramvalues);
###################################################################
# defined in the Repository::Base interface, implemented here
###################################################################
$ok = $rep->connect(); # initialize repository (will happen automatically
in constructor)
$ok = $rep->disconnect(); # cleanup repository (will happen automatically in
destructor)
$rep->is_connected(); # returns 1 if connected (ready for use), 0 if not
$errmsg = $rep->error(); # returns the error string for prev op ("" if no
error)
$numrows = $rep->numrows(); # returns the number of rows affected by prev op
print $rep->error(), "\n" if (!$rep->connect());
print $rep->error(), "\n" if ($rep->connect() != $rep->OK);
# META-DATA: (about the tables)
$rep->load_rep_metadata_auto();
$rep->load_table_metadata_auto($tablename);
# MEDIUM-LEVEL: reads and writes rows, no caching
$row = $rep->select_row ($table, \@cols, \@params,
\%paramvalues);
$rows = $rep->select_rows($table, \@cols, \@params,
\%paramvalues, \@ordercols, $startrow, $endrow);
$ok = $rep->insert_row ($table, \@cols, \@colvalues);
$ok = $rep->insert_rows($table, \@cols, \@rows);
$ok = $rep->update_row ($table, \@cols, \@colvalues, \@keycolidx);
$ok = $rep->update_rows($table, \@cols, \@colvalues, \@params,
\%paramvalues);
$ok = $rep->store_row ($table, \@cols, \@colvalues, \@keycolidx,
$update_first);
$ok = $rep->store_rows ($table, \@cols, \@rows, \@keycolidx,
$update_first);
$ok = $rep->delete_row ($table, \@cols, \@colvalues, \@keycolidx);
$ok = $rep->delete_rows($table, \@params,
\%paramvalues);
=cut
######################################################################
# CONSTANTS
######################################################################
######################################################################
# ATTRIBUTES
######################################################################
# CONNECTION ATTRIBUTES
# $self->{dbidriver} # standard DBI driver name ("mysql", "Oracle", etc.)
# $self->{dbname} # the name of the database
# $self->{dbuser} # database user name
# $self->{dbpass} # database password
# $self->{dbschema} # name of the schema within the database
# $self->{dbioptions} # additional dbi options to tack onto the dsn
# $self->{dsn} # DBI connect string
# $self->{attr} # attributes of a connection
# $self->{dbh} # open DBI database handle
######################################################################
# INHERITED ATTRIBUTES
######################################################################
# BASIC
# $self->{name} # name of this repository (often "db")
# $self->{config} # hash of config file data
# CURRENT STATE
# $self->{error} # most recent error generated from this module
# $self->{numrows}
# METADATA - Database Types
# $self->{types}
# $self->{type}{$type}{name}
# $self->{type}{$type}{num}
# $self->{type}{$type}{type}
# $self->{type}{$type}{column_size}
# $self->{type}{$type}{literal_prefix}
# $self->{type}{$type}{literal_suffix}
# $self->{type}{$type}{unsigned_attribute}
# $self->{type}{$type}{auto_unique_value}
# $self->{type}{$type}{quoted}
# METADATA - Tables and Columns
# $self->{table_names}
# $self->{table}{$table}{readonly}
# $self->{table}{$table}{columns}
# $self->{table}{$table}{column}{$column}
# $self->{table}{$table}{column}{$column}{name}
# $self->{table}{$table}{column}{$column}{type_name}
# $self->{table}{$table}{column}{$column}{type}
# $self->{table}{$table}{column}{$column}{notnull}
# $self->{table}{$table}{column}{$column}{quoted}
=head1 DESCRIPTION
The Repository::DBI class encapsulates all access to the database,
changing SQL statements into get(), save(), and delete() methods.
=cut
######################################################################
# INITIALIZATION
######################################################################
sub init {
my $self = shift;
my ($name, $repconf);
$name = $self->{name};
$repconf = $self->{repconfig};
if (defined $repconf->{dbh}) {
$self->{dbh} = $repconf->{dbh};
$self->{preconnected} = 1;
}
}
######################################################################
# CONFIG METHODS
######################################################################
# take positional args and turn them into a %config structure
sub make_config {
my ($self, $dbidriver, $dbname, $dbuser, $dbpass, $dbioptions, $dbschema) = @_;
my ($conf, $repconf);
$repconf = {};
$repconf->{dbidriver} = $dbidriver;
$repconf->{dbname} = $dbname;
$repconf->{dbuser} = $dbuser;
$repconf->{dbpass} = $dbpass;
$repconf->{dbioptions} = $dbioptions;
$repconf->{dbschema} = $dbschema;
$conf = {
'repository' => {
'db' => $repconf,
},
};
$conf;
}
sub read_config {
my $self = shift;
if ($ENV{DBIDRIVER} && $ENV{DBNAME}) {
return $self->make_config(
$ENV{DBIDRIVER},
$ENV{DBNAME},
$ENV{DBUSER},
$ENV{DBPASS},
$ENV{DBIOPTIONS},
$ENV{DBSCHEMA}
);
}
else {
return ($self->SUPER::read_config());
}
}
######################################################################
# CONNECTION METHODS
######################################################################
sub connect {
my $self = shift;
my ($repname, $repconf);
$repname = $self->{name};
$repconf = $self->{config}{repository}{$repname};
if (!defined $self->{dbh}) {
if (!defined $self->{dsn}) {
my ($dbidriver, $dbname, $dbuser, $dbpass, $dbioptions, $dbschema);
$dbidriver = $repconf->{dbidriver};
$dbname = $repconf->{dbname};
$dbuser = $repconf->{dbuser};
$dbpass = $repconf->{dbpass};
$dbioptions = $repconf->{dbioptions};
$dbschema = $repconf->{dbschema};
print STDERR "ERROR: missing DBI driver and/or db name
[$dbidriver,$dbname] in configuration.\n"
if (!$dbidriver || !$dbname);
$self->{dbidriver} = $dbidriver;
$self->{dbname} = $dbname;
$self->{dbuser} = $dbuser;
$self->{dbpass} = $dbpass;
$self->{dbioptions} = $dbioptions;
$self->{dbschema} = $dbschema;
$self->{dsn} = "dbi:${dbidriver}:database=${dbname}";
# enforce driver-specific rules here
if ($dbidriver eq "mysql") {
# force an update of a row to report that is was found (even if the
values didn't change)
$self->{dsn} .= ";mysql_client_found_rows=true";
$self->{attr} = { "PrintError" => 0, "AutoCommit" => 1 };
}
else {
$self->{attr} = { "PrintError" => 0, "AutoCommit" => 0 };
}
}
$self->{dbh} = DBI->connect($self->{dsn}, $self->{dbuser}, $self->{dbpass},
$self->{attr});
}
return(defined $self->{dbh});
}
sub disconnect {
my $self = shift;
if (defined $self->{dbh} && !($self->{preconnected})) {
my $dbh = $self->{dbh};
$dbh->disconnect;
delete $self->{dbh};
}
1;
}
sub is_connected {
my $self = shift;
if (defined $self->{dbh}) { return 1; }
return 0;
}
sub error {
my $err = $_[0]->{dbh}->errstr || $_[0]->{error};
$err ? $err : "";
}
######################################################################
# SQL EXECUTE METHODS (new methods not defined in Repository::Base)
######################################################################
# $colvararr = $rep->exec_select_row($sql);
sub exec_select_row {
my ($self, $sql) = @_;
my (@row, $dbh, $sth, $rc);
if (!defined $self->{dbh}) {
$self->{error} = "Not connected to database";
return ()
}
$dbh = $self->{dbh};
delete $self->{error};
$self->{sql} = $sql;
$sth = $dbh->prepare($sql);
if ($DBI::err) {
return();
}
$rc = $sth->execute;
if ($DBI::err) {
return();
}
@row = $sth->fetchrow;
if ($DBI::err) {
return();
}
$sth->finish;
if ($DBI::err) {
return();
}
\@row;
}
# @colvararr = $rep->exec_select($sql);
sub exec_select {
my ($self, $sql, $startrow, $endrow) = @_;
my (@rows, @row, $dbh, $sth, $rc, $rownum);
if (!defined $self->{dbh}) {
$self->{error} = "Not connected to database";
return ()
}
$dbh = $self->{dbh};
delete $self->{error};
$self->{sql} = $sql;
$sth = $dbh->prepare($sql);
if ($DBI::err) {
return();
}
$rc = $sth->execute;
if ($DBI::err) {
return();
}
$rownum = 0;
while (1) {
@row = $sth->fetchrow;
$rownum++;
if ($DBI::err) {
return();
}
if ($#row < 0 || ($endrow > 0 && $rownum > $endrow)) {
$sth->finish;
if ($DBI::err) {
return();
}
last;
}
if ($rownum >= $startrow) {
push(@rows, [@row]);
}
}
\@rows;
}
# $ok = $rep->exec_sql($sql);
sub exec_sql {
my ($self, $sql) = @_;
my (@data, @row, $dbh, $sth, $rc);
if (!defined $self->{dbh}) {
$self->{error} = "Not connected to database";
return 0
}
$dbh = $self->{dbh};
delete $self->{error};
$self->{numrows} = 0;
$self->{sql} = $sql;
$sth = $dbh->prepare($sql);
if ($DBI::err) {
return(0);
}
$rc = $sth->execute;
$self->{numrows} = $rc;
if ($DBI::err) {
return(0);
}
$sth->finish;
if ($DBI::err) {
return(0);
}
1;
}
######################################################################
# SQL CREATE METHODS (new methods not defined in Repository::Base)
######################################################################
sub mk_where_clause {
my ($self, $table, $paramref, $paramvaluehashref) = @_;
my ($where, $column, $colstr, $value, $colnum, $sqlop, $column_def);
my $tabcolref = $self->{table}{$table}{column};
my %op = (
'contains' => 'like',
'eq' => '=',
'ne' => '!=',
'le' => '<=',
'lt' => '<',
'ge' => '>=',
'gt' => '>',
'in' => 'in',
);
$where = "";
if (!defined $paramref && ref($paramvaluehashref) eq "HASH") {
$paramref = [ (keys %$paramvaluehashref) ];
}
if (defined $paramref && $#$paramref > -1) {
for ($colnum = 0; $colnum <= $#$paramref; $colnum++) {
$colstr = $paramref->[$colnum];
$column = $colstr;
$sqlop = "=";
# check if $column contains an embedded operation, i.e. "name.eq",
"name.contains"
if ($colstr =~ /^(.*)\.([^.]+)$/) {
if ($op{$2}) {
$column = $1;
$sqlop = $op{$2};
}
}
$column_def = $tabcolref->{$column};
next if (!defined $column_def); # skip if the column is unknown
if (! defined $paramvaluehashref->{$colstr}) {
$value = "?"; # TODO: make this work with the "contains" operator
}
else {
$value = $paramvaluehashref->{$colstr};
if ($tabcolref->{$column}{quoted}) {
$value =~ s/'/\\'/g;
$value = ($sqlop eq "like") ? "'%$value%'" : "'$value'";
}
}
$where .= ($colnum == 0) ? "where $column $sqlop $value\n" : " and
$column $sqlop $value\n";
}
}
$where;
}
# $select_sql = $rep->mk_select_rows_sql($table, \@cols, \@params, \%paramvalues,
\@ordercols);
sub mk_select_rows_sql {
my ($self, $table, $colref, $paramref, $paramvaluehashref, $ordercolref) = @_;
$self->load_table_metadata($table) if (!defined $self->{table}{$table}{loaded});
my ($sql, $col, $colnum);
if ($#$colref == -1) {
$self->{error} = "Database->mk_select_rows_sql(): no columns specified";
return();
}
$sql = "select\n " . join(",\n ", @$colref) . "\nfrom $table\n";
$sql .= $self->mk_where_clause($table, $paramref, $paramvaluehashref);
if (defined $ordercolref && $#$ordercolref > -1) {
for ($colnum = 0; $colnum <= $#$ordercolref; $colnum++) {
$col = $ordercolref->[$colnum];
$sql .= ($colnum == 0) ? "order by\n $col" : ",\n $col";
}
$sql .= "\n";
}
$sql;
}
# $sql = $rep->mk_select_joined_rows_sql($table, \@columns,
# \@params, \%paramvalues, \@ordercols,
# $startrow, $endrow,
# \@sortdircols, \@keycolidx, \@writeable, \@columntype,
\@summarykeys);
sub mk_select_joined_rows_sql {
my ($self, $table, $columnref, $paramref, $paramvaluehref, $ordercolref,
$startrow, $endrow,
$sortdircolref, $keycolidxref, $writeref, $reptyperef, $summarykeyref) = @_;
if ($Repository::DEBUG &&
Repository->dbg(ref($self),"mk_select_joined_rows_sql")) {
Repository->dbgprint("mk_select_joined_rows_sql($table, col=$columnref,
p=$paramref, pv=$paramvaluehref, oc=$ordercolref start=$startrow end=$endrow
dir=$sortdircolref, key=$keycolidxref, w=$writeref, typ=$reptyperef,
sum=$summarykeyref)");
Repository->dbgprint(" columnref=[", join(",",@$columnref), "]")
if ($columnref);
Repository->dbgprint(" paramref=[", join(",",@$paramref), "]")
if ($paramref);
Repository->dbgprint(" paramvaluehref=[",join(",",%$paramvaluehref),"]")
if ($paramvaluehref);
Repository->dbgprint(" ordercolref=[", join(",",@$ordercolref), "]")
if ($ordercolref);
Repository->dbgprint(" sortdircolref=[", join(",",@$sortdircolref), "]")
if ($sortdircolref);
Repository->dbgprint(" keycolidxref=[", join(",",@$keycolidxref), "]")
if ($keycolidxref);
Repository->dbgprint(" writeref=[", join(",",@$writeref), "]")
if ($writeref);
Repository->dbgprint(" reptyperef=[", join(",",@$reptyperef), "]")
if ($reptyperef);
Repository->dbgprint(" summarykeyref=[", join(",",@$summarykeyref), "]")
if ($summarykeyref);
}
my ($table_def, $tablealiases, $tablealiashref);
$table_def = $self->{table}{$table};
return undef if (!$table_def);
$self->load_table_metadata($table) if (!defined $table_def->{loaded});
$tablealiases = $table_def->{tablealiases};
$tablealiashref = $table_def->{tablealias};
############################################################
# Record indexes of all requested columns
############################################################
my ($idx, $column, %columnidx, @write, @reptype);
for ($idx = 0; $idx <= $#$columnref; $idx++) {
$column = $columnref->[$idx];
if (! defined $columnidx{$column}) {
$columnidx{$column} = $idx;
}
$write[$idx] = 1; # assume every field writable
$reptype[$idx] = "string"; # assume every field a string (most general
type)
}
############################################################
# ensure that the primary key and sort keys are included
############################################################
my ($dbexpr, $columnalias, $columntype, $column_def);
my (%dbexpr, @select_phrase, $group_reqd, @group_dbexpr, %reqd_tables);
my (@keycolidx, $prikey, $pritab);
my ($is_summary, %is_summary_key, $summaryexpr, @group_summarykeys);
$is_summary = (defined $summarykeyref && $#$summarykeyref >= 0);
$pritab = "";
if ($is_summary) {
foreach $column (@$summarykeyref) { # primary key is list of summary
keys
$is_summary_key{$column} = 1;
if (! defined $columnidx{$column}) {
push(@$columnref, $column); # add the column to the list
$columnidx{$column} = $#$columnref;
}
push(@keycolidx, $columnidx{$column});
$write[$columnidx{$column}] = 0; # keys aren't editable
}
}
else { # non-summary (detail) table rows
$prikey = $table_def->{prikey}; # primary key is in the metadata
if ($prikey) {
foreach $column (@$prikey) {
if (! defined $columnidx{$column}) {
push(@$columnref, $column); # add the column to the list
$columnidx{$column} = $#$columnref;
}
push(@keycolidx, $columnidx{$column});
$write[$columnidx{$column}] = 0; # keys aren't editable
$dbexpr = $table_def->{column}{$column}{dbexpr}; # take note of
table the key is on
if ($dbexpr && $dbexpr =~
/^([a-zA-Z][a-zA-Z0-9_]*)\.[a-zA-Z_][a-zA-Z_0-9]*$/) {
$pritab = $1;
}
}
}
}
if (defined $ordercolref && ref($ordercolref) eq "ARRAY") {
foreach $column (@$ordercolref) { # foreach sort key
if ($column && ! defined $columnidx{$column}) {
push(@$columnref, $column); # add the column to the list
$columnidx{$column} = $#$columnref;
}
}
}
for ($idx = 0; $idx <= $#$columnref; $idx++) {
$column = $columnref->[$idx];
$column_def = $table_def->{column}{$column};
if (!defined $column_def) {
push(@select_phrase, "NULL u$idx");
next;
}
$columnalias = $column_def->{alias};
$dbexpr = $column_def->{dbexpr};
$reptype[$idx] = $column_def->{type};
# if the field is not defined, or it is not a simple field on the primary
table...
if (!defined $dbexpr || $dbexpr !~ /^$pritab\.[a-zA-Z_][a-zA-Z0-9_]*$/) {
$write[$idx] = 0; # consider it *not* writable
}
############################################################
# accumulate select expressions and their aliases
############################################################
if ($is_summary) {
if ($is_summary_key{$column}) {
if (defined $dbexpr) {
push(@select_phrase, "$dbexpr $columnalias");
push(@group_summarykeys, $columnalias);
}
}
else {
$summaryexpr = $column_def->{summary};
if (!defined $summaryexpr || $summaryexpr eq "") {
$columntype = $column_def->{type};
if ($columntype eq "integer" || $columntype eq "number") {
$summaryexpr = "avg(\$)";
}
else {
$summaryexpr = "count(distinct(\$))";
}
}
if (defined $dbexpr) {
$summaryexpr =~ s#\$#$dbexpr#g; # substitute the dbexpr into
the summaryexpr
}
else {
$summaryexpr = "NULL";
}
push(@select_phrase, "$summaryexpr $columnalias") if ($summaryexpr);
}
}
else {
push(@select_phrase, (defined $dbexpr) ? "$dbexpr $columnalias" : "NULL
$columnalias");
}
############################################################
# get the expression from the config info
############################################################
if (!defined $dbexpr || $dbexpr eq "") {
$dbexpr{$column} = "NULL";
}
else {
############################################################
# save selected columns for reference
############################################################
$dbexpr{$column} = $dbexpr;
############################################################
# accumulate group-by columns and whether grouping reqd
############################################################
if (($dbexpr =~ /sum *\(/i) ||
($dbexpr =~ /min *\(/i) ||
($dbexpr =~ /max *\(/i) ||
($dbexpr =~ /avg *\(/i) ||
($dbexpr =~ /std *\(/i) ||
($dbexpr =~ /stddev *\(/i) || # Oracle extension (supported by MySQL)
($dbexpr =~ /count *\(/i)) {
$group_reqd = 1;
}
else {
push(@group_dbexpr, $columnalias);
}
############################################################
# For each table, mentioned in the select expression...
############################################################
$self->require_tables($dbexpr, \%reqd_tables, $tablealiashref, 1);
}
}
############################################################
# copy data out if a reference is given
############################################################
if (defined $keycolidxref && ref($keycolidxref) eq "ARRAY") {
@$keycolidxref = @keycolidx;
}
if (defined $writeref && ref($writeref) eq "ARRAY") {
@$writeref = @write;
}
if (defined $reptyperef && ref($reptyperef) eq "ARRAY") {
@$reptyperef = @reptype;
}
############################################################
# create order-by columns
############################################################
my (@order_by_dbexpr, $order_by_dbexpr);
if (defined $ordercolref && ref($ordercolref) eq "ARRAY") {
for ($idx = 0; $idx <= $#$ordercolref; $idx++) {
$column = $ordercolref->[$idx];
$column_def = $table_def->{column}{$column};
next if (!defined $column_def);
$order_by_dbexpr = $dbexpr{$column};
if (!$order_by_dbexpr) {
$order_by_dbexpr = $column_def->{dbexpr};
$self->require_tables($order_by_dbexpr, \%reqd_tables,
$tablealiashref, 1);
}
$columnalias = $column_def->{alias};
$order_by_dbexpr = $columnalias if ($columnalias);
if ($order_by_dbexpr) {
if (defined $sortdircolref && ref($sortdircolref) eq "ARRAY" &&
$sortdircolref->[$idx]) {
if ($sortdircolref->[$idx] eq "UP") {
$order_by_dbexpr .= " asc";
}
elsif ($sortdircolref->[$idx] eq "DOWN") {
$order_by_dbexpr .= " desc";
}
}
push(@order_by_dbexpr, $order_by_dbexpr);
}
}
}
############################################################
# create initial where conditions for the selected rows
############################################################
my %op = (
'contains' => 'like',
'eq' => '=',
'ne' => '!=',
'le' => '<=',
'lt' => '<',
'ge' => '>=',
'gt' => '>',
'in' => 'in',
);
my ($where_condition, @join_conditions, @criteria_conditions, $param, $sqlop,
$paramvalue);
if (!defined $paramref && ref($paramvaluehref) eq "HASH") {
$paramref = [ keys %$paramvaluehref ];
}
for ($idx = 0; $idx <= $#$paramref; $idx++) {
$param = $paramref->[$idx];
next if (!defined $param || $param eq "");
next if (!defined $paramvaluehref || $paramvaluehref->{$param} eq "");
$column = $param;
if ($param eq "_key") {
# o TODO: enable multi-field primary keys (this assumes one-field only)
# o TODO: enable non-integer primary key fields (this assumes integer,
no quotes)
$column = $table_def->{prikey}; # assumes one column primary key
$dbexpr = $table_def->{column}{$column}{dbexpr};
if ($paramvalue =~ /,/) {
$where_condition = "$dbexpr in ($paramvalue)"; # assumes one
column, non-quoted primary key
}
else {
$where_condition = "$dbexpr = $paramvalue"; # assumes one
column, non-quoted primary key
}
push(@criteria_conditions, $where_condition);
next;
}
$sqlop = "=";
# check if $param contains an embedded operation, i.e. "name.eq",
"name.contains"
if ($param =~ /^(.*)\.([^.]+)$/) {
if ($op{$2}) {
$column = $1;
$sqlop = $op{$2};
}
}
$column_def = $table_def->{column}{$column};
if (!defined $column_def) {
if ($param =~ /^begin_(.*)/) {
$column = $1;
$sqlop = ">=";
}
elsif ($param =~ /^end_(.*)/) {
$column = $1;
$sqlop = "<=";
}
$column_def = $table_def->{column}{$column};
}
next if (!defined $column_def); # skip if the column is unknown
if (! defined $paramvaluehref->{$param}) {
$paramvalue = "?"; # TODO: make this work with the "contains" operator
}
else {
$paramvalue = $paramvaluehref->{$param};
if ($sqlop eq "like") {
$paramvalue =~ s/'/\\'/g;
$paramvalue = "'%$paramvalue%'";
}
elsif ($sqlop eq "in" || $sqlop eq "=") {
if ($column_def->{quoted}) {
$paramvalue =~ s/'/\\'/g;
if ($paramvalue =~ /,/) {
$paramvalue =~ s/,/','/g;
$paramvalue = "('$paramvalue')";
$sqlop = "in";
}
else {
$paramvalue = "'$paramvalue'";
$sqlop = "=";
}
}
else {
if ($paramvalue =~ /,/) {
$paramvalue = "($paramvalue)";
$sqlop = "in";
}
else {
$sqlop = "=";
}
}
}
elsif (!defined $column_def->{quoted} || $column_def->{quoted}) {
$paramvalue =~ s/'/\\'/g;
$paramvalue = "'$paramvalue'";
}
}
$dbexpr = $column_def->{dbexpr};
if (defined $dbexpr && $dbexpr ne "") {
$self->require_tables($dbexpr, \%reqd_tables, $tablealiashref, 2);
push(@criteria_conditions, "$dbexpr $sqlop $paramvalue");
}
}
# THIS IS DEAD CODE.
# I NEED TO FIGURE OUT WHAT IT USED TO DO SO I CAN FIGURE WHETHER I NEED
# TO REWRITE IT AND REINSTATE IT IN THE CURRENT CODE BASE.
# {
# my ($paramsql_alias_table, %param_used, @params_to_be_used);
# my ($cond1, $cond2, $expr, $p1, $p2, $p1val, $p2val, @pval);
# my ($crit_lines);
#
# $paramsql_alias_table = $self->{table}{aliases}{$table}{parametersql};
# $paramsql_alias_table = $table if (!$dep_alias_table);
# $crit_lines = $self->{table}{criterialines}{$dep_alias_table}
#
# CRIT: foreach $expr (@crit_lines) {
# @params_to_be_used = ();
# if ($expr =~ /^ *([^ ].*[^ ]) *\? *([^ ].*[^ ]) *$/) {
# $cond1 = $1;
# $expr = $2;
#
# if ($cond1 =~ /^#(.+)/) {
# $p = $1;
# @pval = $query->param($p);
# next if ($#pval <= 0);
# }
# elsif ($cond1 =~ /^([a-zA-Z0-9]+) *== *\*([a-zA-Z0-9]+) *$/) {
# $p1 = $1;
# $p2 = $2;
# next CRIT if (defined $param_used{$p1} || defined
$param_used{$p2});
# $p1val = $query->param($p1);
# $p2val = $query->param($p2);
# next CRIT if (!defined $p1val || !defined $p2val || $p1val ne
$p2val);
# push(@params_to_be_used, $p2);
# }
# }
#
# $cond2 = $expr;
# while ($cond2 =~ s/{([a-zA-Z0-9]+)}//) {
# $p = $1;
# @pval = $query->param($p);
# next CRIT if (!defined @pval || $#pval < 0 || $pval[0] eq "");
# next CRIT if (defined $param_used{$p});
# push(@params_to_be_used, $p);
# if ($expr =~ /'{$p}'/) {
# $p1val = "'" . join("','",@pval) . "'";
# $expr =~ s/'{$p}'/$p1val/;
# }
# else {
# $p1val = join(",",@pval);
# $expr =~ s/{$p}/$p1val/;
# }
# }
# foreach (@params_to_be_used) {
# $param_used{$_} = 1;
# }
# push(@criteria_conditions, $expr);
# $self->require_tables($expr, \%reqd_tables, $table_aliases, 2);
# }
# }
############################################################
# put tables in table list in the standard order
# and build the join criteria
############################################################
my ($dbtable, $tablealias, @from_tables);
foreach $tablealias (@$tablealiases) {
if ($reqd_tables{$tablealias}) {
$dbtable = $tablealiashref->{$tablealias}{table};
if ($dbtable) {
push(@from_tables,"$dbtable $tablealias");
}
else {
push(@from_tables,$tablealias);
}
$where_condition = $tablealiashref->{$tablealias}{joincriteria};
push(@join_conditions, split(/ +and +/,$where_condition)) if
($where_condition);
}
}
if ($#from_tables == -1 && $#$tablealiases > -1) {
$tablealias = $tablealiases->[0];
$table = $tablealiashref->{$tablealias}{table};
if ($table) {
push(@from_tables,"$table $tablealias");
}
else {
push(@from_tables,$tablealias);
}
}
############################################################
# create the SQL statement
############################################################
my ($sql, $critsense);
if ($#select_phrase >= 0) {
$sql = "select\n " .
join(",\n ",@select_phrase) . "\n" .
"from\n " .
join(",\n ",@from_tables) . "\n";
}
if ($#join_conditions >= 0) {
$sql .= "where " . join("\n and ",@join_conditions) . "\n";
}
$critsense = "AND";
$critsense = $paramvaluehref->{"_query.critsense"} if (defined $paramvaluehref);
$critsense = "AND" if (!defined $critsense);
if ($#criteria_conditions >= 0) {
$sql .= ($#join_conditions == -1 ? "where " : " and ");
if ($critsense eq "NOT_AND") {
$sql .= "not (" . join("\n and ",@criteria_conditions) . ")\n";
}
elsif ($critsense eq "NOT_OR") {
$sql .= "not (" . join("\n or ",@criteria_conditions) . ")\n";
}
elsif ($critsense eq "OR") {
$sql .= "(" . join("\n or ",@criteria_conditions) . ")\n";
}
else {
$sql .= join("\n and ",@criteria_conditions) . "\n";
}
}
if ($#group_summarykeys >= 0) {
$sql .= "group by\n " . join(",\n ",@group_summarykeys) . "\n";
}
elsif ($group_reqd && $#group_dbexpr >= 0) {
$sql .= "group by\n " . join(",\n ",@group_dbexpr) . "\n";
}
if ($#order_by_dbexpr >= 0) {
$sql .= "order by\n " . join(",\n ",@order_by_dbexpr) . "\n";
}
############################################################
# return the SQL statement
############################################################
$sql;
}
sub require_tables {
my ($self, $dbexpr, $reqd_tables, $tablealiashref, $require_type) = @_;
my ($tablealias, $tablealias2, @tablealias, %tableseen, $dependencies);
while ($dbexpr =~ s/([a-zA-Z_][a-zA-Z_0-9]*)\.[a-zA-Z_][a-zA-Z_0-9]*//) {
if (defined $tablealiashref->{$1} && !$tableseen{$1}) {
push(@tablealias, $1);
$tableseen{$1} = 1;
}
while ($tablealias = pop(@tablealias)) {
if (! defined $reqd_tables->{$tablealias}) {
$reqd_tables->{$tablealias} = $require_type;
$dependencies = $tablealiashref->{$tablealias}{dependencies};
push(@tablealias, @$dependencies)
if (defined $dependencies && ref($dependencies) eq "ARRAY");
}
elsif ($reqd_tables->{$tablealias} < $require_type) {
$reqd_tables->{$tablealias} = $require_type;
}
}
}
}
# $insert_sql = $rep->mk_insert_row_sql ($table, \@cols, \@colvalueref);
sub mk_insert_row_sql {
my ($self, $table, $colref, $colvalueref) = @_;
$self->load_table_metadata($table) if (!defined $self->{table}{$table}{loaded});
my ($sql, $values, $col, $value, $colnum);
if ($#$colref == -1) {
$self->{error} = "Database->mk_insert_row_sql(): no columns specified";
return();
}
my $tabcolref = $self->{table}{$table}{column};
$sql = "insert into $table\n";
$values = "values\n";
for ($colnum = 0; $colnum <= $#$colref; $colnum++) {
$col = $colref->[$colnum];
if ($#$colvalueref == -1) {
$value = "?";
}
else {
$value = $colvalueref->[$colnum];
if ($tabcolref->{$col}{quoted}) {
$value =~ s/'/\\'/g;
$value = "'$value'";
}
}
$sql .= ($colnum == 0) ? " ($col" : ",\n $col";
$values .= ($colnum == 0) ? " ($value" : ",\n $value";
}
$sql .= ")\n";
$values .= ")\n";
$sql . $values;
}
# $update_sql = $rep->mk_update_row_sql ($table, \@cols, \@colvalueref, \@keycolidx);
sub mk_update_row_sql {
my ($self, $table, $colref, $colvalueref, $keycolidxref) = @_;
$self->load_table_metadata($table) if (!defined $self->{table}{$table}{loaded});
my ($sql, $where, @colused, $col, $value, $colnum, $i, $nonkeycolnum);
if ($#$colref == -1) {
$self->{error} = "Database->mk_update_row_sql(): no columns specified";
return();
}
my $tabcolref = $self->{table}{$table}{column};
$colused[$#$colref] = 0; # pre-extend the array
$sql = "update $table set\n";
$where = "";
if (defined $keycolidxref && $#$keycolidxref > -1) {
for ($i = 0; $i <= $#$keycolidxref; $i++) {
$colnum = $keycolidxref->[$i];
$col = $colref->[$colnum];
if ($#$colvalueref == -1) {
$value = "?";
}
else {
$value = $colvalueref->[$colnum];
if ($tabcolref->{$col}{quoted}) {
$value =~ s/'/\\'/g;
$value = "'$value'";
}
}
$where .= ($i == 0) ? "where $col = $value" : "\n and $col = $value";
$colused[$colnum] = 1;
}
$where .= "\n";
}
$nonkeycolnum = 0;
for ($colnum = 0; $colnum <= $#$colref; $colnum++) {
if (!$colused[$colnum]) {
$col = $colref->[$colnum];
if ($#$colvalueref == -1) {
$value = "?";
}
else {
$value = $colvalueref->[$colnum];
if ($tabcolref->{$col}{quoted}) {
$value =~ s/'/\\'/g;
$value = "'$value'";
}
}
$sql .= ($nonkeycolnum++ == 0) ? " $col = $value" : ",\n $col =
$value";
}
}
"$sql\n$where";
}
# $update_sql = $rep->mk_update_rows_sql($table, \@cols, \@colvalueref, \@params,
\%paramvalues);
sub mk_update_rows_sql {
my ($self, $table, $colref, $colvalueref, $paramref, $paramvaluehashref) = @_;
$self->load_table_metadata($table) if (!defined $self->{table}{$table}{loaded});
my ($sql, $col, $value, $colnum);
if ($#$colref == -1) {
$self->{error} = "Database->mk_update_rows_sql(): no columns specified";
return();
}
my $tabcolref = $self->{table}{$table}{column};
$sql = "update $table set";
for ($colnum = 0; $colnum <= $#$colref; $colnum++) {
$col = $colref->[$colnum];
if ($#$colvalueref == -1) {
$value = "?";
}
else {
$value = $colvalueref->[$colnum];
if ($tabcolref->{$col}{quoted}) {
$value =~ s/'/\\'/g;
$value = "'$value'";
}
}
$sql .= ($colnum == 0) ? "\n $col = $value" : ",\n $col = $value";
}
$sql .= "\n";
$sql .= $self->mk_where_clause($table, $paramref, $paramvaluehashref);
$sql;
}
# $delete_sql = $rep->mk_delete_row_sql ($table, \@cols, \@colvalueref, \@keycolidx);
sub mk_delete_row_sql {
my ($self, $table, $colref, $colvalueref, $keycolidxref) = @_;
$self->load_table_metadata($table) if (!defined $self->{table}{$table}{loaded});
my ($sql, $where, @colused, $col, $value, $colnum, $i, $nonkeycolnum);
if ($#$colref == -1) {
$self->{error} = "Database->mk_delete_row_sql(): no columns specified";
return();
}
my $tabcolref = $self->{table}{$table}{column};
$colused[$#$colref] = 0; # pre-extend the array
$sql = "delete from $table\n";
if (defined $keycolidxref && $#$keycolidxref > -1) {
for ($i = 0; $i <= $#$keycolidxref; $i++) {
$colnum = $keycolidxref->[$i];
$col = $colref->[$colnum];
if ($#$colvalueref == -1) {
$value = "?";
}
else {
$value = $colvalueref->[$colnum];
if ($tabcolref->{$col}{quoted}) {
$value =~ s/'/\\'/g;
$value = "'$value'";
}
}
$where .= ($i == 0) ? "where $col = $value" : "\n and $col = $value";
$colused[$colnum] = 1;
}
$where .= "\n";
}
"$sql$where";
}
# $delete_sql = $rep->mk_delete_rows_sql($table, \@params, \%paramvalues);
sub mk_delete_rows_sql {
my ($self, $table, $paramref, $paramvaluehashref) = @_;
$self->load_table_metadata($table) if (!defined $self->{table}{$table}{loaded});
my ($sql, $col, $colnum);
$sql = "delete from $table\n";
$sql .= $self->mk_where_clause($table, $paramref, $paramvaluehashref);
$sql;
}
######################################################################
# REPOSITORY METHODS (implements methods from Repository::Base)
######################################################################
# $colvalues = $rep->select_row ($table, \@cols, \@params, \%paramvalues);
sub select_row {
my $self = shift;
my ($table, $sql);
$table = $_[0];
if ($self->{table}{$table}{rawaccess}) {
$sql = $self->mk_select_rows_sql(@_);
}
else {
$sql = $self->mk_select_joined_rows_sql(@_);
}
$self->exec_select_row($sql);
}
# NOTE: everything after the first line is optional
# @rows = $rep->select_rows($table, \@cols,
# \@params, \%paramvalues, \@ordercols,
# $startrow, $endrow,
# \@sortdircol, \@keycolidx, \@writeable, \@columntype, \@summarykeys);
# TODO: get the $startrow/$endrow working when one/both/neither work in the SQL
portion
# TODO: rethink $startrow/$endrow vs. $numrows/$skiprows
sub select_rows {
my $self = shift;
my ($table, $startrow, $endrow, $sql);
($table, $startrow, $endrow) = @_[(0,5,6)];
if ($self->{table}{$table}{rawaccess}) {
$sql = $self->mk_select_rows_sql(@_);
}
else {
$sql = $self->mk_select_joined_rows_sql(@_);
}
$self->exec_select($sql, $startrow, $endrow);
}
# $ok = $rep->insert_row ($table, \@cols, \@colvalues);
sub insert_row {
my ($self, $table, $colref, $colvalueref) = @_;
my $sql = $self->mk_insert_row_sql($table, $colref, $colvalueref);
$self->exec_sql($sql);
}
# $ok = $rep->insert_rows ($table, \@cols, \@rows);
sub insert_rows {
my ($self, $table, $cols, $rows) = @_;
my ($row, $sql, $nrows, $ok);
$ok = 1;
foreach $row (@$rows) {
$sql = $self->mk_insert_row_sql($table, $cols, $row);
$nrows += $self->{numrows};
if (!$self->exec_sql($sql)) {
$self->{numrows} = $nrows;
$ok = 0;
last;
}
}
$self->{numrows} = $nrows;
return($ok);
}
# $ok = $rep->update_row ($table, \@cols, \@colvalues, \@keycolidx);
sub update_row {
my ($self, $table, $colref, $colvalueref, $keycolidxref) = @_;
my $sql = $self->mk_update_row_sql($table, $colref, $colvalueref, $keycolidxref);
$self->exec_sql($sql);
}
# $ok = $rep->update_rows($table, \@cols, \@colvalues, \@params, \%paramvalues);
sub update_rows {
my ($self, $table, $colref, $colvalueref, $paramref, $paramvaluehashref) = @_;
my $sql = $self->mk_update_rows_sql($table, $colref, $colvalueref, $paramref,
$paramvaluehashref);
$self->exec_sql($sql);
}
# $ok = $rep->store_row ($table, \@cols, \@colvalues, \@keycolidx, $update_first);
sub store_row {
my ($self, $table, $colref, $colvalueref, $keycolidxref, $update_first) = @_;
my ($update_sql, $insert_sql, $success);
$success = 0;
if ($update_first) {
$update_sql =
$self->mk_update_row_sql($table,$colref,$colvalueref,$keycolidxref);
$success = $self->exec_sql($update_sql);
$success = 0 if ($self->{numrows} == 0); # SQL succeeded but found no
rows to update
if (!$success) {
$insert_sql = $self->mk_insert_row_sql($table,$colref,$colvalueref);
$success = $self->exec_sql($insert_sql);
}
}
else {
$insert_sql = $self->mk_insert_row_sql($table,$colref,$colvalueref);
$success = $self->exec_sql($insert_sql);
if (!$success) {
$update_sql =
$self->mk_update_row_sql($table,$colref,$colvalueref,$keycolidxref);
$success = $self->exec_sql($update_sql);
$success = 0 if ($self->{numrows} == 0); # SQL succeeded but found
no rows to update
}
}
$success;
}
# $ok = $rep->store_rows ($table, \@cols, \@rows, \@keycolidx, $update_first);
sub store_rows {
my ($self, $table, $colref, $rowref, $keycolidxref, $update_first) = @_;
my ($colvalueref, $ok, $allok);
$allok = 1;
foreach $colvalueref (@$rowref) {
$ok = $self->store_row ($table, $colref, $colvalueref, $keycolidxref,
$update_first);
$allok = 0 if (!$ok);
}
$allok;
}
# $ok = $rep->delete_row ($table, \@cols, \@colvalues, \@keycolidx);
sub delete_row {
my $self = shift;
my $sql = $self->mk_delete_row_sql(@_);
$self->exec_sql($sql);
}
# $ok = $rep->delete_rows($table, \@params, \%paramvalues);
sub delete_rows {
my $self = shift;
my $sql = $self->mk_delete_rows_sql(@_);
$self->exec_sql($sql);
}
######################################################################
# METADATA REPOSITORY METHODS (implements methods from Repository::Base)
######################################################################
use DBIx::Compat;
sub load_rep_metadata_auto {
my ($self) = @_;
my ($dbidriver, $dbh);
$dbidriver = $self->{dbidriver};
$dbh = $self->{dbh};
#####################################################
# TABLE DATA
#####################################################
my ($table, @tables, $func);
# if we are not hiding the physical tables, go get them
if (! $self->{hide_physical}) {
# get a list of the physical tables from the database
@tables = $dbh->tables;
# if the DBI method doesn't work, try the DBIx method...
if ($#tables == -1) {
$func = DBIx::Compat::GetItem($dbidriver, "ListTables");
@tables = &{$func}($dbh);
}
# go through the list of native tables from the database
foreach $table (@tables) {
# if it has never been defined, then define it
if (!defined $self->{table}{$table}) {
$self->{table}{$table} = {
"name" => $table,
};
}
# if it has not been added to the list and it is not explicitly hidden,
add to list
if (!defined $self->{table}{$table}{idx} && !
$self->{table}{$table}{hide}) {
push(@{$self->{tables}}, $table); # add to list
$self->{table}{$table}{idx} = $#{$self->{tables}}; # take note of
the index
}
}
}
#########################################################
# TYPE DATA
# note: these are native database types, whereas a Repository "type" is a
standard
#########################################################
my ($ntype_attribute_idx, @ntype_attribute_values);
($ntype_attribute_idx, @ntype_attribute_values) = @{$dbh->type_info_all};
# Contents of $type_attribute_idx for MySQL:
# $ntype_attribute_idx = {
# "TYPE_NAME" => 0,
# "DATA_TYPE" => 1,
# "COLUMN_SIZE" => 2,
# "LITERAL_PREFIX" => 3,
# "LITERAL_SUFFIX" => 4,
# "CREATE_PARAMS" => 5,
# "NULLABLE" => 6,
# "CASE_SENSITIVE" => 7,
# "SEARCHABLE" => 8,
# "UNSIGNED_ATTRIBUTE" => 9,
# "FIXED_PREC_SCALE" => 10,
# "AUTO_UNIQUE_VALUE" => 11,
# "LOCAL_TYPE_NAME" => 12,
# "MINIMUM_SCALE" => 13,
# "MAXIMUM_SCALE" => 14,
# "NUM_PREC_RADIX" => 15,
# "mysql_native_type" => 16,
# "mysql_is_num" => 17,
# };
# Contents of @ntype_attribute_values for MySQL:
# TYPE_NAME DATA_TYPE COLUMN_SIZE PRE SUF CREATEPARAMETERS NUL CASE SRCH UNS
FIX AUTO LTYPE MINS MAXS RDX
# varchar 12 255 ' ' max length 1 0 1 0
0 0 0 0 0 0
# decimal 3 15 precision,scale 1 0 1 0
0 0 0 0 6 2
# tinyint -6 3 1 0 1 0
0 0 0 0 0 10
# smallint 5 5 1 0 1 0
0 0 0 0 0 10
# integer 4 10 1 0 1 0
0 0 0 0 0 10
# float 7 7 1 0 0 0
0 0 0 0 2 2
# double 8 15 1 0 1 0
0 0 0 0 4 2
# timestamp 11 14 ' ' 0 0 1 0
0 0 0 0 0 0
# bigint -5 20 1 0 1 0
0 0 0 0 0 10
# middleint 4 8 1 0 1 0
0 0 0 0 0 10
# date 9 10 ' ' 1 0 1 0
0 0 0 0 0 0
# time 10 6 ' ' 1 0 1 0
0 0 0 0 0 0
# datetime 11 21 ' ' 1 0 1 0
0 0 0 0 0 0
# year 5 4 1 0 1 0
0 0 0 0 0 0
# date 9 10 ' ' 1 0 1 0
0 0 0 0 0 0
# enum 12 255 ' ' 1 0 1 0
0 0 0 0 0 0
# set 12 255 ' ' 1 0 1 0
0 0 0 0 0 0
# blob -1 65535 ' ' 1 0 1 0
0 0 0 0 0 0
# tinyblob -1 255 ' ' 1 0 1 0
0 0 0 0 0 0
# mediumblob -1 16777215 ' ' 1 0 1 0
0 0 0 0 0 0
# longblob -1 2147483647 ' ' 1 0 1 0
0 0 0 0 0 0
# char 1 255 ' ' max length 1 0 1 0
0 0 0 0 0 0
# decimal 2 15 precision,scale 1 0 1 0
0 0 0 0 6 2
# tinyint unsigned -6 3 1 0 1 1
0 0 0 0 0 10
# smallint unsigned 5 5 1 0 1 1
0 0 0 0 0 10
# middleint unsigned 4 8 1 0 1 1
0 0 0 0 0 10
# int unsigned 4 10 1 0 1 1
0 0 0 0 0 10
# int 4 10 1 0 1 0
0 0 0 0 0 10
# integer unsigned 4 10 1 0 1 1
0 0 0 0 0 10
# bigint unsigned -5 20 1 0 1 1
0 0 0 0 0 10
# text -1 65535 ' ' 1 0 1 0
0 0 0 0 0 0
# mediumtext -1 16777215 ' ' 1 0 1 0
0 0 0 0 0 0
my ($ntype_name, @ntype_names, $ntype_num, $ntype_attribute_values, $ntype_def);
my ($ntype_name_idx, $ntype_num_idx, $column_size_idx, $literal_prefix_idx,
$literal_suffix_idx);
my ($unsigned_attribute_idx, $auto_unique_value_idx, $column);
$ntype_name_idx = $ntype_attribute_idx->{"TYPE_NAME"};
$ntype_num_idx = $ntype_attribute_idx->{"DATA_TYPE"};
$column_size_idx = $ntype_attribute_idx->{"COLUMN_SIZE"};
$literal_prefix_idx = $ntype_attribute_idx->{"LITERAL_PREFIX"};
$literal_suffix_idx = $ntype_attribute_idx->{"LITERAL_SUFFIX"};
$unsigned_attribute_idx = $ntype_attribute_idx->{"UNSIGNED_ATTRIBUTE"};
$auto_unique_value_idx = $ntype_attribute_idx->{"AUTO_UNIQUE_VALUE"};
# go through the list of native type info from the DBI handle
foreach $ntype_attribute_values (@ntype_attribute_values) {
$ntype_name = $ntype_attribute_values->[$ntype_name_idx];
$ntype_num = $ntype_attribute_values->[$ntype_num_idx];
$ntype_def = {};
push(@ntype_names, $ntype_name);
$self->{native}{type}{$ntype_name} = $ntype_def;
if (!defined $self->{native}{type}{$ntype_num}) {
$self->{native}{type}{$ntype_num} = $ntype_def;
}
# save all the info worth saving in a native type definition
$ntype_def->{name} = $ntype_name; # a real type name
$ntype_def->{num} = $ntype_num; # an internal data type
number
$ntype_def->{column_size} =
$ntype_attribute_values->[$column_size_idx];
$ntype_def->{literal_prefix} =
$ntype_attribute_values->[$literal_prefix_idx];
$ntype_def->{literal_suffix} =
$ntype_attribute_values->[$literal_suffix_idx];
$ntype_def->{unsigned_attribute} =
$ntype_attribute_values->[$unsigned_attribute_idx];
$ntype_def->{auto_unique_value} =
$ntype_attribute_values->[$auto_unique_value_idx];
$ntype_def->{literal_prefix} = "" if (! defined
$ntype_def->{literal_prefix});
$ntype_def->{literal_suffix} = "" if (! defined
$ntype_def->{literal_suffix});
$ntype_def->{quoted} = ($ntype_def->{literal_prefix} ne "" ||
$ntype_def->{literal_suffix} ne "");
# translate a native type into a repository type
if ($ntype_name =~ /char/ || $ntype_name eq "enum" || $ntype_name eq "set") {
$ntype_def->{type} = "string";
}
elsif ($ntype_name =~ /text/) {
$ntype_def->{type} = "text";
}
elsif ($ntype_name =~ /int/ || $ntype_name eq "year") {
$ntype_def->{type} = "integer";
}
elsif ($ntype_name =~ /decimal/ || $ntype_name =~ /float/ || $ntype_name =~
/double/) {
$ntype_def->{type} = "float";
}
elsif ($ntype_name =~ /datetime/ || $ntype_name =~ /timestamp/) {
$ntype_def->{type} = "datetime";
}
elsif ($ntype_name =~ /time/) {
$ntype_def->{type} = "time";
}
elsif ($ntype_name =~ /date/) {
$ntype_def->{type} = "date";
}
elsif ($ntype_name =~ /blob/ || $ntype_name =~ /binary/) {
$ntype_def->{type} = "binary";
}
}
$self->{native}{types} = \@ntype_names;
#########################################################
# DATABASE ATTRIBUTES
#########################################################
$self->{native}{support_join} = DBIx::Compat::GetItem($dbidriver,
"SupportJoin");
$self->{native}{inner_join_syntax} = DBIx::Compat::GetItem($dbidriver,
"SupportSQLJoin");
$self->{native}{inner_join_only2tables} = DBIx::Compat::GetItem($dbidriver,
"SQLJoinOnly2Tabs");
$self->{native}{have_types} = DBIx::Compat::GetItem($dbidriver,
"HaveTypes");
$self->{native}{null_operator} = DBIx::Compat::GetItem($dbidriver,
"NullOperator");
$self->{native}{need_null_in_create} = DBIx::Compat::GetItem($dbidriver,
"NeedNullInCreate");
$self->{native}{empty_is_null} = DBIx::Compat::GetItem($dbidriver,
"EmptyIsNull");
}
sub load_table_metadata_auto {
my ($self, $table) = @_;
return if (! $table);
my ($dbidriver, $dbh, $sth, $native_table, $table_def);
my (@tables, $column, $func, $tablealias);
$dbidriver = $self->{dbidriver};
$dbh = $self->{dbh};
$table_def = $self->{table}{$table};
return if (!defined $table_def);
$native_table = $table; # assume the table name is a physical one
$native_table = $table_def->{native_table} if ($table_def->{native_table});
$table_def->{name} = $table;
$tablealias = $table_def->{alias};
if (! defined $tablealias) {
$tablealias = "t" . Repository->serial("table");
$table_def->{alias} = $tablealias;
}
$table_def->{tablealiases} = [ $tablealias ]
if (!defined $table_def->{tablealiases});
$table_def->{tablealias} = {}
if (!defined $table_def->{tablealias});
$table_def->{tablealias}{$tablealias} = {}
if (!defined $table_def->{tablealias}{$tablealias});
$table_def->{tablealias}{$tablealias}{table} = $table
if (!defined $table_def->{tablealias}{$tablealias}{table});
#########################################################
# COLUMN DATA
#########################################################
my ($colnum, $data_types, $columns, $column_def, $phys_columns);
my ($native_type_num, $native_type_def);
$func = DBIx::Compat::GetItem($dbidriver, "ListFields");
$sth = &{$func}($dbh, $table);
$phys_columns = $sth->{NAME}; # array of fieldnames
$data_types = $sth->{TYPE}; # array of fieldtypes
$columns = $table_def->{columns};
# if we got a list of columns for the table from the database
if (defined $phys_columns && ref($phys_columns) eq "ARRAY") {
for ($colnum = 0; $colnum <= $#$phys_columns; $colnum++) {
$column = $phys_columns->[$colnum];
$column_def = $table_def->{column}{$column};
if (!defined $column_def) {
$column_def = {};
$table_def->{column}{$column} = $column_def;
}
next if ($column_def->{hide});
$native_type_num = $data_types->[$colnum];
$native_type_def = $self->{native}{type}{$native_type_num};
if (! $self->{hide_physical} && ! defined $column_def->{idx}) {
push(@$columns, $column);
$column_def->{idx} = $#$columns;
}
$column_def->{name} = $column;
$column_def->{type} = $native_type_def->{type};
$column_def->{quoted} = $native_type_def->{quoted};
$column_def->{alias} = "cn$colnum" if (!defined $column_def->{alias});
$column_def->{dbexpr} = $table_def->{alias} . "." . $column
if (!defined $column_def->{dbexpr});
}
}
######################################################################
# tables that are related via tablealiases can be "import"-ed
# this copies all of the column definitions from the imported table to this table
# TODO: allow for role modifiers in related tables
# TODO: rethink "import=1" to "multiplicity=1"
# TODO: think about chained imports
# TODO: think about import on demand rather than in advance
######################################################################
my ($tablealiases, $alias, $alias_def, $related_table, $related_table_def);
my ($tablealias_defs, $tablealias_def, $idx);
$tablealiases = $table_def->{tablealiases};
if (defined $tablealiases && ref($tablealiases) eq "ARRAY") {
foreach $alias (@$tablealiases) {
$alias_def = $table_def->{tablealias}{$alias};
if ($alias_def->{import}) {
$related_table = $alias_def->{table};
if (! $self->{table}{$related_table}{loaded}) {
$self->load_table_metadata($related_table);
}
$related_table_def = $self->{table}{$related_table};
foreach $column (@{$related_table_def->{columns}}) {
if (! defined $table_def->{column}{$column} &&
defined $related_table_def->{column}{$column}) {
$table_def->{column}{$column} =
$related_table_def->{column}{$column};
}
}
}
}
}
# for each tablealias named in the configuration, give it a number up front
$tablealias_defs = $table_def->{tablealias};
for ($idx = 0; $idx <= $#$tablealiases; $idx++) {
$tablealias = $tablealiases->[$idx];
$tablealias_defs->{$tablealias}{idx} = $idx;
}
# for each tablealias in the hash (random order), add them to the end
foreach $tablealias (keys %$tablealias_defs) {
$tablealias_def = $tablealias_defs->{$tablealias};
# table has not been added to the list and it's not explicitly "hidden", so
add it
if (!defined $tablealias_def->{idx}) {
push(@$tablealiases, $tablealias);
$tablealias_def->{idx} = $#$tablealiases;
}
}
if ($Repository::DEBUG >= 2 &&
Repository->dbg("Repository::DBI","load_table_metadata_auto",2)) {
print "Table Metadata: $table\n";
Repository->dump($table_def);
}
}
1;
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/Repository/SOAP.pm
Index: SOAP.pm
===================================================================
######################################################################
## File: $Id: SOAP.pm,v 1.1 2002/01/04 16:41:05 spadkins Exp $
######################################################################
use Repository;
package Repository::SOAP;
$VERSION = do { my @r=(q$Revision: 1.1 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
use Repository::Base;
@ISA = ( "Repository::Base" );
use strict;
=head1 NAME
Repository::SOAP - a repository implementation which actually gets data from a
remote source by making a SOAP (simple object access protocol) call
=head1 SYNOPSIS
use Repository::SOAP;
(see man page for Repository::Base for additional methods)
###################################################################
# constructors
###################################################################
$rep = Repository::SOAP->new();
###################################################################
# methods not defined in the Repository::Base interface
###################################################################
# ...
###################################################################
# defined in the Repository::Base interface, implemented here
###################################################################
$ok = $rep->connect(); # initialize repository (will happen automatically
in constructor)
$ok = $rep->disconnect(); # cleanup repository (will happen automatically in
destructor)
$rep->is_connected(); # returns 1 if connected (ready for use), 0 if not
$errmsg = $rep->error(); # returns the error string for prev op ("" if no
error)
$numrows = $rep->numrows(); # returns the number of rows affected by prev op
print $rep->error(), "\n" if (!$rep->connect());
print $rep->error(), "\n" if ($rep->connect() != $rep->OK);
# META-DATA: (about the tables)
$rep->load_rep_metadata_init();
$rep->load_table_metadata_init($tablename);
# MEDIUM-LEVEL: reads and writes rows, no caching
$row = $rep->select_row ($table, \@cols, \@wherecols,
\%wherevalues);
$rows = $rep->select_rows($table, \@cols, \@wherecols,
\%wherevalues, \@ordercols, $startrow, $endrow);
$ok = $rep->insert_row ($table, \@cols, \@colvalues);
$ok = $rep->insert_rows($table, \@cols, \@rows);
$ok = $rep->update_row ($table, \@cols, \@colvalues, \@keycolidx);
$ok = $rep->update_rows($table, \@cols, \@colvalues, \@wherecols,
\%wherevalues);
$ok = $rep->store_row ($table, \@cols, \@colvalues, \@keycolidx,
$update_first);
$ok = $rep->store_rows ($table, \@cols, \@rows, \@keycolidx,
$update_first);
$ok = $rep->delete_row ($table, \@cols, \@colvalues, \@keycolidx);
$ok = $rep->delete_rows($table, \@wherecols,
\%wherevalues);
=cut
######################################################################
# CONSTANTS
######################################################################
######################################################################
# ATTRIBUTES
######################################################################
######################################################################
# INHERITED ATTRIBUTES
######################################################################
# BASIC
# $self->{name} # name of this repository (often "default")
# $self->{config} # hash of config file data
# CURRENT STATE
# $self->{error} # most recent error generated from this module
# $self->{numrows}
# METADATA - Data Types
# $self->{types}
# $self->{type}{$type}
# $self->{type}{$type}{name}
# $self->{type}{$type}{type}
# METADATA - Tables and Columns
# $self->{tables}
# $self->{table}{$table}{readonly}
# $self->{table}{$table}{columns}
# $self->{table}{$table}{column}{$column}
# $self->{table}{$table}{column}{$column}{name}
# $self->{table}{$table}{column}{$column}{type}
# $self->{table}{$table}{column}{$column}{notnull}
=head1 DESCRIPTION
The Repository::SOAP class implements a logical repository which
is actually an interface to a remote source of data.
The local interface translates the parameters of the methods
into XML and issues a SOAP (simple object access protocol)
request to a remote machine to fulfill the method.
Details of the actual SOAP request and the remote repository
are controlled by the configuration information.
=cut
######################################################################
# INITIALIZATION
######################################################################
sub init {
my $self = shift;
}
######################################################################
# CONFIG METHODS
######################################################################
# take positional args and turn them into a %config structure
sub make_config {
my $self = shift;
my $conf = {
'repository' => {
'db' => {
},
},
};
$conf;
}
#sub read_config {
# my $self = shift;
# return ($self->SUPER::read_config());
#}
######################################################################
# CONNECTION METHODS
######################################################################
sub connect {
1; # no connection initialization required (HTTP is connectionless)
}
sub disconnect {
1; # no connection de-initialization required (HTTP is connectionless)
}
sub is_connected {
my $self = shift;
return 1; # TODO: might issue a "ping" of some sort to verify contact with
web server
}
sub error {
my ($self) = @_;
return $self->{error};
}
sub numrows {
my ($self) = @_;
return $self->{numrows};
}
######################################################################
# REPOSITORY METHODS (implements methods from Repository::Base)
######################################################################
# $colvalues = $rep->select_row ($table, \@cols, \@wherecols, \%wherevalues);
sub select_row {
my ($self, $table, $colref, $wherecolref, $wherevaluehashref) = @_;
my $row = [];
$colvalues;
}
# $rows = $rep->select_rows($table, \@cols, \@wherecols, \%wherevalues, \@ordercols,
$startrow, $endrow);
sub select_rows {
my ($self, $table, $colref, $wherecolref, $wherevaluehashref, $ordercolref,
$startrow, $endrow) = @_;
my $rows = [];
$rows;
}
# $ok = $rep->insert_row ($table, \@cols, \@colvalues);
sub insert_row {
my ($self, $table, $colref, $colvalueref) = @_;
my $success = 1;
$success;
}
# $ok = $rep->insert_rows ($table, \@cols, \@rows);
sub insert_rows {
my ($self, $table, $cols, $rows) = @_;
my $success = 1;
$success;
}
# $ok = $rep->update_row ($table, \@cols, \@colvalues, \@keycolidx);
sub update_row {
my ($self, $table, $colref, $colvalueref, $keycolidxref) = @_;
my $success = 1;
$success;
}
# $ok = $rep->update_rows($table, \@cols, \@colvalues, \@wherecols, \%wherevalues);
sub update_rows {
my ($self, $table, $colref, $colvalueref, $wherecolref, $wherevaluehashref) = @_;
my $success = 1;
$success;
}
# $ok = $rep->store_row ($table, \@cols, \@colvalues, \@keycolidx, $update_first);
sub store_row {
my ($self, $table, $colref, $colvalueref, $keycolidxref, $update_first) = @_;
my $success = 1;
$success;
}
# $ok = $rep->store_rows ($table, \@cols, \@rows, \@keycolidx, $update_first);
sub store_rows {
my ($self, $table, $colref, $rowref, $keycolidxref, $update_first) = @_;
my $success = 1;
$success;
}
# $ok = $rep->delete_row ($table, \@cols, \@colvalues, \@keycolidx);
sub delete_row {
my $self = shift;
my $success = 1;
$success;
}
# $ok = $rep->delete_rows($table, \@wherecols, \%wherevalues);
sub delete_rows {
my $self = shift;
my $success = 1;
$success;
}
1;
1.1 p5ee/P5EEx/Blue/P5EEx/Blue/Repository/Sample.pm
Index: Sample.pm
===================================================================
######################################################################
## File: $Id: Sample.pm,v 1.1 2002/01/04 16:41:05 spadkins Exp $
######################################################################
use Repository;
package Repository::Sample;
$VERSION = do { my @r=(q$Revision: 1.1 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r};
use Repository::Base;
@ISA = ( "Repository::Base" );
use strict;
=head1 NAME
Repository::Sample - a set of stubs for a repository implementation which can be
copied and modified
=head1 SYNOPSIS
use Repository::Sample;
(see man page for Repository::Base for additional methods)
###################################################################
# constructors
###################################################################
$rep = Repository::Sample->new();
###################################################################
# methods not defined in the Repository::Base interface
###################################################################
# ...
###################################################################
# defined in the Repository::Base interface, implemented here
###################################################################
$ok = $rep->connect(); # initialize repository (will happen automatically
in constructor)
$ok = $rep->disconnect(); # cleanup repository (will happen automatically in
destructor)
$rep->is_connected(); # returns 1 if connected (ready for use), 0 if not
$errmsg = $rep->error(); # returns the error string for prev op ("" if no
error)
$numrows = $rep->numrows(); # returns the number of rows affected by prev op
print $rep->error(), "\n" if (!$rep->connect());
print $rep->error(), "\n" if ($rep->connect() != $rep->OK);
# META-DATA: (about the tables)
$rep->load_rep_metadata_init();
$rep->load_table_metadata_init($tablename);
# MEDIUM-LEVEL: reads and writes rows, no caching
$row = $rep->select_row ($table, \@cols, \@wherecols,
\%wherevalues);
$rows = $rep->select_rows($table, \@cols, \@wherecols,
\%wherevalues, \@ordercols, $startrow, $endrow);
$ok = $rep->insert_row ($table, \@cols, \@colvalues);
$ok = $rep->insert_rows($table, \@cols, \@rows);
$ok = $rep->update_row ($table, \@cols, \@colvalues, \@keycolidx);
$ok = $rep->update_rows($table, \@cols, \@colvalues, \@wherecols,
\%wherevalues);
$ok = $rep->store_row ($table, \@cols, \@colvalues, \@keycolidx,
$update_first);
$ok = $rep->store_rows ($table, \@cols, \@rows, \@keycolidx,
$update_first);
$ok = $rep->delete_row ($table, \@cols, \@colvalues, \@keycolidx);
$ok = $rep->delete_rows($table, \@wherecols,
\%wherevalues);
=cut
######################################################################
# CONSTANTS
######################################################################
######################################################################
# ATTRIBUTES
######################################################################
######################################################################
# INHERITED ATTRIBUTES
######################################################################
# BASIC
# $self->{name} # name of this repository (often "default")
# $self->{config} # hash of config file data
# CURRENT STATE
# $self->{error} # most recent error generated from this module
# $self->{numrows}
# METADATA - Data Types
# $self->{types}
# $self->{type}{$type}
# $self->{type}{$type}{name}
# $self->{type}{$type}{type}
# METADATA - Tables and Columns
# $self->{tables}
# $self->{table}{$table}{readonly}
# $self->{table}{$table}{columns}
# $self->{table}{$table}{column}{$column}
# $self->{table}{$table}{column}{$column}{name}
# $self->{table}{$table}{column}{$column}{type}
# $self->{table}{$table}{column}{$column}{notnull}
=head1 DESCRIPTION
The Repository::Sample class is a file to be copied and modified
in order to create a new implementation of a repository class.
The methods defined below are stubs that need to be filled out.
=cut
######################################################################
# INITIALIZATION
######################################################################
sub init {
my $self = shift;
}
######################################################################
# CONFIG METHODS
######################################################################
# take positional args and turn them into a %config structure
sub make_config {
my $self = shift;
my $conf = {
'repository' => {
'db' => {
},
},
};
$conf;
}
#sub read_config {
# my $self = shift;
# return ($self->SUPER::read_config());
#}
######################################################################
# CONNECTION METHODS
######################################################################
sub connect {
my $self = shift;
my $success = 1;
return $success;
}
sub disconnect {
my $self = shift;
1;
}
sub is_connected {
my $self = shift;
return 1;
}
sub error {
my ($self) = @_;
return $self->{error};
}
sub numrows {
my ($self) = @_;
return $self->{numrows};
}
######################################################################
# REPOSITORY METHODS (implements methods from Repository::Base)
######################################################################
# $colvalues = $rep->select_row ($table, \@cols, \@wherecols, \%wherevalues);
sub select_row {
my ($self, $table, $colref, $wherecolref, $wherevaluehashref) = @_;
my $colvalues = [];
$colvalues;
}
# $rows = $rep->select_rows($table, \@cols, \@wherecols, \%wherevalues, \@ordercols,
$startrow, $endrow);
sub select_rows {
my ($self, $table, $colref, $wherecolref, $wherevaluehashref, $ordercolref,
$startrow, $endrow) = @_;
my $rows = [];
$rows;
}
# $ok = $rep->insert_row ($table, \@cols, \@colvalues);
sub insert_row {
my ($self, $table, $colref, $colvalueref) = @_;
my $success = 1;
$success;
}
# $ok = $rep->insert_rows ($table, \@cols, \@rows);
sub insert_rows {
my ($self, $table, $cols, $rows) = @_;
my $success = 1;
$success;
}
# $ok = $rep->update_row ($table, \@cols, \@colvalues, \@keycolidx);
sub update_row {
my ($self, $table, $colref, $colvalueref, $keycolidxref) = @_;
my $success = 1;
$success;
}
# $ok = $rep->update_rows($table, \@cols, \@colvalues, \@wherecols, \%wherevalues);
sub update_rows {
my ($self, $table, $colref, $colvalueref, $wherecolref, $wherevaluehashref) = @_;
my $success = 1;
$success;
}
# $ok = $rep->store_row ($table, \@cols, \@colvalues, \@keycolidx, $update_first);
sub store_row {
my ($self, $table, $colref, $colvalueref, $keycolidxref, $update_first) = @_;
my $success = 1;
$success;
}
# $ok = $rep->store_rows ($table, \@cols, \@rows, \@keycolidx, $update_first);
sub store_rows {
my ($self, $table, $colref, $rowref, $keycolidxref, $update_first) = @_;
my $success = 1;
$success;
}
# $ok = $rep->delete_row ($table, \@cols, \@colvalues, \@keycolidx);
sub delete_row {
my $self = shift;
my $success = 1;
$success;
}
# $ok = $rep->delete_rows($table, \@wherecols, \%wherevalues);
sub delete_rows {
my $self = shift;
my $success = 1;
$success;
}
######################################################################
# METADATA REPOSITORY METHODS (implements methods from Repository::Base)
# (implement if desired. needed only if metadata already exists.)
######################################################################
#sub load_rep_metadata_init {
# my ($self) = @_;
#
# # repeat calls will get no farther than this.
# return if (defined $self->{tables});
#
# #########################################################
# # TABLE LIST
# #########################################################
#
# $self->{tables} = [ "table1", "table2", "table3" ];
#
# #########################################################
# # TYPE DATA
# #########################################################
#
# my ($type, @types);
# @types = ( "string", "number", "date", "time", "datetime" );
#
# foreach $type (@types) {
# $self->{type}{$type}{name} = $type;
# }
#
# $self->{types} = \@types;
#
#}
#sub load_table_metadata_init {
# my ($self, $table) = @_;
#
# return if ($table eq "");
# return if (defined $self->{table}{$table}{loaded});
#
# $self->{table}{$table}{name} = $table;
# $self->{table}{$table}{readonly} = 0;
# $self->{table}{$table}{loaded} = 1;
#
# #########################################################
# # COLUMN DATA
# #########################################################
# my ($col, @data_types, @columns, $coldata, $column);
#
# @columns = ("key1", "col1", "col2");
# @data_types = ("integer", "string", "date");
#
# $self->{table}{$table}{columns} = [ @columns ];
#
# for ($col = 0; $col <= $#columns; $col++) {
# $column = $columns[$col];
# $coldata = $self->{table}{$table}{column}{$column} = {};
# $coldata->{name} = $column;
# $coldata->{type} = $data_types[$col];
# $coldata->{notnull} = 0;
# }
#}
1;