Initial commit.
This commit is contained in:
commit
ca4eef314f
3
AUTHORS
Normal file
3
AUTHORS
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Version since 1.11 maintained by imacat <imacat@mail.imacat.idv.tw>
|
||||||
|
|
||||||
|
Version 1.10 and earlier written by Paul Sharpe <paul@miraclefish.com>.
|
131
Artistic
Normal file
131
Artistic
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The "Artistic License"
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The intent of this document is to state the conditions under which a
|
||||||
|
Package may be copied, such that the Copyright Holder maintains some
|
||||||
|
semblance of artistic control over the development of the package,
|
||||||
|
while giving the users of the package the right to use and distribute
|
||||||
|
the Package in a more-or-less customary fashion, plus the right to make
|
||||||
|
reasonable modifications.
|
||||||
|
|
||||||
|
Definitions:
|
||||||
|
|
||||||
|
"Package" refers to the collection of files distributed by the
|
||||||
|
Copyright Holder, and derivatives of that collection of files
|
||||||
|
created through textual modification.
|
||||||
|
|
||||||
|
"Standard Version" refers to such a Package if it has not been
|
||||||
|
modified, or has been modified in accordance with the wishes
|
||||||
|
of the Copyright Holder as specified below.
|
||||||
|
|
||||||
|
"Copyright Holder" is whoever is named in the copyright or
|
||||||
|
copyrights for the package.
|
||||||
|
|
||||||
|
"You" is you, if you're thinking about copying or distributing
|
||||||
|
this Package.
|
||||||
|
|
||||||
|
"Reasonable copying fee" is whatever you can justify on the
|
||||||
|
basis of media cost, duplication charges, time of people involved,
|
||||||
|
and so on. (You will not be required to justify it to the
|
||||||
|
Copyright Holder, but only to the computing community at large
|
||||||
|
as a market that must bear the fee.)
|
||||||
|
|
||||||
|
"Freely Available" means that no fee is charged for the item
|
||||||
|
itself, though there may be fees involved in handling the item.
|
||||||
|
It also means that recipients of the item may redistribute it
|
||||||
|
under the same conditions they received it.
|
||||||
|
|
||||||
|
1. You may make and give away verbatim copies of the source form of the
|
||||||
|
Standard Version of this Package without restriction, provided that you
|
||||||
|
duplicate all of the original copyright notices and associated disclaimers.
|
||||||
|
|
||||||
|
2. You may apply bug fixes, portability fixes and other modifications
|
||||||
|
derived from the Public Domain or from the Copyright Holder. A Package
|
||||||
|
modified in such a way shall still be considered the Standard Version.
|
||||||
|
|
||||||
|
3. You may otherwise modify your copy of this Package in any way, provided
|
||||||
|
that you insert a prominent notice in each changed file stating how and
|
||||||
|
when you changed that file, and provided that you do at least ONE of the
|
||||||
|
following:
|
||||||
|
|
||||||
|
a) place your modifications in the Public Domain or otherwise make them
|
||||||
|
Freely Available, such as by posting said modifications to Usenet or
|
||||||
|
an equivalent medium, or placing the modifications on a major archive
|
||||||
|
site such as uunet.uu.net, or by allowing the Copyright Holder to include
|
||||||
|
your modifications in the Standard Version of the Package.
|
||||||
|
|
||||||
|
b) use the modified Package only within your corporation or organization.
|
||||||
|
|
||||||
|
c) rename any non-standard executables so the names do not conflict
|
||||||
|
with standard executables, which must also be provided, and provide
|
||||||
|
a separate manual page for each non-standard executable that clearly
|
||||||
|
documents how it differs from the Standard Version.
|
||||||
|
|
||||||
|
d) make other distribution arrangements with the Copyright Holder.
|
||||||
|
|
||||||
|
4. You may distribute the programs of this Package in object code or
|
||||||
|
executable form, provided that you do at least ONE of the following:
|
||||||
|
|
||||||
|
a) distribute a Standard Version of the executables and library files,
|
||||||
|
together with instructions (in the manual page or equivalent) on where
|
||||||
|
to get the Standard Version.
|
||||||
|
|
||||||
|
b) accompany the distribution with the machine-readable source of
|
||||||
|
the Package with your modifications.
|
||||||
|
|
||||||
|
c) give non-standard executables non-standard names, and clearly
|
||||||
|
document the differences in manual pages (or equivalent), together
|
||||||
|
with instructions on where to get the Standard Version.
|
||||||
|
|
||||||
|
d) make other distribution arrangements with the Copyright Holder.
|
||||||
|
|
||||||
|
5. You may charge a reasonable copying fee for any distribution of this
|
||||||
|
Package. You may charge any fee you choose for support of this
|
||||||
|
Package. You may not charge a fee for this Package itself. However,
|
||||||
|
you may distribute this Package in aggregate with other (possibly
|
||||||
|
commercial) programs as part of a larger (possibly commercial) software
|
||||||
|
distribution provided that you do not advertise this Package as a
|
||||||
|
product of your own. You may embed this Package's interpreter within
|
||||||
|
an executable of yours (by linking); this shall be construed as a mere
|
||||||
|
form of aggregation, provided that the complete Standard Version of the
|
||||||
|
interpreter is so embedded.
|
||||||
|
|
||||||
|
6. The scripts and library files supplied as input to or produced as
|
||||||
|
output from the programs of this Package do not automatically fall
|
||||||
|
under the copyright of this Package, but belong to whoever generated
|
||||||
|
them, and may be sold commercially, and may be aggregated with this
|
||||||
|
Package. If such scripts or library files are aggregated with this
|
||||||
|
Package via the so-called "undump" or "unexec" methods of producing a
|
||||||
|
binary executable image, then distribution of such an image shall
|
||||||
|
neither be construed as a distribution of this Package nor shall it
|
||||||
|
fall under the restrictions of Paragraphs 3 and 4, provided that you do
|
||||||
|
not represent such an executable image as a Standard Version of this
|
||||||
|
Package.
|
||||||
|
|
||||||
|
7. C subroutines (or comparably compiled subroutines in other
|
||||||
|
languages) supplied by you and linked into this Package in order to
|
||||||
|
emulate subroutines and variables of the language defined by this
|
||||||
|
Package shall not be considered part of this Package, but are the
|
||||||
|
equivalent of input as in Paragraph 6, provided these subroutines do
|
||||||
|
not change the language in any way that would cause it to fail the
|
||||||
|
regression tests for the language.
|
||||||
|
|
||||||
|
8. Aggregation of this Package with a commercial distribution is always
|
||||||
|
permitted provided that the use of this Package is embedded; that is,
|
||||||
|
when no overt attempt is made to make this Package's interfaces visible
|
||||||
|
to the end user of the commercial distribution. Such use shall not be
|
||||||
|
construed as a distribution of this Package.
|
||||||
|
|
||||||
|
9. The name of the Copyright Holder may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
||||||
|
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
|
||||||
|
The End
|
309
Build.PL
Executable file
309
Build.PL
Executable file
@ -0,0 +1,309 @@
|
|||||||
|
#! /usr/bin/perl -w
|
||||||
|
use Module::Build;
|
||||||
|
use lib qw(lib);
|
||||||
|
use DbFramework::Util;
|
||||||
|
require 't/util.pl';
|
||||||
|
|
||||||
|
$catalog_db = 'dbframework_catalog';
|
||||||
|
%keytypes = (primary => 0, foreign => 1, index => 2);
|
||||||
|
|
||||||
|
Module::Build->prompt(<<EOF, "");
|
||||||
|
|
||||||
|
Because there is so much variation in the syntax for creating
|
||||||
|
databases between different engines, DbFramework requires that you
|
||||||
|
create some databases before it can be installed. Please ensure that
|
||||||
|
each engine you wish to test DbFramework against contains the catalog
|
||||||
|
database '$catalog_db' and a database which can be used for testing.
|
||||||
|
|
||||||
|
Press return to continue.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
my $config = 't/Config.pm';
|
||||||
|
|
||||||
|
unless ( -f $config && Module::Build->prompt("$config exists, use it?", "Y") =~ /^y/i ) {
|
||||||
|
# select drivers to test
|
||||||
|
my @drivers = grep eval "require DBD::$_; 1;", qw/mysql mSQL Pg/;
|
||||||
|
my $drivers = Module::Build->prompt("Enter (space seperated) DBI drivers to test:", join(" ", @drivers));
|
||||||
|
@drivers = split /\s/,$drivers;
|
||||||
|
$test_db = 'test';
|
||||||
|
|
||||||
|
my %driver;
|
||||||
|
for my $driver ( @drivers ) {
|
||||||
|
$test_db = Module::Build->prompt("\nConfiguring for driver 'DBI:$driver'\nEnter the name of your test database:", $test_db);
|
||||||
|
print "\n";
|
||||||
|
for my $db ( $catalog_db, $test_db ) {
|
||||||
|
print "Configuring database '$db'\n";
|
||||||
|
my $dsn;
|
||||||
|
if ($driver eq "mysql") {
|
||||||
|
$dsn = "database=$db";
|
||||||
|
} elsif ($driver eq "mSQL") {
|
||||||
|
$dsn = "database=$db";
|
||||||
|
} elsif ($driver eq "Pg") {
|
||||||
|
$dsn = "dbname=$db";
|
||||||
|
} else {
|
||||||
|
die "unknown DBI driver: $db";
|
||||||
|
}
|
||||||
|
$_ = Module::Build->prompt(qq{Enter the portion of the DSN that DBD::$driver will use to connect()
|
||||||
|
to $db i.e. 'DBI:$driver:[dsn_string]':}, $db);
|
||||||
|
$driver{$driver}->{$db}->{dsn} = "DBI:$driver:$_";
|
||||||
|
($driver{$driver}->{$db}->{u},$driver{$driver}->{$db}->{p})
|
||||||
|
= DbFramework::Util::get_auth();
|
||||||
|
if ( $db eq $catalog_db ) {
|
||||||
|
if ( Module::Build->prompt("Create schema for '$catalog_db' in DBI:$driver?", "N") !~ /^n/i ) {
|
||||||
|
# create catalog schema
|
||||||
|
my %sql = %{catalog_schema()};
|
||||||
|
# default to mysql DDL syntax
|
||||||
|
$ddl = (exists $sql{$driver}) ? $driver : 'mysql';
|
||||||
|
$dsn = $driver{$driver}->{$db}->{dsn};
|
||||||
|
$u = $driver{$driver}->{$db}->{u};
|
||||||
|
$p = $driver{$driver}->{$db}->{p};
|
||||||
|
my $dbh = DbFramework::Util::get_dbh($dsn,$u,$p);
|
||||||
|
$dbh->{PrintError} = 0;
|
||||||
|
for my $table ( qw/c_db c_key c_relationship c_table/ ) {
|
||||||
|
drop_create($catalog_db,$table,undef,$sql{$ddl}->{$table},$dbh);
|
||||||
|
}
|
||||||
|
my($t1,$t2) = ('foo','bar');
|
||||||
|
|
||||||
|
## set db
|
||||||
|
my $sql = qq{
|
||||||
|
INSERT INTO c_db
|
||||||
|
VALUES('$test_db')};
|
||||||
|
my $sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set tables
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_table
|
||||||
|
VALUES('$t1','$test_db','bar')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_table
|
||||||
|
VALUES('$t2','$test_db',NULL)};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set primary keys
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t1','primary',$keytypes{primary},'foo:bar')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t2','primary',$keytypes{primary},'foo')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set keys (indexes)
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t1','foo',$keytypes{index},'bar:baz')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t1','bar',$keytypes{index},'baz:quux')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set foreign keys
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t2','f_foo',$keytypes{foreign},'foo_foo:foo_bar')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_relationship
|
||||||
|
VALUES('$test_db','$t2','f_foo','$t1')
|
||||||
|
};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
print "Done.\n";
|
||||||
|
$dbh->disconnect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open(CONFIG,">$config") || die "Couldn't open config file: $config: $!";
|
||||||
|
print "Writing config file: $config\n";
|
||||||
|
print CONFIG qq{package t::Config;
|
||||||
|
|
||||||
|
\$test_db = '$test_db';
|
||||||
|
\@drivers = qw/@drivers/;
|
||||||
|
\%driver = (};
|
||||||
|
|
||||||
|
while ( my($k,$v) = each %driver ) {
|
||||||
|
print CONFIG "$k => { \n";
|
||||||
|
while ( my($k,$v) = each %$v ) {
|
||||||
|
print CONFIG "$k => { \n";
|
||||||
|
while ( my($k,$v) = each %$v ) {
|
||||||
|
print CONFIG "$k => '$v',";
|
||||||
|
}
|
||||||
|
print CONFIG "},\n";
|
||||||
|
}
|
||||||
|
print CONFIG "},\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print CONFIG qq{);
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
close CONFIG;
|
||||||
|
print <<EOF;
|
||||||
|
|
||||||
|
If you have supplied sensitive information you should remove $config
|
||||||
|
after ensuring that 'make test' passes all tests.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# return a hashref containing DDL to create the catalog for various drivers
|
||||||
|
sub catalog_schema {
|
||||||
|
return { Pg => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_type int DEFAULT '0' NOT NULL,
|
||||||
|
key_columns varchar(255) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,table_name,key_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_key varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
pk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,fk_table,fk_key,pk_table)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
labels varchar(127) DEFAULT '',
|
||||||
|
PRIMARY KEY (table_name,db_name)
|
||||||
|
)
|
||||||
|
} },
|
||||||
|
CSV => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name varchar(50)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name varchar(50),
|
||||||
|
table_name varchar(50),
|
||||||
|
key_name varchar(50),
|
||||||
|
key_type int,
|
||||||
|
key_columns varchar(255)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name varchar(50),
|
||||||
|
fk_table varchar(50),
|
||||||
|
fk_key varchar(50),
|
||||||
|
pk_table varchar(50)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name varchar(50),
|
||||||
|
db_name varchar(50),
|
||||||
|
labels varchar(127)
|
||||||
|
)
|
||||||
|
} },
|
||||||
|
mysql => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_type int DEFAULT '0' NOT NULL,
|
||||||
|
key_columns varchar(255) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,table_name,key_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_key varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
pk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,fk_table,fk_key,pk_table)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
labels varchar(127) DEFAULT '' NULL,
|
||||||
|
PRIMARY KEY (table_name,db_name)
|
||||||
|
)
|
||||||
|
} },
|
||||||
|
mSQL => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name char(50) NOT NULL
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name char(50) NOT NULL,
|
||||||
|
table_name char(50) NOT NULL,
|
||||||
|
key_name char(50) NOT NULL,
|
||||||
|
key_type int NOT NULL,
|
||||||
|
key_columns char(255) NOT NULL
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name char(50) NOT NULL,
|
||||||
|
fk_table char(50) NOT NULL,
|
||||||
|
fk_key char(50) NOT NULL,
|
||||||
|
pk_table char(50) NOT NULL
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name char(50) NOT NULL,
|
||||||
|
db_name char(50) NOT NULL,
|
||||||
|
labels char(127)
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $build = Module::Build->new(
|
||||||
|
dist_name => "DbFramework",
|
||||||
|
dist_version => "1.12",
|
||||||
|
dist_abstract => "Classes for Manipulating DBI Databases, Based on the CDIF Data Model Subject Area",
|
||||||
|
dist_author => "imacat <imacat\@mail.imacat.idv.tw>",
|
||||||
|
license => "perl",
|
||||||
|
sign => 1,
|
||||||
|
|
||||||
|
requires => {
|
||||||
|
"Alias" => 0,
|
||||||
|
"DBI" => 1.06,
|
||||||
|
"CGI" => 0,
|
||||||
|
"Text::FillIn" => 0,
|
||||||
|
"URI::Escape" => 0,
|
||||||
|
"Term::ReadKey" => 0,
|
||||||
|
},
|
||||||
|
build_requires => {
|
||||||
|
"Module::Signature" => 0,
|
||||||
|
},
|
||||||
|
add_to_cleanup => [ "t/Config.pm", "TAGS" ],
|
||||||
|
);
|
||||||
|
|
||||||
|
$build->create_build_script;
|
||||||
|
|
||||||
|
__END__
|
674
COPYING
Normal file
674
COPYING
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
345
Changes
Normal file
345
Changes
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
DbFramework change log
|
||||||
|
|
||||||
|
2008-05-03
|
||||||
|
1. Makefile.PL and Build.PL: Automatic checks and lists only
|
||||||
|
the available DBD drivers for tests.
|
||||||
|
|
||||||
|
2008-05-03 version 1.12
|
||||||
|
1. Makefile.PL and Build.PL: Provide a valid default DSN for
|
||||||
|
DBD::mysql, DBD::mSQL and DBD::Pg.
|
||||||
|
2. Makefile.PL and Build.PL: Do not create the schema if
|
||||||
|
answer is "no".
|
||||||
|
|
||||||
|
2008-04-21
|
||||||
|
1. Changes: Updated.
|
||||||
|
2. Build.PL: Renamed test database from "dbframework_test" to
|
||||||
|
"test", to adapt the convention of MySQL and PostgreSQL.
|
||||||
|
3. TODO: Added.
|
||||||
|
4. AUTHORS: Added.
|
||||||
|
5. Artistic and COPYING: Added.
|
||||||
|
6. Makefile.PL: Added PL_FILES to disable PL files searching
|
||||||
|
behavior, in order to work with ExtUtils::MakeMaker earlier
|
||||||
|
than 6.25 that treats Build.PL as one of the PL files to run.
|
||||||
|
|
||||||
|
2008-04-20
|
||||||
|
1. Added lib and lib/DbFramework subdirectory. Move everything
|
||||||
|
inside, to make the directory cleaner.
|
||||||
|
2. Makefile.PL: Clean up. Removed PMLIBDIRS since it is not
|
||||||
|
required anymore. Removed linkext. linkext is only needed
|
||||||
|
for ExtUtils::MakeMaker before version 5. That is too old
|
||||||
|
today.
|
||||||
|
3. Makefile.PL and Build.PL: Use "use lib qw(lib);
|
||||||
|
use DbFramework::Util;" instead of "require "./Util.pm"", in
|
||||||
|
order to be more portable on different platforms.
|
||||||
|
4. Makefile.PL: Renamed test database from "dbframework_test" to
|
||||||
|
"test", to adapt the convention of MySQL and PostgreSQL.
|
||||||
|
5. README: Moved Paul's BackPen URL to the previous line.
|
||||||
|
|
||||||
|
2008-04-19
|
||||||
|
1. Added lib and lib/DbFramework subdirectory. Move everything
|
||||||
|
inside, to make the directory cleaner.
|
||||||
|
2. Changes: Added, with contents from original Paul's README file.
|
||||||
|
This is to adapt the Perl module convention.
|
||||||
|
|
||||||
|
2008-04-19 version 1.11
|
||||||
|
Maintainance taken over by imacat.
|
||||||
|
1. Makefile.PL: Updated to use prompt() from ExtUtils::MakeMaker
|
||||||
|
instead of reading STDIN directly, in order to prevent infinite
|
||||||
|
loop with automated CPAN testers. (RT#34538)
|
||||||
|
2. t/util.pl: Subroutine yn() removed. It is not used anymore,
|
||||||
|
due to the prompt() fix.
|
||||||
|
3. README: Add notice about the distribution ownership change.
|
||||||
|
4. Makefile.PL: ABSTRACT, AUTHOR, LICENSE and SIGN added to the
|
||||||
|
WriteMakefile() arguments. VERSION is added in replace of
|
||||||
|
VERSION_FROM.
|
||||||
|
5. SIGNATURE: Digital signature added.
|
||||||
|
6. META.yml: YAML distribution meta-nformation added.
|
||||||
|
7. Build.PL: Module::Build build support added.
|
||||||
|
|
||||||
|
Original change log by Paul Sharpe follows.
|
||||||
|
==================================
|
||||||
|
History
|
||||||
|
=======
|
||||||
|
|
||||||
|
15-05-1999 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
RELEASE 1.10
|
||||||
|
|
||||||
|
Added support for PostgreSQL.
|
||||||
|
|
||||||
|
INTERFACE CHANGES
|
||||||
|
PrimaryKey::
|
||||||
|
- New method as_hidden_html()
|
||||||
|
|
||||||
|
30-04-1999 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
RELEASE 1.09
|
||||||
|
|
||||||
|
dbforms.cgi
|
||||||
|
- Can now update primary keys.
|
||||||
|
- Removed dependency on ePerl.
|
||||||
|
- Now takes dsn arguments to cater for variance between DBDs.
|
||||||
|
|
||||||
|
INTERFACE CHANGES
|
||||||
|
Catalog::
|
||||||
|
- set_primary_key() now sets labels if defined in the catalog.
|
||||||
|
DataModel::
|
||||||
|
- New method dsn().
|
||||||
|
- new() now requires name to be the database name as pattern match
|
||||||
|
to get database name forgot that this part of a DBI DSN is *non
|
||||||
|
standard*
|
||||||
|
- init_db_metadata() now requires DSN for catalog as it varies
|
||||||
|
between drivers. Also now allows username/password for authentication
|
||||||
|
against catalog database.
|
||||||
|
ForeignKey::
|
||||||
|
- New method sql_where().
|
||||||
|
Makefile.PL
|
||||||
|
- Moved most of the catalog initialisation here.
|
||||||
|
- Manual creation of databases now required as there's no standard
|
||||||
|
for creating databases. Each driver can use an arbitrarily named
|
||||||
|
database for testing.
|
||||||
|
Persistent::
|
||||||
|
- new() now requires a Catalog object.
|
||||||
|
- update now takes %attributes argument so that a primary key can be
|
||||||
|
updated. The current object state is used to update the row WHERE the
|
||||||
|
primary key matches the values in %attributes.
|
||||||
|
PrimaryKey::
|
||||||
|
- html_select_field() now has 'Any' *and* 'NULL' entries. I have a
|
||||||
|
feeling this still isn't quite right.
|
||||||
|
- New method as_hidden_html()
|
||||||
|
- Signature to new() has changed now that label columns are
|
||||||
|
supported by the catalog.
|
||||||
|
- html_select_field() now follows label columns which are foreign
|
||||||
|
keys back to their associated primary key table and uses the label
|
||||||
|
columns from that table. This gives more meaningful select fields.
|
||||||
|
Table::
|
||||||
|
- init_db_metadata() now requires catalog object argument. This
|
||||||
|
should improve performance as a new catalog object (which involves a
|
||||||
|
database connect()) won't be created every time this method is called.
|
||||||
|
- in_foreign_key() now returns a list of foreign keys containing
|
||||||
|
$attribute
|
||||||
|
DataType::ANSII::
|
||||||
|
- Signature to new() has changed. Now requires $ansii_type
|
||||||
|
argument. All data type objects will need to return their closest
|
||||||
|
ANSII types as this is the $type which needs to be supplied to
|
||||||
|
$dbh->quote($value,$type).
|
||||||
|
- New method ansii_type().
|
||||||
|
|
||||||
|
1-04-1999 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
RELEASE 1.08
|
||||||
|
|
||||||
|
BUG FIX
|
||||||
|
- Test database wasn't being created which was causing tests to
|
||||||
|
fail.
|
||||||
|
|
||||||
|
28-03-1999 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
RELEASE 1.07
|
||||||
|
|
||||||
|
Highlights
|
||||||
|
==========
|
||||||
|
+ Support for Msql (and potentially all DBI drivers.)
|
||||||
|
+ Improved template support.
|
||||||
|
|
||||||
|
= API
|
||||||
|
Attribute::
|
||||||
|
-- BUG FIXES
|
||||||
|
- as_html_form_field() was producing invalid HTML for date fields.
|
||||||
|
- as_html_heading() now takes $bgcolor argument.
|
||||||
|
Catalog.pm (NEW CLASS)
|
||||||
|
DataModel::
|
||||||
|
- new() now takes data source name to move away from dependence on Mysql.
|
||||||
|
- init_db_metadata() now needs to be called explicity (not called in
|
||||||
|
new()).
|
||||||
|
- New methods driver() and db().
|
||||||
|
DataType::ANSII:: (NEW CLASS)
|
||||||
|
DataType/Mysql:: (NEW CLASS)
|
||||||
|
ForeignKey::
|
||||||
|
-- BUG FIX
|
||||||
|
- as_html_form_field() was using pk attribute names to select
|
||||||
|
defaults in select field. Should use fk attribute names.
|
||||||
|
Table::
|
||||||
|
- read_form now takes ($name,$path) arguments.
|
||||||
|
- get_dbh() now takes data source name to move away from dependence on
|
||||||
|
Mysql.
|
||||||
|
Persistent::
|
||||||
|
- New method init_pk()
|
||||||
|
- New method table_qualified_attribute_hashref()
|
||||||
|
- Removed method fill_template(). Templates now handled by Template.pm.
|
||||||
|
- select() now takes extra argument $order.
|
||||||
|
PrimaryKey::
|
||||||
|
- New method as_query_string()
|
||||||
|
- html_select_field() sets null value labels to the string 'NULL'.
|
||||||
|
Table::
|
||||||
|
-- BUG FIXES
|
||||||
|
- insert() should only try to insert values which are defined.
|
||||||
|
- Don't try to update columns where the value is undefined.
|
||||||
|
- as_html_heading() required more specific pattern match.
|
||||||
|
- Quoting in insert() and update() required type to correctly quote
|
||||||
|
numeric fields.
|
||||||
|
- new() was using wrong argument in belongs_to()
|
||||||
|
|
||||||
|
- new() now takes optional DataModel argument
|
||||||
|
- New method belongs_to()
|
||||||
|
- get_attribute_names renamed attribute_names()
|
||||||
|
- New method select_loh()
|
||||||
|
- @columns in select() and select_loh() can contain references to
|
||||||
|
database functions to be applied to the column.
|
||||||
|
- removed methods set_templates(), read_form() and fill_templates()
|
||||||
|
as template handling is now done in Template.pm
|
||||||
|
- as_html_heading() now adds one heading for each key attribute.
|
||||||
|
- insert() returns -1 for non auto increment columns.
|
||||||
|
Template:: (NEW CLASS)
|
||||||
|
Util::
|
||||||
|
- Removed get_db() as it was Mysql specific. The database name can
|
||||||
|
be retrieved from DbFramework::DataModel::db
|
||||||
|
- New sub do_sql()
|
||||||
|
- get_dbh() now takes data source name to move away from
|
||||||
|
dependence on Mysql.
|
||||||
|
|
||||||
|
= Makefile.PL configures drivers to test at 'make test' stage.
|
||||||
|
|
||||||
|
= dbforms.cgi
|
||||||
|
- Now accepts DBI driver CGI parameter (default is Mysql.)
|
||||||
|
|
||||||
|
= Overhaul of test scripts.
|
||||||
|
|
||||||
|
|
||||||
|
12-1-1999 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
RELEASE 1.06
|
||||||
|
|
||||||
|
= UI
|
||||||
|
Default input templates now produce 'datasheet' view.
|
||||||
|
|
||||||
|
= API
|
||||||
|
Attribute::
|
||||||
|
- New method as_html_heading()
|
||||||
|
Key::
|
||||||
|
- New method as_html_heading()
|
||||||
|
PrimaryKey::
|
||||||
|
- New method as_html_heading()
|
||||||
|
Table::
|
||||||
|
- New method as_html_heading()
|
||||||
|
|
||||||
|
|
||||||
|
6-1-1999 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
RELEASE 1.05
|
||||||
|
|
||||||
|
= INTERFACE
|
||||||
|
Table
|
||||||
|
- New method dbh($dbh) (added pod for AUTOLOAD() method)
|
||||||
|
= Improved form and template support
|
||||||
|
- new() now takes list of forms rather than template definitions and
|
||||||
|
evals configuration from config.pl files.
|
||||||
|
- New method read_form()
|
||||||
|
- More pod
|
||||||
|
- Default output templates now handle foreign keys by replacing them
|
||||||
|
with columns from the related table.
|
||||||
|
- <DbValue> template tag can now contain a list of attributes to be
|
||||||
|
substituted for values.
|
||||||
|
dbforms.cgi
|
||||||
|
- fills values from related table for foreign keys in default
|
||||||
|
output template.
|
||||||
|
- Now handles user-defined forms.
|
||||||
|
|
||||||
|
18-Dec-1998 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
RELEASE 1.04
|
||||||
|
|
||||||
|
= INTERFACE
|
||||||
|
ForeignKey
|
||||||
|
- New method as_html_form_field(\%values)
|
||||||
|
Table
|
||||||
|
- as_html_form() now returns fields for foreign keys too.
|
||||||
|
- fill_template() now creates selection boxes for <DbFKey> tags.
|
||||||
|
|
||||||
|
= dbforms.cgi
|
||||||
|
New experimental UI separating search and modify.
|
||||||
|
Catches and displays SQL errors.
|
||||||
|
|
||||||
|
11-Dec-1998 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
RELEASE 1.03
|
||||||
|
- Bug fixes.
|
||||||
|
|
||||||
|
11-Dec-1998 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
RELEASE 1.02
|
||||||
|
- dbforms.cgi HTML forms application
|
||||||
|
- Bug fixes: Table::select(), Attribute::as_html_form_field()
|
||||||
|
- INTERFACE
|
||||||
|
*_h_byname() methods take an array rather than an arrayref
|
||||||
|
Persistent
|
||||||
|
- New method make_class()
|
||||||
|
- New method fill_template()
|
||||||
|
- New method as_html_form()
|
||||||
|
- fill_template() now takes $name argument
|
||||||
|
Table
|
||||||
|
- New method in_foreign_key()
|
||||||
|
- as_html_form() doesn't return <FORM></FORM> container so doesn't
|
||||||
|
require $action argument.
|
||||||
|
- New method in_key()
|
||||||
|
- New method in_primary_key()
|
||||||
|
- New method in_any_key()
|
||||||
|
- New method non_key_attributes()
|
||||||
|
- Removed method html_pk_select_field()
|
||||||
|
- New attribute/methods HAS_FOREIGN_KEYS_H
|
||||||
|
- fill_template() now supports <DbFKey table.name> placeholders.
|
||||||
|
Key
|
||||||
|
- New method belongs_to()
|
||||||
|
- new() now takes a reference to a hash of templates.
|
||||||
|
- fill_template() now takes a template name and reference to a hash of
|
||||||
|
values.
|
||||||
|
- templates() removed
|
||||||
|
- New method set_templates()
|
||||||
|
- New method bgcolor()
|
||||||
|
Attribute
|
||||||
|
- New method _input_template()
|
||||||
|
- New method _output_template()
|
||||||
|
- New method bgcolor()
|
||||||
|
PrimaryKey
|
||||||
|
- New method html_select_field()
|
||||||
|
|
||||||
|
26-Nov-1998 Paul Sharpe <paul@miraclefish.com>
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
RELEASE 1.01 (FIRST PUBLIC RELEASE)
|
||||||
|
|
||||||
|
|
||||||
|
To do
|
||||||
|
=====
|
||||||
|
|
||||||
|
* BUG: dbforms.cgi delete on row consisting of all foreign keys silently fails.
|
||||||
|
|
||||||
|
* Improve dbforms.cgi interface. Perhaps a rename is in order too.
|
||||||
|
I'm thinking of having a single record interface with record
|
||||||
|
navigation. The form can then be designed and specified as an
|
||||||
|
argument.
|
||||||
|
|
||||||
|
* Fri Jan 8, 1999
|
||||||
|
BUG - >1 fk relating to the same pk table in a single table is not
|
||||||
|
handled by dbforms.cgi
|
||||||
|
|
||||||
|
4/1/1999
|
||||||
|
- User-defined templates specify attributes to be used for foreign keys
|
||||||
|
and ordering
|
||||||
|
- Allow relationships between the same table (in different roles).
|
||||||
|
Needs documenting
|
||||||
|
|
||||||
|
* Make Bundle::DbFramework
|
||||||
|
|
||||||
|
* Schema grammar
|
||||||
|
- might be better off using Jeeves (from the Panther book)
|
||||||
|
- should handle relationships of degree >2
|
||||||
|
- could be CDIF compliant
|
||||||
|
|
||||||
|
* Attributes of relationships should be stored so that full schema
|
||||||
|
including attributes of relationships can be built from meta-data.
|
||||||
|
|
||||||
|
* add support for UNIQUE columns (different to ordinary KEYs)
|
||||||
|
|
||||||
|
* make primary key in grammar optional (1:1 relationships)
|
||||||
|
|
||||||
|
* make relationships optional in grammar
|
36
MANIFEST
Normal file
36
MANIFEST
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
Artistic
|
||||||
|
AUTHORS
|
||||||
|
Build.PL
|
||||||
|
Changes
|
||||||
|
COPYING
|
||||||
|
forms/dbforms.cgi
|
||||||
|
lib/DbFramework/Attribute.pm
|
||||||
|
lib/DbFramework/Catalog.pm
|
||||||
|
lib/DbFramework/DataModel.pm
|
||||||
|
lib/DbFramework/DataModelObject.pm
|
||||||
|
lib/DbFramework/DataType/ANSII.pm
|
||||||
|
lib/DbFramework/DataType/Mysql.pm
|
||||||
|
lib/DbFramework/DefinitionObject.pm
|
||||||
|
lib/DbFramework/ForeignKey.pm
|
||||||
|
lib/DbFramework/Key.pm
|
||||||
|
lib/DbFramework/Persistent.pm
|
||||||
|
lib/DbFramework/PrimaryKey.pm
|
||||||
|
lib/DbFramework/Relationship.pm
|
||||||
|
lib/DbFramework/Table.pm
|
||||||
|
lib/DbFramework/Template.pm
|
||||||
|
lib/DbFramework/Util.pm
|
||||||
|
Makefile.PL
|
||||||
|
MANIFEST This list of files
|
||||||
|
META.yml
|
||||||
|
README
|
||||||
|
SIGNATURE
|
||||||
|
t/10base.t
|
||||||
|
t/15catalog.t
|
||||||
|
t/17datatype.t
|
||||||
|
t/20table.t
|
||||||
|
t/30persistent.t
|
||||||
|
t/40template.t
|
||||||
|
t/template
|
||||||
|
t/test/foo/foo.form
|
||||||
|
t/util.pl
|
||||||
|
TODO
|
19
META.yml
Normal file
19
META.yml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
--- #YAML:1.0
|
||||||
|
name: DbFramework
|
||||||
|
version: 1.12
|
||||||
|
abstract: Classes for Manipulating DBI Databases, Based on the CDIF Data Model Subject Area
|
||||||
|
license: perl
|
||||||
|
author:
|
||||||
|
- imacat <imacat@mail.imacat.idv.tw>
|
||||||
|
generated_by: ExtUtils::MakeMaker version 6.44
|
||||||
|
distribution_type: module
|
||||||
|
requires:
|
||||||
|
Alias: 0
|
||||||
|
CGI: 0
|
||||||
|
DBI: 1.06
|
||||||
|
Term::ReadKey: 0
|
||||||
|
Text::FillIn: 0
|
||||||
|
URI::Escape: 0
|
||||||
|
meta-spec:
|
||||||
|
url: http://module-build.sourceforge.net/META-spec-v1.3.html
|
||||||
|
version: 1.3
|
912
Makefile
Normal file
912
Makefile
Normal file
@ -0,0 +1,912 @@
|
|||||||
|
# This Makefile is for the DbFramework extension to perl.
|
||||||
|
#
|
||||||
|
# It was generated automatically by MakeMaker version
|
||||||
|
# 6.44 (Revision: 54639) from the contents of
|
||||||
|
# Makefile.PL. Don't edit this file, edit Makefile.PL instead.
|
||||||
|
#
|
||||||
|
# ANY CHANGES MADE HERE WILL BE LOST!
|
||||||
|
#
|
||||||
|
# MakeMaker ARGV: ()
|
||||||
|
#
|
||||||
|
# MakeMaker Parameters:
|
||||||
|
|
||||||
|
# ABSTRACT => q[Classes for Manipulating DBI Databases, Based on the CDIF Data Model Subject Area]
|
||||||
|
# AUTHOR => q[imacat <imacat@mail.imacat.idv.tw>]
|
||||||
|
# LICENSE => q[perl]
|
||||||
|
# NAME => q[DbFramework]
|
||||||
|
# PL_FILES => { }
|
||||||
|
# PREREQ_PM => { Alias=>q[0], URI::Escape=>q[0], Text::FillIn=>q[0], Term::ReadKey=>q[0], CGI=>q[0], DBI=>q[1.06] }
|
||||||
|
# SIGN => q[1]
|
||||||
|
# VERSION => q[1.12]
|
||||||
|
# clean => { FILES=>q[t/Config.pm TAGS] }
|
||||||
|
# dist => { COMPRESS=>q[gzip -9], SUFFIX=>q[.gz] }
|
||||||
|
|
||||||
|
# --- MakeMaker post_initialize section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker const_config section:
|
||||||
|
|
||||||
|
# These definitions are from config.sh (via /usr/lib/perl5/5.8.8/x86_64-linux-thread-multi-ld/Config.pm).
|
||||||
|
# They may have been overridden via Makefile.PL or on the command line.
|
||||||
|
AR = ar
|
||||||
|
CC = gcc
|
||||||
|
CCCDLFLAGS = -fPIC
|
||||||
|
CCDLFLAGS = -Wl,-E -Wl,-rpath,/usr/lib/perl5/5.8.8/x86_64-linux-thread-multi-ld/CORE
|
||||||
|
DLEXT = so
|
||||||
|
DLSRC = dl_dlopen.xs
|
||||||
|
EXE_EXT =
|
||||||
|
FULL_AR = /usr/bin/ar
|
||||||
|
LD = gcc
|
||||||
|
LDDLFLAGS = -shared -L/usr/local/lib
|
||||||
|
LDFLAGS = -L/usr/local/lib
|
||||||
|
LIBC = /lib/libc-2.3.6.so
|
||||||
|
LIB_EXT = .a
|
||||||
|
OBJ_EXT = .o
|
||||||
|
OSNAME = linux
|
||||||
|
OSVERS = 2.6.22.10
|
||||||
|
RANLIB = :
|
||||||
|
SITELIBEXP = /usr/lib/perl5/site_perl/5.8.8
|
||||||
|
SITEARCHEXP = /usr/lib/perl5/site_perl/5.8.8/x86_64-linux-thread-multi-ld
|
||||||
|
SO = so
|
||||||
|
VENDORARCHEXP =
|
||||||
|
VENDORLIBEXP =
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker constants section:
|
||||||
|
AR_STATIC_ARGS = cr
|
||||||
|
DIRFILESEP = /
|
||||||
|
DFSEP = $(DIRFILESEP)
|
||||||
|
NAME = DbFramework
|
||||||
|
NAME_SYM = DbFramework
|
||||||
|
VERSION = 1.12
|
||||||
|
VERSION_MACRO = VERSION
|
||||||
|
VERSION_SYM = 1_12
|
||||||
|
DEFINE_VERSION = -D$(VERSION_MACRO)=\"$(VERSION)\"
|
||||||
|
XS_VERSION = 1.12
|
||||||
|
XS_VERSION_MACRO = XS_VERSION
|
||||||
|
XS_DEFINE_VERSION = -D$(XS_VERSION_MACRO)=\"$(XS_VERSION)\"
|
||||||
|
INST_ARCHLIB = blib/arch
|
||||||
|
INST_SCRIPT = blib/script
|
||||||
|
INST_BIN = blib/bin
|
||||||
|
INST_LIB = blib/lib
|
||||||
|
INST_MAN1DIR = blib/man1
|
||||||
|
INST_MAN3DIR = blib/man3
|
||||||
|
MAN1EXT = 1
|
||||||
|
MAN3EXT = 3
|
||||||
|
INSTALLDIRS = site
|
||||||
|
DESTDIR =
|
||||||
|
PREFIX = $(SITEPREFIX)
|
||||||
|
PERLPREFIX = /usr
|
||||||
|
SITEPREFIX = /usr
|
||||||
|
VENDORPREFIX =
|
||||||
|
INSTALLPRIVLIB = /usr/lib/perl5/5.8.8
|
||||||
|
DESTINSTALLPRIVLIB = $(DESTDIR)$(INSTALLPRIVLIB)
|
||||||
|
INSTALLSITELIB = /usr/lib/perl5/site_perl/5.8.8
|
||||||
|
DESTINSTALLSITELIB = $(DESTDIR)$(INSTALLSITELIB)
|
||||||
|
INSTALLVENDORLIB =
|
||||||
|
DESTINSTALLVENDORLIB = $(DESTDIR)$(INSTALLVENDORLIB)
|
||||||
|
INSTALLARCHLIB = /usr/lib/perl5/5.8.8/x86_64-linux-thread-multi-ld
|
||||||
|
DESTINSTALLARCHLIB = $(DESTDIR)$(INSTALLARCHLIB)
|
||||||
|
INSTALLSITEARCH = /usr/lib/perl5/site_perl/5.8.8/x86_64-linux-thread-multi-ld
|
||||||
|
DESTINSTALLSITEARCH = $(DESTDIR)$(INSTALLSITEARCH)
|
||||||
|
INSTALLVENDORARCH =
|
||||||
|
DESTINSTALLVENDORARCH = $(DESTDIR)$(INSTALLVENDORARCH)
|
||||||
|
INSTALLBIN = /usr/bin
|
||||||
|
DESTINSTALLBIN = $(DESTDIR)$(INSTALLBIN)
|
||||||
|
INSTALLSITEBIN = /usr/bin
|
||||||
|
DESTINSTALLSITEBIN = $(DESTDIR)$(INSTALLSITEBIN)
|
||||||
|
INSTALLVENDORBIN =
|
||||||
|
DESTINSTALLVENDORBIN = $(DESTDIR)$(INSTALLVENDORBIN)
|
||||||
|
INSTALLSCRIPT = /usr/bin
|
||||||
|
DESTINSTALLSCRIPT = $(DESTDIR)$(INSTALLSCRIPT)
|
||||||
|
INSTALLSITESCRIPT = /usr/bin
|
||||||
|
DESTINSTALLSITESCRIPT = $(DESTDIR)$(INSTALLSITESCRIPT)
|
||||||
|
INSTALLVENDORSCRIPT =
|
||||||
|
DESTINSTALLVENDORSCRIPT = $(DESTDIR)$(INSTALLVENDORSCRIPT)
|
||||||
|
INSTALLMAN1DIR = /usr/share/man/man1
|
||||||
|
DESTINSTALLMAN1DIR = $(DESTDIR)$(INSTALLMAN1DIR)
|
||||||
|
INSTALLSITEMAN1DIR = /usr/share/man/man1
|
||||||
|
DESTINSTALLSITEMAN1DIR = $(DESTDIR)$(INSTALLSITEMAN1DIR)
|
||||||
|
INSTALLVENDORMAN1DIR =
|
||||||
|
DESTINSTALLVENDORMAN1DIR = $(DESTDIR)$(INSTALLVENDORMAN1DIR)
|
||||||
|
INSTALLMAN3DIR = /usr/share/man/man3
|
||||||
|
DESTINSTALLMAN3DIR = $(DESTDIR)$(INSTALLMAN3DIR)
|
||||||
|
INSTALLSITEMAN3DIR = /usr/share/man/man3
|
||||||
|
DESTINSTALLSITEMAN3DIR = $(DESTDIR)$(INSTALLSITEMAN3DIR)
|
||||||
|
INSTALLVENDORMAN3DIR =
|
||||||
|
DESTINSTALLVENDORMAN3DIR = $(DESTDIR)$(INSTALLVENDORMAN3DIR)
|
||||||
|
PERL_LIB = /usr/lib/perl5/5.8.8
|
||||||
|
PERL_ARCHLIB = /usr/lib/perl5/5.8.8/x86_64-linux-thread-multi-ld
|
||||||
|
LIBPERL_A = libperl.a
|
||||||
|
FIRST_MAKEFILE = Makefile
|
||||||
|
MAKEFILE_OLD = Makefile.old
|
||||||
|
MAKE_APERL_FILE = Makefile.aperl
|
||||||
|
PERLMAINCC = $(CC)
|
||||||
|
PERL_INC = /usr/lib/perl5/5.8.8/x86_64-linux-thread-multi-ld/CORE
|
||||||
|
PERL = /usr/bin/perl
|
||||||
|
FULLPERL = /usr/bin/perl
|
||||||
|
ABSPERL = $(PERL)
|
||||||
|
PERLRUN = $(PERL)
|
||||||
|
FULLPERLRUN = $(FULLPERL)
|
||||||
|
ABSPERLRUN = $(ABSPERL)
|
||||||
|
PERLRUNINST = $(PERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
|
||||||
|
FULLPERLRUNINST = $(FULLPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
|
||||||
|
ABSPERLRUNINST = $(ABSPERLRUN) "-I$(INST_ARCHLIB)" "-I$(INST_LIB)"
|
||||||
|
PERL_CORE = 0
|
||||||
|
PERM_RW = 644
|
||||||
|
PERM_RWX = 755
|
||||||
|
|
||||||
|
MAKEMAKER = /usr/lib/perl5/5.8.8/ExtUtils/MakeMaker.pm
|
||||||
|
MM_VERSION = 6.44
|
||||||
|
MM_REVISION = 54639
|
||||||
|
|
||||||
|
# FULLEXT = Pathname for extension directory (eg Foo/Bar/Oracle).
|
||||||
|
# BASEEXT = Basename part of FULLEXT. May be just equal FULLEXT. (eg Oracle)
|
||||||
|
# PARENT_NAME = NAME without BASEEXT and no trailing :: (eg Foo::Bar)
|
||||||
|
# DLBASE = Basename part of dynamic library. May be just equal BASEEXT.
|
||||||
|
MAKE = make
|
||||||
|
FULLEXT = DbFramework
|
||||||
|
BASEEXT = DbFramework
|
||||||
|
PARENT_NAME =
|
||||||
|
DLBASE = $(BASEEXT)
|
||||||
|
VERSION_FROM =
|
||||||
|
OBJECT =
|
||||||
|
LDFROM = $(OBJECT)
|
||||||
|
LINKTYPE = dynamic
|
||||||
|
BOOTDEP =
|
||||||
|
|
||||||
|
# Handy lists of source code files:
|
||||||
|
XS_FILES =
|
||||||
|
C_FILES =
|
||||||
|
O_FILES =
|
||||||
|
H_FILES =
|
||||||
|
MAN1PODS =
|
||||||
|
MAN3PODS = lib/DbFramework/Attribute.pm \
|
||||||
|
lib/DbFramework/Catalog.pm \
|
||||||
|
lib/DbFramework/DataModel.pm \
|
||||||
|
lib/DbFramework/DataModelObject.pm \
|
||||||
|
lib/DbFramework/DataType/ANSII.pm \
|
||||||
|
lib/DbFramework/DataType/Mysql.pm \
|
||||||
|
lib/DbFramework/DefinitionObject.pm \
|
||||||
|
lib/DbFramework/ForeignKey.pm \
|
||||||
|
lib/DbFramework/Key.pm \
|
||||||
|
lib/DbFramework/Persistent.pm \
|
||||||
|
lib/DbFramework/PrimaryKey.pm \
|
||||||
|
lib/DbFramework/Table.pm \
|
||||||
|
lib/DbFramework/Template.pm \
|
||||||
|
lib/DbFramework/Util.pm
|
||||||
|
|
||||||
|
# Where is the Config information that we are using/depend on
|
||||||
|
CONFIGDEP = $(PERL_ARCHLIB)$(DFSEP)Config.pm $(PERL_INC)$(DFSEP)config.h
|
||||||
|
|
||||||
|
# Where to build things
|
||||||
|
INST_LIBDIR = $(INST_LIB)
|
||||||
|
INST_ARCHLIBDIR = $(INST_ARCHLIB)
|
||||||
|
|
||||||
|
INST_AUTODIR = $(INST_LIB)/auto/$(FULLEXT)
|
||||||
|
INST_ARCHAUTODIR = $(INST_ARCHLIB)/auto/$(FULLEXT)
|
||||||
|
|
||||||
|
INST_STATIC =
|
||||||
|
INST_DYNAMIC =
|
||||||
|
INST_BOOT =
|
||||||
|
|
||||||
|
# Extra linker info
|
||||||
|
EXPORT_LIST =
|
||||||
|
PERL_ARCHIVE =
|
||||||
|
PERL_ARCHIVE_AFTER =
|
||||||
|
|
||||||
|
|
||||||
|
TO_INST_PM = lib/DbFramework/Attribute.pm \
|
||||||
|
lib/DbFramework/Catalog.pm \
|
||||||
|
lib/DbFramework/DataModel.pm \
|
||||||
|
lib/DbFramework/DataModelObject.pm \
|
||||||
|
lib/DbFramework/DataType/ANSII.pm \
|
||||||
|
lib/DbFramework/DataType/Mysql.pm \
|
||||||
|
lib/DbFramework/DefinitionObject.pm \
|
||||||
|
lib/DbFramework/ForeignKey.pm \
|
||||||
|
lib/DbFramework/Key.pm \
|
||||||
|
lib/DbFramework/Persistent.pm \
|
||||||
|
lib/DbFramework/PrimaryKey.pm \
|
||||||
|
lib/DbFramework/Relationship.pm \
|
||||||
|
lib/DbFramework/Table.pm \
|
||||||
|
lib/DbFramework/Template.pm \
|
||||||
|
lib/DbFramework/Util.pm
|
||||||
|
|
||||||
|
PM_TO_BLIB = lib/DbFramework/PrimaryKey.pm \
|
||||||
|
blib/lib/DbFramework/PrimaryKey.pm \
|
||||||
|
lib/DbFramework/DataModelObject.pm \
|
||||||
|
blib/lib/DbFramework/DataModelObject.pm \
|
||||||
|
lib/DbFramework/ForeignKey.pm \
|
||||||
|
blib/lib/DbFramework/ForeignKey.pm \
|
||||||
|
lib/DbFramework/DataType/ANSII.pm \
|
||||||
|
blib/lib/DbFramework/DataType/ANSII.pm \
|
||||||
|
lib/DbFramework/Util.pm \
|
||||||
|
blib/lib/DbFramework/Util.pm \
|
||||||
|
lib/DbFramework/DataModel.pm \
|
||||||
|
blib/lib/DbFramework/DataModel.pm \
|
||||||
|
lib/DbFramework/Persistent.pm \
|
||||||
|
blib/lib/DbFramework/Persistent.pm \
|
||||||
|
lib/DbFramework/Template.pm \
|
||||||
|
blib/lib/DbFramework/Template.pm \
|
||||||
|
lib/DbFramework/Key.pm \
|
||||||
|
blib/lib/DbFramework/Key.pm \
|
||||||
|
lib/DbFramework/DataType/Mysql.pm \
|
||||||
|
blib/lib/DbFramework/DataType/Mysql.pm \
|
||||||
|
lib/DbFramework/Relationship.pm \
|
||||||
|
blib/lib/DbFramework/Relationship.pm \
|
||||||
|
lib/DbFramework/Catalog.pm \
|
||||||
|
blib/lib/DbFramework/Catalog.pm \
|
||||||
|
lib/DbFramework/Table.pm \
|
||||||
|
blib/lib/DbFramework/Table.pm \
|
||||||
|
lib/DbFramework/Attribute.pm \
|
||||||
|
blib/lib/DbFramework/Attribute.pm \
|
||||||
|
lib/DbFramework/DefinitionObject.pm \
|
||||||
|
blib/lib/DbFramework/DefinitionObject.pm
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker platform_constants section:
|
||||||
|
MM_Unix_VERSION = 6.44
|
||||||
|
PERL_MALLOC_DEF = -DPERL_EXTMALLOC_DEF -Dmalloc=Perl_malloc -Dfree=Perl_mfree -Drealloc=Perl_realloc -Dcalloc=Perl_calloc
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker tool_autosplit section:
|
||||||
|
# Usage: $(AUTOSPLITFILE) FileToSplit AutoDirToSplitInto
|
||||||
|
AUTOSPLITFILE = $(ABSPERLRUN) -e 'use AutoSplit; autosplit($$ARGV[0], $$ARGV[1], 0, 1, 1)' --
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker tool_xsubpp section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker tools_other section:
|
||||||
|
SHELL = /bin/sh
|
||||||
|
CHMOD = chmod
|
||||||
|
CP = cp
|
||||||
|
MV = mv
|
||||||
|
NOOP = $(SHELL) -c true
|
||||||
|
NOECHO = @
|
||||||
|
RM_F = rm -f
|
||||||
|
RM_RF = rm -rf
|
||||||
|
TEST_F = test -f
|
||||||
|
TOUCH = touch
|
||||||
|
UMASK_NULL = umask 0
|
||||||
|
DEV_NULL = > /dev/null 2>&1
|
||||||
|
MKPATH = $(ABSPERLRUN) "-MExtUtils::Command" -e mkpath
|
||||||
|
EQUALIZE_TIMESTAMP = $(ABSPERLRUN) "-MExtUtils::Command" -e eqtime
|
||||||
|
ECHO = echo
|
||||||
|
ECHO_N = echo -n
|
||||||
|
UNINST = 0
|
||||||
|
VERBINST = 0
|
||||||
|
MOD_INSTALL = $(ABSPERLRUN) -MExtUtils::Install -e 'install({@ARGV}, '\''$(VERBINST)'\'', 0, '\''$(UNINST)'\'');' --
|
||||||
|
DOC_INSTALL = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e perllocal_install
|
||||||
|
UNINSTALL = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e uninstall
|
||||||
|
WARN_IF_OLD_PACKLIST = $(ABSPERLRUN) "-MExtUtils::Command::MM" -e warn_if_old_packlist
|
||||||
|
MACROSTART =
|
||||||
|
MACROEND =
|
||||||
|
USEMAKEFILE = -f
|
||||||
|
FIXIN = $(PERLRUN) "-MExtUtils::MY" -e "MY->fixin(shift)"
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker makemakerdflt section:
|
||||||
|
makemakerdflt : all
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dist section:
|
||||||
|
TAR = tar
|
||||||
|
TARFLAGS = cvf
|
||||||
|
ZIP = zip
|
||||||
|
ZIPFLAGS = -r
|
||||||
|
COMPRESS = gzip -9
|
||||||
|
SUFFIX = .gz
|
||||||
|
SHAR = shar
|
||||||
|
PREOP = $(NOECHO) $(NOOP)
|
||||||
|
POSTOP = $(NOECHO) $(NOOP)
|
||||||
|
TO_UNIX = $(NOECHO) $(NOOP)
|
||||||
|
CI = ci -u
|
||||||
|
RCS_LABEL = rcs -Nv$(VERSION_SYM): -q
|
||||||
|
DIST_CP = best
|
||||||
|
DIST_DEFAULT = tardist
|
||||||
|
DISTNAME = DbFramework
|
||||||
|
DISTVNAME = DbFramework-1.12
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker macro section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker depend section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker cflags section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker const_loadlibs section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker const_cccmd section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker post_constants section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker pasthru section:
|
||||||
|
|
||||||
|
PASTHRU = LIBPERL_A="$(LIBPERL_A)"\
|
||||||
|
LINKTYPE="$(LINKTYPE)"\
|
||||||
|
PREFIX="$(PREFIX)"
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker special_targets section:
|
||||||
|
.SUFFIXES : .xs .c .C .cpp .i .s .cxx .cc $(OBJ_EXT)
|
||||||
|
|
||||||
|
.PHONY: all config static dynamic test linkext manifest blibdirs clean realclean disttest distdir
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker c_o section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker xs_c section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker xs_o section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker top_targets section:
|
||||||
|
all :: pure_all manifypods
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
pure_all :: config pm_to_blib subdirs linkext
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
subdirs :: $(MYEXTLIB)
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
config :: $(FIRST_MAKEFILE) blibdirs
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
help :
|
||||||
|
perldoc ExtUtils::MakeMaker
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker blibdirs section:
|
||||||
|
blibdirs : $(INST_LIBDIR)$(DFSEP).exists $(INST_ARCHLIB)$(DFSEP).exists $(INST_AUTODIR)$(DFSEP).exists $(INST_ARCHAUTODIR)$(DFSEP).exists $(INST_BIN)$(DFSEP).exists $(INST_SCRIPT)$(DFSEP).exists $(INST_MAN1DIR)$(DFSEP).exists $(INST_MAN3DIR)$(DFSEP).exists
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
# Backwards compat with 6.18 through 6.25
|
||||||
|
blibdirs.ts : blibdirs
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
$(INST_LIBDIR)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_LIBDIR)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_LIBDIR)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_LIBDIR)$(DFSEP).exists
|
||||||
|
|
||||||
|
$(INST_ARCHLIB)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_ARCHLIB)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_ARCHLIB)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_ARCHLIB)$(DFSEP).exists
|
||||||
|
|
||||||
|
$(INST_AUTODIR)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_AUTODIR)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_AUTODIR)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_AUTODIR)$(DFSEP).exists
|
||||||
|
|
||||||
|
$(INST_ARCHAUTODIR)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_ARCHAUTODIR)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_ARCHAUTODIR)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_ARCHAUTODIR)$(DFSEP).exists
|
||||||
|
|
||||||
|
$(INST_BIN)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_BIN)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_BIN)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_BIN)$(DFSEP).exists
|
||||||
|
|
||||||
|
$(INST_SCRIPT)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_SCRIPT)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_SCRIPT)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_SCRIPT)$(DFSEP).exists
|
||||||
|
|
||||||
|
$(INST_MAN1DIR)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_MAN1DIR)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_MAN1DIR)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_MAN1DIR)$(DFSEP).exists
|
||||||
|
|
||||||
|
$(INST_MAN3DIR)$(DFSEP).exists :: Makefile.PL
|
||||||
|
$(NOECHO) $(MKPATH) $(INST_MAN3DIR)
|
||||||
|
$(NOECHO) $(CHMOD) 755 $(INST_MAN3DIR)
|
||||||
|
$(NOECHO) $(TOUCH) $(INST_MAN3DIR)$(DFSEP).exists
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker linkext section:
|
||||||
|
|
||||||
|
linkext :: $(LINKTYPE)
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dlsyms section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dynamic section:
|
||||||
|
|
||||||
|
dynamic :: $(FIRST_MAKEFILE) $(INST_DYNAMIC) $(INST_BOOT)
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dynamic_bs section:
|
||||||
|
|
||||||
|
BOOTSTRAP =
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dynamic_lib section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker static section:
|
||||||
|
|
||||||
|
## $(INST_PM) has been moved to the all: target.
|
||||||
|
## It remains here for awhile to allow for old usage: "make static"
|
||||||
|
static :: $(FIRST_MAKEFILE) $(INST_STATIC)
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker static_lib section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker manifypods section:
|
||||||
|
|
||||||
|
POD2MAN_EXE = $(PERLRUN) "-MExtUtils::Command::MM" -e pod2man "--"
|
||||||
|
POD2MAN = $(POD2MAN_EXE)
|
||||||
|
|
||||||
|
|
||||||
|
manifypods : pure_all \
|
||||||
|
lib/DbFramework/PrimaryKey.pm \
|
||||||
|
lib/DbFramework/DataModelObject.pm \
|
||||||
|
lib/DbFramework/ForeignKey.pm \
|
||||||
|
lib/DbFramework/DataType/ANSII.pm \
|
||||||
|
lib/DbFramework/Util.pm \
|
||||||
|
lib/DbFramework/DataModel.pm \
|
||||||
|
lib/DbFramework/Persistent.pm \
|
||||||
|
lib/DbFramework/Template.pm \
|
||||||
|
lib/DbFramework/Key.pm \
|
||||||
|
lib/DbFramework/DataType/Mysql.pm \
|
||||||
|
lib/DbFramework/Catalog.pm \
|
||||||
|
lib/DbFramework/Table.pm \
|
||||||
|
lib/DbFramework/Attribute.pm \
|
||||||
|
lib/DbFramework/DefinitionObject.pm
|
||||||
|
$(NOECHO) $(POD2MAN) --section=3 --perm_rw=$(PERM_RW) \
|
||||||
|
lib/DbFramework/PrimaryKey.pm $(INST_MAN3DIR)/DbFramework::PrimaryKey.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/DataModelObject.pm $(INST_MAN3DIR)/DbFramework::DataModelObject.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/ForeignKey.pm $(INST_MAN3DIR)/DbFramework::ForeignKey.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/DataType/ANSII.pm $(INST_MAN3DIR)/DbFramework::DataType::ANSII.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/Util.pm $(INST_MAN3DIR)/DbFramework::Util.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/DataModel.pm $(INST_MAN3DIR)/DbFramework::DataModel.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/Persistent.pm $(INST_MAN3DIR)/DbFramework::Persistent.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/Template.pm $(INST_MAN3DIR)/DbFramework::Template.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/Key.pm $(INST_MAN3DIR)/DbFramework::Key.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/DataType/Mysql.pm $(INST_MAN3DIR)/DbFramework::DataType::Mysql.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/Catalog.pm $(INST_MAN3DIR)/DbFramework::Catalog.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/Table.pm $(INST_MAN3DIR)/DbFramework::Table.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/Attribute.pm $(INST_MAN3DIR)/DbFramework::Attribute.$(MAN3EXT) \
|
||||||
|
lib/DbFramework/DefinitionObject.pm $(INST_MAN3DIR)/DbFramework::DefinitionObject.$(MAN3EXT)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker processPL section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker installbin section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker subdirs section:
|
||||||
|
|
||||||
|
# none
|
||||||
|
|
||||||
|
# --- MakeMaker clean_subdirs section:
|
||||||
|
clean_subdirs :
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker clean section:
|
||||||
|
|
||||||
|
# Delete temporary files but do not touch installed files. We don't delete
|
||||||
|
# the Makefile here so a later make realclean still has a makefile to use.
|
||||||
|
|
||||||
|
clean :: clean_subdirs
|
||||||
|
- $(RM_F) \
|
||||||
|
*$(LIB_EXT) core \
|
||||||
|
core.[0-9] $(INST_ARCHAUTODIR)/extralibs.all \
|
||||||
|
core.[0-9][0-9] $(BASEEXT).bso \
|
||||||
|
pm_to_blib.ts core.[0-9][0-9][0-9][0-9] \
|
||||||
|
$(BASEEXT).x $(BOOTSTRAP) \
|
||||||
|
perl$(EXE_EXT) tmon.out \
|
||||||
|
*$(OBJ_EXT) pm_to_blib \
|
||||||
|
$(INST_ARCHAUTODIR)/extralibs.ld blibdirs.ts \
|
||||||
|
core.[0-9][0-9][0-9][0-9][0-9] *perl.core \
|
||||||
|
core.*perl.*.? $(MAKE_APERL_FILE) \
|
||||||
|
perl $(BASEEXT).def \
|
||||||
|
core.[0-9][0-9][0-9] mon.out \
|
||||||
|
lib$(BASEEXT).def perlmain.c \
|
||||||
|
perl.exe so_locations \
|
||||||
|
$(BASEEXT).exp
|
||||||
|
- $(RM_RF) \
|
||||||
|
t/Config.pm blib \
|
||||||
|
TAGS
|
||||||
|
- $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD) $(DEV_NULL)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker realclean_subdirs section:
|
||||||
|
realclean_subdirs :
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker realclean section:
|
||||||
|
# Delete temporary files (via clean) and also delete dist files
|
||||||
|
realclean purge :: clean realclean_subdirs
|
||||||
|
- $(RM_F) \
|
||||||
|
$(MAKEFILE_OLD) $(FIRST_MAKEFILE)
|
||||||
|
- $(RM_RF) \
|
||||||
|
$(DISTVNAME)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker metafile section:
|
||||||
|
metafile : create_distdir
|
||||||
|
$(NOECHO) $(ECHO) Generating META.yml
|
||||||
|
$(NOECHO) $(ECHO) '--- #YAML:1.0' > META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'name: DbFramework' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'version: 1.12' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'abstract: Classes for Manipulating DBI Databases, Based on the CDIF Data Model Subject Area' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'license: perl' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'author: ' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' - imacat <imacat@mail.imacat.idv.tw>' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'generated_by: ExtUtils::MakeMaker version 6.44' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'distribution_type: module' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'requires: ' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' Alias: 0' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' CGI: 0' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' DBI: 1.06' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' Term::ReadKey: 0' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' Text::FillIn: 0' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' URI::Escape: 0' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) 'meta-spec:' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' url: http://module-build.sourceforge.net/META-spec-v1.3.html' >> META_new.yml
|
||||||
|
$(NOECHO) $(ECHO) ' version: 1.3' >> META_new.yml
|
||||||
|
-$(NOECHO) $(MV) META_new.yml $(DISTVNAME)/META.yml
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker signature section:
|
||||||
|
signature :
|
||||||
|
cpansign -s
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dist_basics section:
|
||||||
|
distclean :: realclean distcheck
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
distcheck :
|
||||||
|
$(PERLRUN) "-MExtUtils::Manifest=fullcheck" -e fullcheck
|
||||||
|
|
||||||
|
skipcheck :
|
||||||
|
$(PERLRUN) "-MExtUtils::Manifest=skipcheck" -e skipcheck
|
||||||
|
|
||||||
|
manifest :
|
||||||
|
$(PERLRUN) "-MExtUtils::Manifest=mkmanifest" -e mkmanifest
|
||||||
|
|
||||||
|
veryclean : realclean
|
||||||
|
$(RM_F) *~ */*~ *.orig */*.orig *.bak */*.bak *.old */*.old
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dist_core section:
|
||||||
|
|
||||||
|
dist : $(DIST_DEFAULT) $(FIRST_MAKEFILE)
|
||||||
|
$(NOECHO) $(ABSPERLRUN) -l -e 'print '\''Warning: Makefile possibly out of date with $(VERSION_FROM)'\''' \
|
||||||
|
-e ' if -e '\''$(VERSION_FROM)'\'' and -M '\''$(VERSION_FROM)'\'' < -M '\''$(FIRST_MAKEFILE)'\'';' --
|
||||||
|
|
||||||
|
tardist : $(DISTVNAME).tar$(SUFFIX)
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
uutardist : $(DISTVNAME).tar$(SUFFIX)
|
||||||
|
uuencode $(DISTVNAME).tar$(SUFFIX) $(DISTVNAME).tar$(SUFFIX) > $(DISTVNAME).tar$(SUFFIX)_uu
|
||||||
|
|
||||||
|
$(DISTVNAME).tar$(SUFFIX) : distdir
|
||||||
|
$(PREOP)
|
||||||
|
$(TO_UNIX)
|
||||||
|
$(TAR) $(TARFLAGS) $(DISTVNAME).tar $(DISTVNAME)
|
||||||
|
$(RM_RF) $(DISTVNAME)
|
||||||
|
$(COMPRESS) $(DISTVNAME).tar
|
||||||
|
$(POSTOP)
|
||||||
|
|
||||||
|
zipdist : $(DISTVNAME).zip
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
$(DISTVNAME).zip : distdir
|
||||||
|
$(PREOP)
|
||||||
|
$(ZIP) $(ZIPFLAGS) $(DISTVNAME).zip $(DISTVNAME)
|
||||||
|
$(RM_RF) $(DISTVNAME)
|
||||||
|
$(POSTOP)
|
||||||
|
|
||||||
|
shdist : distdir
|
||||||
|
$(PREOP)
|
||||||
|
$(SHAR) $(DISTVNAME) > $(DISTVNAME).shar
|
||||||
|
$(RM_RF) $(DISTVNAME)
|
||||||
|
$(POSTOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker distdir section:
|
||||||
|
create_distdir :
|
||||||
|
$(RM_RF) $(DISTVNAME)
|
||||||
|
$(PERLRUN) "-MExtUtils::Manifest=manicopy,maniread" \
|
||||||
|
-e "manicopy(maniread(),'$(DISTVNAME)', '$(DIST_CP)');"
|
||||||
|
|
||||||
|
distdir : create_distdir distmeta distsignature
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dist_test section:
|
||||||
|
disttest : distdir
|
||||||
|
cd $(DISTVNAME) && $(ABSPERLRUN) Makefile.PL
|
||||||
|
cd $(DISTVNAME) && $(MAKE) $(PASTHRU)
|
||||||
|
cd $(DISTVNAME) && $(MAKE) test $(PASTHRU)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker dist_ci section:
|
||||||
|
|
||||||
|
ci :
|
||||||
|
$(PERLRUN) "-MExtUtils::Manifest=maniread" \
|
||||||
|
-e "@all = keys %{ maniread() };" \
|
||||||
|
-e "print(qq{Executing $(CI) @all\n}); system(qq{$(CI) @all});" \
|
||||||
|
-e "print(qq{Executing $(RCS_LABEL) ...\n}); system(qq{$(RCS_LABEL) @all});"
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker distmeta section:
|
||||||
|
distmeta : create_distdir metafile
|
||||||
|
$(NOECHO) cd $(DISTVNAME) && $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{META.yml} => q{Module meta-data (added by MakeMaker)}}) } ' \
|
||||||
|
-e ' or print "Could not add META.yml to MANIFEST: $${'\''@'\''}\n"' --
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker distsignature section:
|
||||||
|
distsignature : create_distdir
|
||||||
|
$(NOECHO) cd $(DISTVNAME) && $(ABSPERLRUN) -MExtUtils::Manifest=maniadd -e 'eval { maniadd({q{SIGNATURE} => q{Public-key signature (added by MakeMaker)}}) } ' \
|
||||||
|
-e ' or print "Could not add SIGNATURE to MANIFEST: $${'\''@'\''}\n"' --
|
||||||
|
$(NOECHO) cd $(DISTVNAME) && $(TOUCH) SIGNATURE
|
||||||
|
cd $(DISTVNAME) && cpansign -s
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker install section:
|
||||||
|
|
||||||
|
install :: all pure_install doc_install
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
install_perl :: all pure_perl_install doc_perl_install
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
install_site :: all pure_site_install doc_site_install
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
install_vendor :: all pure_vendor_install doc_vendor_install
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
pure_install :: pure_$(INSTALLDIRS)_install
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
doc_install :: doc_$(INSTALLDIRS)_install
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
pure__install : pure_site_install
|
||||||
|
$(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site
|
||||||
|
|
||||||
|
doc__install : doc_site_install
|
||||||
|
$(NOECHO) $(ECHO) INSTALLDIRS not defined, defaulting to INSTALLDIRS=site
|
||||||
|
|
||||||
|
pure_perl_install ::
|
||||||
|
$(NOECHO) $(MOD_INSTALL) \
|
||||||
|
read $(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist \
|
||||||
|
write $(DESTINSTALLARCHLIB)/auto/$(FULLEXT)/.packlist \
|
||||||
|
$(INST_LIB) $(DESTINSTALLPRIVLIB) \
|
||||||
|
$(INST_ARCHLIB) $(DESTINSTALLARCHLIB) \
|
||||||
|
$(INST_BIN) $(DESTINSTALLBIN) \
|
||||||
|
$(INST_SCRIPT) $(DESTINSTALLSCRIPT) \
|
||||||
|
$(INST_MAN1DIR) $(DESTINSTALLMAN1DIR) \
|
||||||
|
$(INST_MAN3DIR) $(DESTINSTALLMAN3DIR)
|
||||||
|
$(NOECHO) $(WARN_IF_OLD_PACKLIST) \
|
||||||
|
$(SITEARCHEXP)/auto/$(FULLEXT)
|
||||||
|
|
||||||
|
|
||||||
|
pure_site_install ::
|
||||||
|
$(NOECHO) $(MOD_INSTALL) \
|
||||||
|
read $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist \
|
||||||
|
write $(DESTINSTALLSITEARCH)/auto/$(FULLEXT)/.packlist \
|
||||||
|
$(INST_LIB) $(DESTINSTALLSITELIB) \
|
||||||
|
$(INST_ARCHLIB) $(DESTINSTALLSITEARCH) \
|
||||||
|
$(INST_BIN) $(DESTINSTALLSITEBIN) \
|
||||||
|
$(INST_SCRIPT) $(DESTINSTALLSITESCRIPT) \
|
||||||
|
$(INST_MAN1DIR) $(DESTINSTALLSITEMAN1DIR) \
|
||||||
|
$(INST_MAN3DIR) $(DESTINSTALLSITEMAN3DIR)
|
||||||
|
$(NOECHO) $(WARN_IF_OLD_PACKLIST) \
|
||||||
|
$(PERL_ARCHLIB)/auto/$(FULLEXT)
|
||||||
|
|
||||||
|
pure_vendor_install ::
|
||||||
|
$(NOECHO) $(MOD_INSTALL) \
|
||||||
|
read $(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist \
|
||||||
|
write $(DESTINSTALLVENDORARCH)/auto/$(FULLEXT)/.packlist \
|
||||||
|
$(INST_LIB) $(DESTINSTALLVENDORLIB) \
|
||||||
|
$(INST_ARCHLIB) $(DESTINSTALLVENDORARCH) \
|
||||||
|
$(INST_BIN) $(DESTINSTALLVENDORBIN) \
|
||||||
|
$(INST_SCRIPT) $(DESTINSTALLVENDORSCRIPT) \
|
||||||
|
$(INST_MAN1DIR) $(DESTINSTALLVENDORMAN1DIR) \
|
||||||
|
$(INST_MAN3DIR) $(DESTINSTALLVENDORMAN3DIR)
|
||||||
|
|
||||||
|
doc_perl_install ::
|
||||||
|
$(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
|
||||||
|
-$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
|
||||||
|
-$(NOECHO) $(DOC_INSTALL) \
|
||||||
|
"Module" "$(NAME)" \
|
||||||
|
"installed into" "$(INSTALLPRIVLIB)" \
|
||||||
|
LINKTYPE "$(LINKTYPE)" \
|
||||||
|
VERSION "$(VERSION)" \
|
||||||
|
EXE_FILES "$(EXE_FILES)" \
|
||||||
|
>> $(DESTINSTALLARCHLIB)/perllocal.pod
|
||||||
|
|
||||||
|
doc_site_install ::
|
||||||
|
$(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
|
||||||
|
-$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
|
||||||
|
-$(NOECHO) $(DOC_INSTALL) \
|
||||||
|
"Module" "$(NAME)" \
|
||||||
|
"installed into" "$(INSTALLSITELIB)" \
|
||||||
|
LINKTYPE "$(LINKTYPE)" \
|
||||||
|
VERSION "$(VERSION)" \
|
||||||
|
EXE_FILES "$(EXE_FILES)" \
|
||||||
|
>> $(DESTINSTALLARCHLIB)/perllocal.pod
|
||||||
|
|
||||||
|
doc_vendor_install ::
|
||||||
|
$(NOECHO) $(ECHO) Appending installation info to $(DESTINSTALLARCHLIB)/perllocal.pod
|
||||||
|
-$(NOECHO) $(MKPATH) $(DESTINSTALLARCHLIB)
|
||||||
|
-$(NOECHO) $(DOC_INSTALL) \
|
||||||
|
"Module" "$(NAME)" \
|
||||||
|
"installed into" "$(INSTALLVENDORLIB)" \
|
||||||
|
LINKTYPE "$(LINKTYPE)" \
|
||||||
|
VERSION "$(VERSION)" \
|
||||||
|
EXE_FILES "$(EXE_FILES)" \
|
||||||
|
>> $(DESTINSTALLARCHLIB)/perllocal.pod
|
||||||
|
|
||||||
|
|
||||||
|
uninstall :: uninstall_from_$(INSTALLDIRS)dirs
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
uninstall_from_perldirs ::
|
||||||
|
$(NOECHO) $(UNINSTALL) $(PERL_ARCHLIB)/auto/$(FULLEXT)/.packlist
|
||||||
|
|
||||||
|
uninstall_from_sitedirs ::
|
||||||
|
$(NOECHO) $(UNINSTALL) $(SITEARCHEXP)/auto/$(FULLEXT)/.packlist
|
||||||
|
|
||||||
|
uninstall_from_vendordirs ::
|
||||||
|
$(NOECHO) $(UNINSTALL) $(VENDORARCHEXP)/auto/$(FULLEXT)/.packlist
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker force section:
|
||||||
|
# Phony target to force checking subdirectories.
|
||||||
|
FORCE :
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker perldepend section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker makefile section:
|
||||||
|
# We take a very conservative approach here, but it's worth it.
|
||||||
|
# We move Makefile to Makefile.old here to avoid gnu make looping.
|
||||||
|
$(FIRST_MAKEFILE) : Makefile.PL $(CONFIGDEP)
|
||||||
|
$(NOECHO) $(ECHO) "Makefile out-of-date with respect to $?"
|
||||||
|
$(NOECHO) $(ECHO) "Cleaning current config before rebuilding Makefile..."
|
||||||
|
-$(NOECHO) $(RM_F) $(MAKEFILE_OLD)
|
||||||
|
-$(NOECHO) $(MV) $(FIRST_MAKEFILE) $(MAKEFILE_OLD)
|
||||||
|
- $(MAKE) $(USEMAKEFILE) $(MAKEFILE_OLD) clean $(DEV_NULL)
|
||||||
|
$(PERLRUN) Makefile.PL
|
||||||
|
$(NOECHO) $(ECHO) "==> Your Makefile has been rebuilt. <=="
|
||||||
|
$(NOECHO) $(ECHO) "==> Please rerun the $(MAKE) command. <=="
|
||||||
|
false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker staticmake section:
|
||||||
|
|
||||||
|
# --- MakeMaker makeaperl section ---
|
||||||
|
MAP_TARGET = perl
|
||||||
|
FULLPERL = /usr/bin/perl
|
||||||
|
|
||||||
|
$(MAP_TARGET) :: static $(MAKE_APERL_FILE)
|
||||||
|
$(MAKE) $(USEMAKEFILE) $(MAKE_APERL_FILE) $@
|
||||||
|
|
||||||
|
$(MAKE_APERL_FILE) : $(FIRST_MAKEFILE) pm_to_blib
|
||||||
|
$(NOECHO) $(ECHO) Writing \"$(MAKE_APERL_FILE)\" for this $(MAP_TARGET)
|
||||||
|
$(NOECHO) $(PERLRUNINST) \
|
||||||
|
Makefile.PL DIR= \
|
||||||
|
MAKEFILE=$(MAKE_APERL_FILE) LINKTYPE=static \
|
||||||
|
MAKEAPERL=1 NORECURS=1 CCCDLFLAGS=
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker test section:
|
||||||
|
|
||||||
|
TEST_VERBOSE=0
|
||||||
|
TEST_TYPE=test_$(LINKTYPE)
|
||||||
|
TEST_FILE = test.pl
|
||||||
|
TEST_FILES = t/*.t
|
||||||
|
TESTDB_SW = -d
|
||||||
|
|
||||||
|
testdb :: testdb_$(LINKTYPE)
|
||||||
|
|
||||||
|
test :: $(TEST_TYPE) subdirs-test
|
||||||
|
|
||||||
|
subdirs-test ::
|
||||||
|
$(NOECHO) $(NOOP)
|
||||||
|
|
||||||
|
|
||||||
|
test_dynamic :: pure_all
|
||||||
|
PERL_DL_NONLAZY=1 $(FULLPERLRUN) "-MExtUtils::Command::MM" "-e" "test_harness($(TEST_VERBOSE), '$(INST_LIB)', '$(INST_ARCHLIB)')" $(TEST_FILES)
|
||||||
|
|
||||||
|
testdb_dynamic :: pure_all
|
||||||
|
PERL_DL_NONLAZY=1 $(FULLPERLRUN) $(TESTDB_SW) "-I$(INST_LIB)" "-I$(INST_ARCHLIB)" $(TEST_FILE)
|
||||||
|
|
||||||
|
test_ : test_dynamic
|
||||||
|
|
||||||
|
test_static :: test_dynamic
|
||||||
|
testdb_static :: testdb_dynamic
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker ppd section:
|
||||||
|
# Creates a PPD (Perl Package Description) for a binary distribution.
|
||||||
|
ppd :
|
||||||
|
$(NOECHO) $(ECHO) '<SOFTPKG NAME="$(DISTNAME)" VERSION="1,12,0,0">' > $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <TITLE>$(DISTNAME)</TITLE>' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <ABSTRACT>Classes for Manipulating DBI Databases, Based on the CDIF Data Model Subject Area</ABSTRACT>' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <AUTHOR>imacat <imacat@mail.imacat.idv.tw></AUTHOR>' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <IMPLEMENTATION>' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <DEPENDENCY NAME="Alias" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <DEPENDENCY NAME="CGI" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <DEPENDENCY NAME="DBI" VERSION="1,06,0,0" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <DEPENDENCY NAME="Term-ReadKey" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <DEPENDENCY NAME="Text-FillIn" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <DEPENDENCY NAME="URI-Escape" VERSION="0,0,0,0" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <OS NAME="$(OSNAME)" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <ARCHITECTURE NAME="x86_64-linux-thread-multi-ld-5.8" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' <CODEBASE HREF="" />' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) ' </IMPLEMENTATION>' >> $(DISTNAME).ppd
|
||||||
|
$(NOECHO) $(ECHO) '</SOFTPKG>' >> $(DISTNAME).ppd
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker pm_to_blib section:
|
||||||
|
|
||||||
|
pm_to_blib : $(TO_INST_PM)
|
||||||
|
$(NOECHO) $(ABSPERLRUN) -MExtUtils::Install -e 'pm_to_blib({@ARGV}, '\''$(INST_LIB)/auto'\'', '\''$(PM_FILTER)'\'')' -- \
|
||||||
|
lib/DbFramework/PrimaryKey.pm blib/lib/DbFramework/PrimaryKey.pm \
|
||||||
|
lib/DbFramework/DataModelObject.pm blib/lib/DbFramework/DataModelObject.pm \
|
||||||
|
lib/DbFramework/ForeignKey.pm blib/lib/DbFramework/ForeignKey.pm \
|
||||||
|
lib/DbFramework/DataType/ANSII.pm blib/lib/DbFramework/DataType/ANSII.pm \
|
||||||
|
lib/DbFramework/Util.pm blib/lib/DbFramework/Util.pm \
|
||||||
|
lib/DbFramework/DataModel.pm blib/lib/DbFramework/DataModel.pm \
|
||||||
|
lib/DbFramework/Persistent.pm blib/lib/DbFramework/Persistent.pm \
|
||||||
|
lib/DbFramework/Template.pm blib/lib/DbFramework/Template.pm \
|
||||||
|
lib/DbFramework/Key.pm blib/lib/DbFramework/Key.pm \
|
||||||
|
lib/DbFramework/DataType/Mysql.pm blib/lib/DbFramework/DataType/Mysql.pm \
|
||||||
|
lib/DbFramework/Relationship.pm blib/lib/DbFramework/Relationship.pm \
|
||||||
|
lib/DbFramework/Catalog.pm blib/lib/DbFramework/Catalog.pm \
|
||||||
|
lib/DbFramework/Table.pm blib/lib/DbFramework/Table.pm \
|
||||||
|
lib/DbFramework/Attribute.pm blib/lib/DbFramework/Attribute.pm \
|
||||||
|
lib/DbFramework/DefinitionObject.pm blib/lib/DbFramework/DefinitionObject.pm
|
||||||
|
$(NOECHO) $(TOUCH) pm_to_blib
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker selfdocument section:
|
||||||
|
|
||||||
|
|
||||||
|
# --- MakeMaker postamble section:
|
||||||
|
|
||||||
|
|
||||||
|
# End.
|
318
Makefile.PL
Executable file
318
Makefile.PL
Executable file
@ -0,0 +1,318 @@
|
|||||||
|
#! /usr/bin/perl -w
|
||||||
|
# See lib/ExtUtils/MakeMaker.pm for details of how to influence
|
||||||
|
# the contents of the Makefile that is written.
|
||||||
|
use ExtUtils::MakeMaker;
|
||||||
|
use lib qw(lib);
|
||||||
|
use DbFramework::Util;
|
||||||
|
require 't/util.pl';
|
||||||
|
|
||||||
|
$catalog_db = 'dbframework_catalog';
|
||||||
|
%keytypes = (primary => 0, foreign => 1, index => 2);
|
||||||
|
|
||||||
|
prompt(<<EOF);
|
||||||
|
|
||||||
|
Because there is so much variation in the syntax for creating
|
||||||
|
databases between different engines, DbFramework requires that you
|
||||||
|
create some databases before it can be installed. Please ensure that
|
||||||
|
each engine you wish to test DbFramework against contains the catalog
|
||||||
|
database '$catalog_db' and a database which can be used for testing.
|
||||||
|
|
||||||
|
Press return to continue.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
my $config = 't/Config.pm';
|
||||||
|
|
||||||
|
unless ( -f $config && prompt("$config exists, use it?", "Y") =~ /^y/i ) {
|
||||||
|
# select drivers to test
|
||||||
|
my @drivers = grep eval "require DBD::$_; 1;", qw/mysql mSQL Pg/;
|
||||||
|
my $drivers = prompt("Enter (space seperated) DBI drivers to test:", join(" ", @drivers));
|
||||||
|
@drivers = split /\s/,$drivers;
|
||||||
|
$test_db = 'test';
|
||||||
|
|
||||||
|
my %driver;
|
||||||
|
for my $driver ( @drivers ) {
|
||||||
|
$test_db = prompt("\nConfiguring for driver 'DBI:$driver'\nEnter the name of your test database:", $test_db);
|
||||||
|
print "\n";
|
||||||
|
for my $db ( $catalog_db, $test_db ) {
|
||||||
|
print "Configuring database '$db'\n";
|
||||||
|
my $dsn;
|
||||||
|
if ($driver eq "mysql") {
|
||||||
|
$dsn = "database=$db";
|
||||||
|
} elsif ($driver eq "mSQL") {
|
||||||
|
$dsn = "database=$db";
|
||||||
|
} elsif ($driver eq "Pg") {
|
||||||
|
$dsn = "dbname=$db";
|
||||||
|
} else {
|
||||||
|
die "unknown DBI driver: $db";
|
||||||
|
}
|
||||||
|
$_ = prompt(qq{Enter the portion of the DSN that DBD::$driver will use to connect()
|
||||||
|
to $db i.e. 'DBI:$driver:[dsn_string]':}, $dsn);
|
||||||
|
$driver{$driver}->{$db}->{dsn} = "DBI:$driver:$_";
|
||||||
|
($driver{$driver}->{$db}->{u},$driver{$driver}->{$db}->{p})
|
||||||
|
= DbFramework::Util::get_auth();
|
||||||
|
if ( $db eq $catalog_db ) {
|
||||||
|
if ( prompt("Create schema for '$catalog_db' in DBI:$driver?", "N") !~ /^n/i ) {
|
||||||
|
# create catalog schema
|
||||||
|
my %sql = %{catalog_schema()};
|
||||||
|
# default to mysql DDL syntax
|
||||||
|
$ddl = (exists $sql{$driver}) ? $driver : 'mysql';
|
||||||
|
$dsn = $driver{$driver}->{$db}->{dsn};
|
||||||
|
$u = $driver{$driver}->{$db}->{u};
|
||||||
|
$p = $driver{$driver}->{$db}->{p};
|
||||||
|
my $dbh = DbFramework::Util::get_dbh($dsn,$u,$p);
|
||||||
|
$dbh->{PrintError} = 0;
|
||||||
|
for my $table ( qw/c_db c_key c_relationship c_table/ ) {
|
||||||
|
drop_create($catalog_db,$table,undef,$sql{$ddl}->{$table},$dbh);
|
||||||
|
}
|
||||||
|
my($t1,$t2) = ('foo','bar');
|
||||||
|
|
||||||
|
## set db
|
||||||
|
my $sql = qq{
|
||||||
|
INSERT INTO c_db
|
||||||
|
VALUES('$test_db')};
|
||||||
|
my $sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set tables
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_table
|
||||||
|
VALUES('$t1','$test_db','bar')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_table
|
||||||
|
VALUES('$t2','$test_db',NULL)};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set primary keys
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t1','primary',$keytypes{primary},'foo:bar')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t2','primary',$keytypes{primary},'foo')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set keys (indexes)
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t1','foo',$keytypes{index},'bar:baz')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t1','bar',$keytypes{index},'baz:quux')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
|
||||||
|
## set foreign keys
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_key
|
||||||
|
VALUES('$test_db','$t2','f_foo',$keytypes{foreign},'foo_foo:foo_bar')};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
$sql = qq{
|
||||||
|
INSERT INTO c_relationship
|
||||||
|
VALUES('$test_db','$t2','f_foo','$t1')
|
||||||
|
};
|
||||||
|
$sth = do_sql($dbh,$sql); $sth->finish;
|
||||||
|
print "Done.\n";
|
||||||
|
$dbh->disconnect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open(CONFIG,">$config") || die "Couldn't open config file: $config: $!";
|
||||||
|
print "Writing config file: $config\n";
|
||||||
|
print CONFIG qq{package t::Config;
|
||||||
|
|
||||||
|
\$test_db = '$test_db';
|
||||||
|
\@drivers = qw/@drivers/;
|
||||||
|
\%driver = (};
|
||||||
|
|
||||||
|
while ( my($k,$v) = each %driver ) {
|
||||||
|
print CONFIG "$k => { \n";
|
||||||
|
while ( my($k,$v) = each %$v ) {
|
||||||
|
print CONFIG "$k => { \n";
|
||||||
|
while ( my($k,$v) = each %$v ) {
|
||||||
|
print CONFIG "$k => '$v',";
|
||||||
|
}
|
||||||
|
print CONFIG "},\n";
|
||||||
|
}
|
||||||
|
print CONFIG "},\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print CONFIG qq{);
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
close CONFIG;
|
||||||
|
print <<EOF;
|
||||||
|
|
||||||
|
If you have supplied sensitive information you should remove $config
|
||||||
|
after ensuring that 'make test' passes all tests.
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# return a hashref containing DDL to create the catalog for various drivers
|
||||||
|
sub catalog_schema {
|
||||||
|
return { Pg => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_type int DEFAULT '0' NOT NULL,
|
||||||
|
key_columns varchar(255) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,table_name,key_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_key varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
pk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,fk_table,fk_key,pk_table)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
labels varchar(127) DEFAULT '',
|
||||||
|
PRIMARY KEY (table_name,db_name)
|
||||||
|
)
|
||||||
|
} },
|
||||||
|
CSV => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name varchar(50)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name varchar(50),
|
||||||
|
table_name varchar(50),
|
||||||
|
key_name varchar(50),
|
||||||
|
key_type int,
|
||||||
|
key_columns varchar(255)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name varchar(50),
|
||||||
|
fk_table varchar(50),
|
||||||
|
fk_key varchar(50),
|
||||||
|
pk_table varchar(50)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name varchar(50),
|
||||||
|
db_name varchar(50),
|
||||||
|
labels varchar(127)
|
||||||
|
)
|
||||||
|
} },
|
||||||
|
mysql => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_type int DEFAULT '0' NOT NULL,
|
||||||
|
key_columns varchar(255) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,table_name,key_name)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_key varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
pk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,fk_table,fk_key,pk_table)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
labels varchar(127) DEFAULT '' NULL,
|
||||||
|
PRIMARY KEY (table_name,db_name)
|
||||||
|
)
|
||||||
|
} },
|
||||||
|
mSQL => { c_db => q{
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name char(50) NOT NULL
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_key => q{
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name char(50) NOT NULL,
|
||||||
|
table_name char(50) NOT NULL,
|
||||||
|
key_name char(50) NOT NULL,
|
||||||
|
key_type int NOT NULL,
|
||||||
|
key_columns char(255) NOT NULL
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_relationship => q{
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name char(50) NOT NULL,
|
||||||
|
fk_table char(50) NOT NULL,
|
||||||
|
fk_key char(50) NOT NULL,
|
||||||
|
pk_table char(50) NOT NULL
|
||||||
|
)
|
||||||
|
},
|
||||||
|
c_table => q{
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name char(50) NOT NULL,
|
||||||
|
db_name char(50) NOT NULL,
|
||||||
|
labels char(127)
|
||||||
|
)
|
||||||
|
} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# LICENSE is only availabe since ExtUtils::MakeMaker 6.30_01
|
||||||
|
use vars qw(%license $eummver);
|
||||||
|
%license = qw();
|
||||||
|
$eummver = $ExtUtils::MakeMaker::VERSION;
|
||||||
|
$eummver =~ s/_//;
|
||||||
|
%license = (LICENSE => "perl") if $eummver > 6.30;
|
||||||
|
|
||||||
|
WriteMakefile(
|
||||||
|
NAME => "DbFramework",
|
||||||
|
VERSION => "1.12",
|
||||||
|
ABSTRACT => "Classes for Manipulating DBI Databases, Based on the CDIF Data Model Subject Area",
|
||||||
|
AUTHOR => "imacat <imacat\@mail.imacat.idv.tw>",
|
||||||
|
%license,
|
||||||
|
PREREQ_PM => {
|
||||||
|
"Alias" => 0,
|
||||||
|
"DBI" => 1.06,
|
||||||
|
"CGI" => 0,
|
||||||
|
"Text::FillIn" => 0,
|
||||||
|
"URI::Escape" => 0,
|
||||||
|
"Term::ReadKey" => 0,
|
||||||
|
},
|
||||||
|
PL_FILES => { },
|
||||||
|
SIGN => 1,
|
||||||
|
|
||||||
|
dist => {
|
||||||
|
COMPRESS => "gzip -9",
|
||||||
|
SUFFIX => ".gz",
|
||||||
|
},
|
||||||
|
clean => {
|
||||||
|
FILES => "t/Config.pm TAGS",
|
||||||
|
},
|
||||||
|
);
|
78
README
Normal file
78
README
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
DbFramework is a collection of classes for manipulating DBI databases.
|
||||||
|
The classes are loosely based on the CDIF Data Model Subject Area.
|
||||||
|
|
||||||
|
Note: Do not use DbFramework. I believe it is outdated and not working
|
||||||
|
anymore. I obtained its ownership only to keep it clean until it is
|
||||||
|
retired. If you were using DbFramework, consider other, more recent
|
||||||
|
solutions like XML instead.
|
||||||
|
|
||||||
|
The last release 1.10 of DbFramework is due 1999-05-13, even before Perl
|
||||||
|
5.005_03. As today is 2008-04-19, that is NINE years ago. It is not
|
||||||
|
suprising DbFramework does not work now. If you are still using CDIF
|
||||||
|
Data Model Subject Area, it might be easier to migrate your code to use
|
||||||
|
XML, than to make DbFramework working. Besides, DbFramework takes
|
||||||
|
a CPAN root namespace, DbFramework::*, which is not right, too.
|
||||||
|
|
||||||
|
DbFramework was written by Paul Sharpe (paul@miraclefish.com, CPAN ID:
|
||||||
|
PSHARPE). If you are looking for older versions, see Paul's BackPen
|
||||||
|
directory: http://backpan.perl.org/authors/id/P/PS/PSHARPE/
|
||||||
|
|
||||||
|
Please report bugs to rt.cpan.org. Even that I may not fix them, they
|
||||||
|
are still public records that could help the others understanding them.
|
||||||
|
|
||||||
|
By imacat <imacat@mail.imacat.idv.tw>, written 2008-04-19.
|
||||||
|
=============
|
||||||
|
DbFramework is a collection of classes for manipulating DBI databases.
|
||||||
|
The classes are loosely based on the CDIF Data Model Subject Area.
|
||||||
|
|
||||||
|
This module will help you to
|
||||||
|
|
||||||
|
- Present data model objects (tables, columns) as HTML
|
||||||
|
- Add persistency to your Perl objects
|
||||||
|
- Manipulate DBI databases through an HTML forms interface
|
||||||
|
|
||||||
|
See the POD for further details.
|
||||||
|
|
||||||
|
Prerequisites
|
||||||
|
=============
|
||||||
|
|
||||||
|
Perl 5.005
|
||||||
|
Alias
|
||||||
|
CGI
|
||||||
|
URI::Escape
|
||||||
|
DBI 1.06
|
||||||
|
Text::FillIn
|
||||||
|
Term::ReadKey
|
||||||
|
|
||||||
|
DbFramework has been successfully built and tested on (at least) the
|
||||||
|
following configurations. In general the driver version is VERY
|
||||||
|
IMPORTANT as DbFramework makes use of some of the newer DBI metadata
|
||||||
|
methods which may only be implemented in development branches of
|
||||||
|
certain drivers.
|
||||||
|
|
||||||
|
OS Driver Database
|
||||||
|
================ ========================== ===================
|
||||||
|
RedHat Linux 5.1 Msql-Mysql-modules-1.21_15 Mysql 3.22.14-gamma
|
||||||
|
Msql-Mysql-modules-1.21_15 Msql 2.0.8
|
||||||
|
DBD-Pg-0.91 PostgreSQL 6.4.2
|
||||||
|
|
||||||
|
Note that DBD::CSV is unlikely to be supported in the near future due
|
||||||
|
to the limitations of this driver.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
1) Ensure you have installed the prerequisites above.
|
||||||
|
|
||||||
|
2) perl Makefile.PL
|
||||||
|
Select each DBD driver you wish to test DbFramework against.
|
||||||
|
make
|
||||||
|
make test
|
||||||
|
You will need permission to create the databases 'dbframework_test'
|
||||||
|
and 'dbframework_catalog' for each DBI driver you chose to test.
|
||||||
|
make install
|
||||||
|
|
||||||
|
To use forms/dbforms.cgi, install it in a CGI directory then 'perldoc
|
||||||
|
forms/dbforms.cgi'.
|
||||||
|
|
||||||
|
paul@miraclefish.com
|
58
SIGNATURE
Normal file
58
SIGNATURE
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
This file contains message digests of all files listed in MANIFEST,
|
||||||
|
signed via the Module::Signature module, version 0.55.
|
||||||
|
|
||||||
|
To verify the content in this distribution, first make sure you have
|
||||||
|
Module::Signature installed, then type:
|
||||||
|
|
||||||
|
% cpansign -v
|
||||||
|
|
||||||
|
It will check each file's integrity, as well as the signature's
|
||||||
|
validity. If "==> Signature verified OK! <==" is not displayed,
|
||||||
|
the distribution may already have been compromised, and you should
|
||||||
|
not run its Makefile.PL or Build.PL.
|
||||||
|
|
||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA1
|
||||||
|
|
||||||
|
SHA1 a679d98cb7145fb68be7570425370ce6224689c0 AUTHORS
|
||||||
|
SHA1 be0627fff2e8aef3d2a14d5d7486babc8a4873ba Artistic
|
||||||
|
SHA1 357d0bf457b5d3f7e5ffa264745b0ca936ec1800 Build.PL
|
||||||
|
SHA1 8624bcdae55baeef00cd11d5dfcfa60f68710a02 COPYING
|
||||||
|
SHA1 a286ec26956d71dbff229e86bea7e989943822a6 Changes
|
||||||
|
SHA1 f571fb7a3ede932cd7af4464f53e307b0ef0a345 MANIFEST
|
||||||
|
SHA1 4ed7c30f29bcfb84abded747fc65d57439f9c0d5 META.yml
|
||||||
|
SHA1 e5b82da01a02ce61f3eefa98d0fcf370c918fd07 Makefile.PL
|
||||||
|
SHA1 819243ec435702f419673c0b0e52074928091c02 README
|
||||||
|
SHA1 069311923a9a5ce342bed8daafcead7a173ada4a TODO
|
||||||
|
SHA1 d738fc0c4750e5cca4c5331a7f81b642c74c6ca8 forms/dbforms.cgi
|
||||||
|
SHA1 3838d1387f5d1d4587a75d720069a6a9dd40d71b lib/DbFramework/Attribute.pm
|
||||||
|
SHA1 66566a46862c7335d4cca4dcce86f6628c8e6ab7 lib/DbFramework/Catalog.pm
|
||||||
|
SHA1 7eeebafc4dddc5f0821bee571ece66460e08f4d0 lib/DbFramework/DataModel.pm
|
||||||
|
SHA1 b55df0160345f967d3a620d761e952f8b06e8121 lib/DbFramework/DataModelObject.pm
|
||||||
|
SHA1 bbfba57d46163b15cba222875987da0fa35874fa lib/DbFramework/DataType/ANSII.pm
|
||||||
|
SHA1 e2bb3c108f3f8e5c415a000a077a95c9a28de12c lib/DbFramework/DataType/Mysql.pm
|
||||||
|
SHA1 03b5b99f872e57e69d3c9c51221d4135a1fa7b61 lib/DbFramework/DefinitionObject.pm
|
||||||
|
SHA1 007da37acd0b05d39eb5a22722067c7c1d5a3627 lib/DbFramework/ForeignKey.pm
|
||||||
|
SHA1 bea535189a79352154d8bd3fcabfd9766042ca45 lib/DbFramework/Key.pm
|
||||||
|
SHA1 607f0330f2f423df32eb77dd357f0eb3371e690f lib/DbFramework/Persistent.pm
|
||||||
|
SHA1 93cf31db275578e1dd81543aff64227b4bda30c8 lib/DbFramework/PrimaryKey.pm
|
||||||
|
SHA1 337e27a37b0ebf173b1307af3ce4899c3b93f635 lib/DbFramework/Relationship.pm
|
||||||
|
SHA1 73e4b0973cd54cde69dc8313bf3daaa3816d0953 lib/DbFramework/Table.pm
|
||||||
|
SHA1 d295442819c677ea01b3f1c0fb1903a473c9d5d9 lib/DbFramework/Template.pm
|
||||||
|
SHA1 7d6b52232e52dcd4026ac52176ba53790ade83c0 lib/DbFramework/Util.pm
|
||||||
|
SHA1 025178ab79a02175f0078661ead7aa978b1a48a4 t/10base.t
|
||||||
|
SHA1 038c3ed12c48d1ccb523cb29c41c88a24803a0cd t/15catalog.t
|
||||||
|
SHA1 360cef80c3cafeefcd47717d75d868a89a9f33f6 t/17datatype.t
|
||||||
|
SHA1 f13586408a7e059991b0191b6d7cb4f496693df7 t/20table.t
|
||||||
|
SHA1 2e11ee0000d5eac1ec349be010ee6e5d8dc81ce3 t/30persistent.t
|
||||||
|
SHA1 61d3a46315dc447e8e1d1976676fe57ac992b063 t/40template.t
|
||||||
|
SHA1 ed7b13a575d7eb7b31eb1530976030c2a990fb30 t/template
|
||||||
|
SHA1 f7888dea771f14b105c2628711bc112b79a90737 t/test/foo/foo.form
|
||||||
|
SHA1 567aa1bf40d2fd42957d0ca95d815790c35f8752 t/util.pl
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GnuPG v1.4.9 (GNU/Linux)
|
||||||
|
|
||||||
|
iEYEARECAAYFAkgbyqYACgkQi9gubzC5S1zXHwCfai7eg9AkpEUizI/5fCnbrZIa
|
||||||
|
hqsAn29CM3sA5/bmzC4zMcoM8iQVGEDO
|
||||||
|
=KWYc
|
||||||
|
-----END PGP SIGNATURE-----
|
22
TODO
Normal file
22
TODO
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
DbFramework TODO list
|
||||||
|
|
||||||
|
- Moved the test tables creation from Makefile.PL/Build.PL to the
|
||||||
|
test suite, and clean up the test tables after the test suite
|
||||||
|
finished.
|
||||||
|
- Remove the Makefile.PL/Build.PL dependency to DbFramework::Util,
|
||||||
|
and hence Term::ReadKey. It does not make sense that
|
||||||
|
Makefile.PL/Build.PL fails to run. CPAN and CPANPLUS shells
|
||||||
|
cannot install with this, too.
|
||||||
|
- Rename t/util.pl to t/util.pm, and remove Makefile.PL/Build.PL
|
||||||
|
dependency to it, too.
|
||||||
|
- Dealing with the installation of forms/dbforms.cgi.
|
||||||
|
- Seperate INSTALL from README.
|
||||||
|
- Register a proper name space and drop the unused.
|
||||||
|
- Transfer the primary maintainer of DbFramework::CandidateKey,
|
||||||
|
DbFramework::Catalog, DbFramework::DataModelObject,
|
||||||
|
DbFramework::DataType::ANSII, DbFramework::DataType::Mysql,
|
||||||
|
DbFramework::DefinitionObject, DbFramework::Relationship and
|
||||||
|
DbFramework::Template to me.
|
||||||
|
- Remove "UNAUTHORIZED RELEASE" from search.cpan.org.
|
||||||
|
- Maybe make DbFramework working again.
|
||||||
|
- Retire DbFramework.
|
273
forms/dbforms.cgi
Executable file
273
forms/dbforms.cgi
Executable file
@ -0,0 +1,273 @@
|
|||||||
|
#!/usr/local/bin/perl -I../..
|
||||||
|
|
||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
dbforms.cgi - Forms interface to DbFramework databases
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
http://foo/cgi_bin/dbforms.cgi?db=foo&db_dsn=mysql:database=foo&c_dsn=mysql:database=dbframework_catalog
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
B<dbforms.cgi> presents a simple HTML forms interface to any database
|
||||||
|
configured to work with B<DbFramework>. The database B<must> have the
|
||||||
|
appropriate catalog entries in the catalog database before it will
|
||||||
|
work with this script (see L<DbFramework::Catalog/"The Catalog">.)
|
||||||
|
|
||||||
|
=head2 Query string arguments
|
||||||
|
|
||||||
|
The following arguments are supported in the query string. Mandatory
|
||||||
|
arguments are shown in B<bold>.
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item B<db>
|
||||||
|
|
||||||
|
The name of the database.
|
||||||
|
|
||||||
|
=item B<db_dsn>
|
||||||
|
|
||||||
|
The portion of the DBI DSN after 'DBI:' to be used to connect to the
|
||||||
|
database e.g. 'mysql:database=foo'.
|
||||||
|
|
||||||
|
=item B<c_dsn>
|
||||||
|
|
||||||
|
The portion of the DBI DSN after 'DBI:' to be used to connect to the
|
||||||
|
catalog database e.g. 'mysql:database=dbframework_catalog'.
|
||||||
|
|
||||||
|
=item B<host>
|
||||||
|
|
||||||
|
The host on which the database is located (default = 'localhost'.)
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::Catalog>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1999 Paul Sharpe. England. All rights reserved. This
|
||||||
|
program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
use lib '../..';
|
||||||
|
use DbFramework::Util;
|
||||||
|
use DbFramework::Persistent;
|
||||||
|
use DbFramework::DataModel;
|
||||||
|
use DbFramework::Template;
|
||||||
|
use DbFramework::Catalog;
|
||||||
|
use CGI qw/:standard/;
|
||||||
|
use URI::Escape;
|
||||||
|
|
||||||
|
$cgi = new CGI;
|
||||||
|
$db = $cgi->param('db') || die "No database specified";
|
||||||
|
$db_dsn = $cgi->param('db_dsn') || die "No database DBI string specified";
|
||||||
|
$c_dsn = $cgi->param('c_dsn') || die "No catalog DBI string specified";
|
||||||
|
$host = $cgi->param('host') || undef;
|
||||||
|
$form = $cgi->param('form') || 'input';
|
||||||
|
$action = $cgi->param('action') || 'select';
|
||||||
|
$dsn = "DBI:$db_dsn";
|
||||||
|
$dsn = "$dsn;host=$host" if $host;
|
||||||
|
$dm = new DbFramework::DataModel($db,$dsn);
|
||||||
|
$dm->dbh->{PrintError} = 0; # ePerl chokes on STDERR
|
||||||
|
$dbh = $dm->dbh; $dbh->{PrintError} = 0;
|
||||||
|
$dm->init_db_metadata("DBI:$c_dsn");
|
||||||
|
|
||||||
|
@tables = @{$dm->collects_table_l};
|
||||||
|
$class = $table = $cgi->param('table') || $tables[0]->name;
|
||||||
|
$template = new DbFramework::Template(undef,\@tables);
|
||||||
|
$template->default($table);
|
||||||
|
|
||||||
|
$code = DbFramework::Persistent->make_class($class);
|
||||||
|
eval $code;
|
||||||
|
|
||||||
|
package main;
|
||||||
|
($t) = $dm->collects_table_h_byname($table);
|
||||||
|
$catalog = new DbFramework::Catalog("DBI:$c_dsn");
|
||||||
|
$thing = new $class($t,$dbh,$catalog);
|
||||||
|
cgi_set_attributes($thing);
|
||||||
|
|
||||||
|
# unless ( $form eq 'input' ) {
|
||||||
|
# $thing->init_pk;
|
||||||
|
# $thing->table->read_form($form);
|
||||||
|
# }
|
||||||
|
|
||||||
|
# unpack composite column name parameters
|
||||||
|
for my $param ( $cgi->param ) {
|
||||||
|
if ( $param =~ /,/ ) {
|
||||||
|
my @columns = split /,/,$param;
|
||||||
|
my @values = split /,/,$cgi->param($param);
|
||||||
|
for ( my $i = 0; $i <= $#columns; $i++ ) {
|
||||||
|
$cgi->param($columns[$i],$values[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cgi_set_attributes {
|
||||||
|
my $thing = shift;
|
||||||
|
my %attributes;
|
||||||
|
for ( $thing->table->attribute_names ) {
|
||||||
|
$attributes{$_} = $cgi->param($_) ne '' ? $cgi->param($_) : undef;
|
||||||
|
}
|
||||||
|
$thing->attributes_h([%attributes]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub error {
|
||||||
|
my $message = shift;
|
||||||
|
print "<font color=#ff0000><strong>ERROR!</strong><p>$message</font>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print $cgi->header;
|
||||||
|
print <<EOF;
|
||||||
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>$db: $table</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<table border=1>
|
||||||
|
<tr>
|
||||||
|
<td valign=top>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td valign=top>
|
||||||
|
<h1>db: $db</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<h4>Tables</h4>
|
||||||
|
<ul>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for ( @{$dm->collects_table_l} ) {
|
||||||
|
my $table = $_->name;
|
||||||
|
print "<li><a href=",$cgi->url,"?db=$db&driver=$driver&db_dsn=$db_dsn&c_dsn=$c_dsn&host=$host&table=$table>$table</a>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print <<EOF;
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
<td valign=top>
|
||||||
|
<table border=0>
|
||||||
|
<tr>
|
||||||
|
<td colspan=2 align=middle>
|
||||||
|
<h1>$table</h1>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if ( $form eq 'input' ) {
|
||||||
|
my $self_url = $cgi->self_url;
|
||||||
|
print "<form method=post action=$self_url>\n";
|
||||||
|
for ( qw(host driver db db_dsn c_dsn table form) ) {
|
||||||
|
print "<input type=hidden name=$_ value=",$$_,">\n";
|
||||||
|
}
|
||||||
|
my $values_hashref = $thing->table_qualified_attribute_hashref;
|
||||||
|
print $thing->table->as_html_heading,"\n<tr>\n";
|
||||||
|
print $template->fill($values_hashref);
|
||||||
|
for ( 'select','insert' ) {
|
||||||
|
print "<td><input type=radio name=action value=$_";
|
||||||
|
print ' checked' if /^$action$/;
|
||||||
|
print "> $_</td>\n";
|
||||||
|
}
|
||||||
|
print <<EOF;
|
||||||
|
<td><input type=submit value="Submit"></td>
|
||||||
|
</form>
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
print <<EOF;
|
||||||
|
</tr>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
my $action = $cgi->param('action') || '';
|
||||||
|
|
||||||
|
SWITCH: {
|
||||||
|
$action eq 'select' &&
|
||||||
|
do {
|
||||||
|
my @names = $thing->table->attribute_names;
|
||||||
|
my $conditions;
|
||||||
|
for ( @names ) {
|
||||||
|
if ( $cgi->param($_) ) {
|
||||||
|
$conditions .= " AND " if $conditions;
|
||||||
|
if ( $thing->table->in_foreign_key($thing->table->contains_h_byname($_)) ) {
|
||||||
|
$conditions .= "$_ = " . $cgi->param($_);
|
||||||
|
} else {
|
||||||
|
$conditions .= "$_ " . $cgi->param($_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
my @things = eval { $thing->select($conditions) };
|
||||||
|
if ( $@ ) {
|
||||||
|
error($@);
|
||||||
|
} else {
|
||||||
|
if ( @things ) {
|
||||||
|
for my $thing ( @things ) {
|
||||||
|
my %attributes = %{$thing->attributes_h};
|
||||||
|
my $url = $cgi->url . "?db=$db&db_dsn=$db_dsn&c_dsn=$c_dsn&host=$host&table=$table&form=$form&action=update";
|
||||||
|
for ( keys(%attributes) ) {
|
||||||
|
$url .= uri_escape("&$_=$attributes{$_}");
|
||||||
|
}
|
||||||
|
# fill template
|
||||||
|
my $values_hashref = $thing->attributes_h;
|
||||||
|
print "<form method=post action=",$cgi->self_url,">\n";
|
||||||
|
for ( qw(host driver db db_dsn c_dsn table form) ) {
|
||||||
|
print "<input type=hidden name=$_ value=",$$_,">\n";
|
||||||
|
}
|
||||||
|
print $thing->table->is_identified_by->as_hidden_html($values_hashref);
|
||||||
|
print "<TR>",$template->fill($thing->table_qualified_attribute_hashref),"\n";
|
||||||
|
print "<td><input type=radio name=action value=update",($action eq 'select') ? ' checked>' : '',"update</td>\n";
|
||||||
|
print "<td><input type=radio name=action value=delete>",($action eq 'delete') ? ' checked' : '',"delete</td>\n";
|
||||||
|
print "<td><input type=submit value='Submit'></td></tr></form>\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print "<TR><TD><strong>No rows matched your query</strong></TD></TR>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last SWITCH;
|
||||||
|
};
|
||||||
|
$action =~ /^(insert|update|delete)$/ &&
|
||||||
|
do {
|
||||||
|
my %attributes;
|
||||||
|
if ( $action =~ /update/ ) {
|
||||||
|
# make update condition from current pk
|
||||||
|
for my $param ( $cgi->param ) {
|
||||||
|
if ( my($pk_column) = $param =~ /^pk_(\w+)$/ ) {
|
||||||
|
$attributes{$pk_column} = $cgi->param($param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cgi_set_attributes($thing);
|
||||||
|
eval { $thing->$action(\%attributes); };
|
||||||
|
error($@) if $@;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$dm->dbh->disconnect;
|
||||||
|
$dbh->disconnect;
|
||||||
|
|
||||||
|
print <<EOF;
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
EOF
|
241
lib/DbFramework/Attribute.pm
Normal file
241
lib/DbFramework/Attribute.pm
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::Attribute - Attribute class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::Attribute;
|
||||||
|
my $a = new DbFramework::Attribute($name,$default_value,$is_optional,$data_type);
|
||||||
|
$name = $a->name($name);
|
||||||
|
$value = $a->default_value($value);
|
||||||
|
$opt = $a->is_optional($boolean);
|
||||||
|
$type = $a->references($data_type);
|
||||||
|
$bgc = $a->bgcolor($bgcolor);
|
||||||
|
$sql = $a->as_sql;
|
||||||
|
$s = $a->as_string;
|
||||||
|
$html = $a->as_html_form_field($value,$type);
|
||||||
|
$html = $a->as_html_heading($bgcolor);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
A B<DbFramework::Attribute> object represents an attribute (column) in
|
||||||
|
a table (entity).
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::Attribute;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
use vars qw($_DEBUG $NAME $DEFAULT_VALUE $IS_OPTIONAL $REFERENCES $BGCOLOR);
|
||||||
|
use Alias;
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
NAME => undef,
|
||||||
|
DEFAULT_VALUE => undef,
|
||||||
|
IS_OPTIONAL => undef,
|
||||||
|
# Attribute 0:N References 0:1 DefinitionObject (DataType)
|
||||||
|
REFERENCES => undef,
|
||||||
|
BGCOLOR => '#007777',
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($name,$default_value,$is_optional,$data_type)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::Attribute> object. I<$name> is the name
|
||||||
|
of the attribute. I<$default_value> is the default value for the
|
||||||
|
attribute. I<$is_optional> should be set to true if the attribute is
|
||||||
|
optional or false. I<$data_type> is a B<DbFramework::DataType>
|
||||||
|
object.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
$self->name(shift);
|
||||||
|
$self->default_value(shift);
|
||||||
|
$self->is_optional(shift);
|
||||||
|
$self->references(shift);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 name($name)
|
||||||
|
|
||||||
|
If I<$name> is supplied sets the attribute name. Returns the
|
||||||
|
attribute name.
|
||||||
|
|
||||||
|
=head2 default_value($value)
|
||||||
|
|
||||||
|
If I<$value> is supplied sets the default value for the attribute.
|
||||||
|
I<$value> should be compatible with the the data type set by
|
||||||
|
references(). Returns the default value for the attribute.
|
||||||
|
|
||||||
|
=head2 is_optional($boolean)
|
||||||
|
|
||||||
|
If I<$boolean> is supplied sets the optionality of the attribute
|
||||||
|
(i.e. whether it can contain NULLs.) I<$boolean> should evaluate to
|
||||||
|
true or false. Returns the optionality of the attribute.
|
||||||
|
|
||||||
|
=head2 references($data_type)
|
||||||
|
|
||||||
|
If I<$data_type> is supplied sets the data type of the attribute.
|
||||||
|
I<$data_type> is an ANSII data type object
|
||||||
|
i.e. B<DbFramework::DataType::ANSII> or a driver-specific object
|
||||||
|
e.g. B<DbFramework::DataType::Mysql>. Returns the data type object.
|
||||||
|
|
||||||
|
=head2 bgcolor($bgcolor)
|
||||||
|
|
||||||
|
If I<$color> is supplied sets the background colour for HTML table
|
||||||
|
headings returned by as_html_heading(). Returns the current
|
||||||
|
background colour.
|
||||||
|
|
||||||
|
=head2 as_sql($dbh)
|
||||||
|
|
||||||
|
Returns a string which can be used to create a column in an SQL
|
||||||
|
'CREATE TABLE' statement. I<$dbh> is a B<DBI> handle.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_sql {
|
||||||
|
my($self,$dbh) = (attr shift,shift);
|
||||||
|
my $sql = "$NAME ";
|
||||||
|
$sql .= $REFERENCES->name;
|
||||||
|
$sql .= '(' . $REFERENCES->length . ')' if $REFERENCES->length;
|
||||||
|
$sql .= " NOT NULL" unless $IS_OPTIONAL;
|
||||||
|
$sql .= " DEFAULT " . $dbh->quote($DEFAULT_VALUE) if $DEFAULT_VALUE;
|
||||||
|
$sql .= ' ' . $REFERENCES->extra if $REFERENCES->extra;
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_form_field($value,$type)
|
||||||
|
|
||||||
|
Returns an HTML form field representation of the attribute. The HTML
|
||||||
|
field type produced depends on the name of object returned by
|
||||||
|
data_type(). This can be overidden by setting I<$type>. I<$value> is
|
||||||
|
the default value to be entered in the generated field.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_form_field {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $value = defined($_[0]) ? $_[0] : '';
|
||||||
|
my $type = defined($_[1]) ? uc($_[1]) : $REFERENCES->name;
|
||||||
|
my $length = $REFERENCES->length || 10;
|
||||||
|
my $html;
|
||||||
|
|
||||||
|
SWITCH: {
|
||||||
|
$type =~ /(INT|DATE)/ &&
|
||||||
|
do {
|
||||||
|
$html = qq{<INPUT NAME="$NAME" VALUE="$value" SIZE=10 TYPE="text">};
|
||||||
|
last SWITCH;
|
||||||
|
};
|
||||||
|
$type =~ /CHAR$/ &&
|
||||||
|
do {
|
||||||
|
$html = qq{<INPUT NAME="$NAME" VALUE='$value' SIZE=30 TYPE="text" MAXLENGTH=$length>};
|
||||||
|
last SWITCH;
|
||||||
|
};
|
||||||
|
$type eq 'TEXT' &&
|
||||||
|
do {
|
||||||
|
$value =~ s/'//g; # remove quotes
|
||||||
|
$html = qq{<TEXTAREA COLS=60 NAME="$NAME" ROWS=4>$value</TEXTAREA>};
|
||||||
|
last SWITCH;
|
||||||
|
};
|
||||||
|
$type eq 'BOOLEAN' &&
|
||||||
|
do {
|
||||||
|
my $y = qq{Yes <INPUT TYPE="RADIO" NAME="$NAME" VALUE=1};
|
||||||
|
my $n = qq{No <INPUT TYPE="RADIO" NAME="$NAME" VALUE=0};
|
||||||
|
$html = $value ? qq{$y CHECKED>\n$n>\n} : qq{$y>\n$n CHECKED>\n};
|
||||||
|
last SWITCH;
|
||||||
|
};
|
||||||
|
# default
|
||||||
|
my $size = ($length < 30) ? $length : 30;
|
||||||
|
$html = qq{<INPUT MAXLENGTH=$size NAME='$NAME' VALUE='$value' SIZE=$size TYPE="text">};
|
||||||
|
}
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_string()
|
||||||
|
|
||||||
|
Return attribute details as a text string.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_string {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $s = "$NAME(" . $REFERENCES->name;
|
||||||
|
$s .= ' ('. $REFERENCES->length . ')' if $REFERENCES->length;
|
||||||
|
$s .= " '$DEFAULT_VALUE'" if $DEFAULT_VALUE;
|
||||||
|
$s .= ' NOT NULL' unless $IS_OPTIONAL;
|
||||||
|
$s .= ' ' . $REFERENCES->extra if $REFERENCES->extra;
|
||||||
|
#print " $FUNCTION" if $FUNCTION;
|
||||||
|
return "$s)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
##----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _input_template {
|
||||||
|
my($self,$t_name) = (attr shift,shift);
|
||||||
|
return qq{<TD><DbField ${t_name}.$NAME></TD>};
|
||||||
|
}
|
||||||
|
|
||||||
|
##----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _output_template {
|
||||||
|
my($self,$t_name) = (attr shift,shift);
|
||||||
|
return qq{<TD BGCOLOR='$BGCOLOR'><DbValue ${t_name}.$NAME></TD>};
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_heading($bgcolor)
|
||||||
|
|
||||||
|
Returns a string for use as a column heading cell in an HTML table.
|
||||||
|
I<$bgcolor> is the background colour to use for the heading.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_heading {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $bgcolor = shift || $BGCOLOR;
|
||||||
|
my $text = $NAME;
|
||||||
|
$text .= ' ('.$REFERENCES->extra.')' if $REFERENCES->extra;
|
||||||
|
qq{<TD BGCOLOR='$bgcolor'>$text</TD>};
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::DataType::ANSII> and L<DbFramework::DataType::Mysql>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998,1999 Paul Sharpe. England. All rights
|
||||||
|
reserved. This program is free software; you can redistribute it
|
||||||
|
and/or modify it under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
326
lib/DbFramework/Catalog.pm
Normal file
326
lib/DbFramework/Catalog.pm
Normal file
@ -0,0 +1,326 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::Catalog - Catalog class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::Catalog;
|
||||||
|
my $c = new DbFramework::Catalog($dsn,$user,$password);
|
||||||
|
$c->set_primary_key($table);
|
||||||
|
$c->set_keys($table);
|
||||||
|
$c->set_foreign_keys($dm);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
B<DbFramework::Catalog> is a class for manipulating the catalog
|
||||||
|
database used by various DbFramework modules and scripts.
|
||||||
|
|
||||||
|
=head2 The Catalog
|
||||||
|
|
||||||
|
DbFramework retrieves as much metadata as possible using DBI. It aims
|
||||||
|
to store the metadata B<not> provided by DBI in a consistent manner
|
||||||
|
across all DBI drivers by using a catalog database called
|
||||||
|
I<dbframework_catalog>. Each database you use with DbFramework
|
||||||
|
B<requires> corresponding key information added to the catalog. The
|
||||||
|
I<dbframework_catalog> database will be created for each driver you
|
||||||
|
test when you build DbFramework. Entries in the catalog only need to
|
||||||
|
be modified when the corresponding database schema changes.
|
||||||
|
|
||||||
|
The following (Mysql) SQL creates the catalog database schema.
|
||||||
|
|
||||||
|
CREATE TABLE c_db (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE c_key (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
key_type int(11) DEFAULT '0' NOT NULL,
|
||||||
|
key_columns varchar(255) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,table_name,key_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE c_relationship (
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
fk_key varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
pk_table varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
PRIMARY KEY (db_name,fk_table,fk_key,pk_table)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE c_table (
|
||||||
|
table_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
db_name varchar(50) DEFAULT '' NOT NULL,
|
||||||
|
labels varchar(127) DEFAULT '',
|
||||||
|
PRIMARY KEY (table_name,db_name)
|
||||||
|
);
|
||||||
|
|
||||||
|
The example below shows the creation of a simple Mysql database and
|
||||||
|
the corresponding catalog entries required by DbFramework.
|
||||||
|
|
||||||
|
CREATE DATABASE foo;
|
||||||
|
use foo;
|
||||||
|
|
||||||
|
CREATE TABLE foo (
|
||||||
|
foo integer not null,
|
||||||
|
bar varchar(50),
|
||||||
|
KEY var(bar),
|
||||||
|
PRIMARY KEY (foo)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE bar (
|
||||||
|
bar integer not null,
|
||||||
|
# foreign key to table foo
|
||||||
|
foo integer not null,
|
||||||
|
PRIMARY KEY (bar)
|
||||||
|
);
|
||||||
|
|
||||||
|
use dbframework_catalog;
|
||||||
|
|
||||||
|
# catalog entry for database 'foo'
|
||||||
|
INSERT INTO c_db VALUES('foo');
|
||||||
|
|
||||||
|
# catalog entries for table 'foo'
|
||||||
|
INSERT INTO c_table VALUES('foo','foo','bar');
|
||||||
|
# primary key type = 0
|
||||||
|
INSERT INTO c_key VALUES('foo','foo','primary',0,'foo');
|
||||||
|
# index type = 2
|
||||||
|
INSERT INTO c_key VALUES('foo','foo','bar_index',2,'bar');
|
||||||
|
|
||||||
|
# catalog entries for table 'bar'
|
||||||
|
INSERT INTO c_table VALUES('bar','foo',NULL);
|
||||||
|
# primary key type = 0
|
||||||
|
INSERT INTO c_key VALUES('foo','bar','primary',0,'bar');
|
||||||
|
# foreign key type = 1
|
||||||
|
INSERT INTO c_key VALUES('foo','bar','foreign_foo',2,'foo');
|
||||||
|
# relationship between 'bar' and 'foo'
|
||||||
|
INSERT INTO c_relationship VALUES('foo','bar','foreign_foo','foo');
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::Catalog;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
use DbFramework::PrimaryKey;
|
||||||
|
use DbFramework::ForeignKey;
|
||||||
|
use Alias;
|
||||||
|
use Carp;
|
||||||
|
use vars qw($DBH $_DEBUG %keytypes $db);
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
DBH => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
$db = 'dbframework_catalog';
|
||||||
|
%keytypes = (primary => 0, foreign => 1, index => 2);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($dsn,$user,$password)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::Catalog> object. I<$dsn> is the DBI data
|
||||||
|
source name containing the catalog database (default is
|
||||||
|
'dbframework_catalog'). I<$user> and I<$password> are optional
|
||||||
|
arguments specifying the username and password to use when connecting
|
||||||
|
to the database.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
|
||||||
|
my $self = bless({},$class);
|
||||||
|
for my $element (keys %fields) {
|
||||||
|
$self->{_PERMITTED}->{$element} = $fields{$element};
|
||||||
|
}
|
||||||
|
@{$self}{keys %fields} = values %fields;
|
||||||
|
$self->dbh(DbFramework::Util::get_dbh(@_));
|
||||||
|
$self->dbh->{PrintError} = 0;
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
=head2 set_primary_key($table)
|
||||||
|
|
||||||
|
Set the primary key for the B<DbFramework::Table> object I<$table>.
|
||||||
|
The catalog column B<c_table.labels> may contain a colon seperated
|
||||||
|
list of column names to be used as 'labels' (see
|
||||||
|
L<DbFramework::Primary/new()>.)
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub set_primary_key {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $table = shift;
|
||||||
|
my $sth = $self->_get_key_columns($table,'primary');
|
||||||
|
if ( $sth->rows == 0 ) {
|
||||||
|
$sth->finish;
|
||||||
|
carp "Can't get primary key for ",$table->name,"\n"
|
||||||
|
}
|
||||||
|
my($name,$columns) = @{$sth->fetchrow_arrayref};
|
||||||
|
$sth->finish;
|
||||||
|
my @attributes = $table->get_attributes(split /:/,$columns);
|
||||||
|
|
||||||
|
# get label columns
|
||||||
|
my $table_name = $DBH->quote($table->name);
|
||||||
|
my $db_name = $DBH->quote($table->belongs_to->db);
|
||||||
|
my $sql = qq{
|
||||||
|
SELECT labels
|
||||||
|
FROM c_table
|
||||||
|
WHERE db_name = $db_name
|
||||||
|
AND table_name = $table_name
|
||||||
|
};
|
||||||
|
print STDERR "$sql\n" if $_DEBUG;
|
||||||
|
$sth = $DBH->prepare($sql) || die($DBH->errstr);
|
||||||
|
my $rv = $sth->execute || die($sth->errstr);
|
||||||
|
my($labels) = $sth->fetchrow_array;
|
||||||
|
my $labels_ref = undef;
|
||||||
|
@$labels_ref = split /:/,$labels if defined $labels && $labels ne '';
|
||||||
|
$sth->finish;
|
||||||
|
print STDERR "$table_name.pk: $columns\n" if $_DEBUG;
|
||||||
|
my $pk = new DbFramework::PrimaryKey(\@attributes,$table,$labels_ref);
|
||||||
|
$table->is_identified_by($pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 set_keys($table)
|
||||||
|
|
||||||
|
Set the keys (indexes) for the B<DbFramework::Table> object I<$table>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub set_keys {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $table = shift;
|
||||||
|
my $sth = $self->_get_key_columns($table,'index');
|
||||||
|
my @keys;
|
||||||
|
while ( my $rowref = $sth->fetchrow_arrayref ) {
|
||||||
|
my($name,$columns) = @$rowref;
|
||||||
|
print STDERR "$name $columns\n" if $_DEBUG;
|
||||||
|
my @attributes = $table->get_attributes(split /:/,$columns);
|
||||||
|
my $key = new DbFramework::Key($name,\@attributes);
|
||||||
|
$key->belongs_to($table);
|
||||||
|
push(@keys,$key);
|
||||||
|
}
|
||||||
|
$table->is_accessed_using_l(\@keys);
|
||||||
|
$sth->finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 set_foreign_keys($dm)
|
||||||
|
|
||||||
|
Set the foreign keys for the B<DbFramework::DataModel> object I<$dm>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub set_foreign_keys {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $dm = shift;
|
||||||
|
my $db_name = $DBH->quote($dm->db);
|
||||||
|
for my $table ( @{$dm->collects_table_l} ) {
|
||||||
|
my $table_name = $DBH->quote($table->name);
|
||||||
|
my $sql;
|
||||||
|
if ( $dm->driver eq 'CSV' ) {
|
||||||
|
$sql = qq{
|
||||||
|
SELECT key_name,key_columns,pk_table
|
||||||
|
FROM c_relationship,c_key WHERE c_relationship.db_name = $db_name
|
||||||
|
AND c_relationship.fk_table = $table_name
|
||||||
|
AND c_relationsihp.db_name = c_key.db_name
|
||||||
|
AND c_relationship.fk_table = c_key.table_name
|
||||||
|
AND c_relationship.fk_key = c_key.key_name
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
$sql = qq{
|
||||||
|
SELECT k.key_name,k.key_columns,r.pk_table
|
||||||
|
FROM c_relationship r, c_key k
|
||||||
|
WHERE r.db_name = $db_name
|
||||||
|
AND r.fk_table = $table_name
|
||||||
|
AND r.db_name = k.db_name
|
||||||
|
AND r.fk_table = k.table_name
|
||||||
|
AND r.fk_key = k.key_name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
print STDERR "$sql\n" if $_DEBUG;
|
||||||
|
my $sth = DbFramework::Util::do_sql($DBH,$sql);
|
||||||
|
while ( my $rowref = $sth->fetchrow_arrayref ) {
|
||||||
|
my($name,$columns,$pk_table_name) = @$rowref;
|
||||||
|
print STDERR "name = $name, columns = $columns, pk_table = $pk_table_name)\n" if $_DEBUG;
|
||||||
|
my @attributes = $table->get_attributes(split /:/,$columns);
|
||||||
|
my $pk_table = $table->belongs_to->collects_table_h_byname($pk_table_name);
|
||||||
|
|
||||||
|
my $fk = new DbFramework::ForeignKey($name,
|
||||||
|
\@attributes,
|
||||||
|
$pk_table->is_identified_by);
|
||||||
|
$fk->belongs_to($table);
|
||||||
|
$table->has_foreign_keys_l_add($fk); # by number
|
||||||
|
$table->has_foreign_keys_h_add({$fk->name => $fk}); # by name
|
||||||
|
$pk_table->is_identified_by->incorporates($fk); # pk ref
|
||||||
|
}
|
||||||
|
$sth->finish;
|
||||||
|
|
||||||
|
$table->validate_foreign_keys;
|
||||||
|
# default templates need updating after setting foreign keys
|
||||||
|
#$table->_templates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _get_key_columns {
|
||||||
|
my $self = attr shift;
|
||||||
|
my($table,$key_type) = @_;
|
||||||
|
my $table_name = $DBH->quote($table->name);
|
||||||
|
my $db_name = $DBH->quote($table->belongs_to->db);
|
||||||
|
my $sql = qq{
|
||||||
|
SELECT key_name,key_columns
|
||||||
|
FROM c_key
|
||||||
|
WHERE db_name = $db_name
|
||||||
|
AND table_name = $table_name
|
||||||
|
AND key_type = $keytypes{$key_type}
|
||||||
|
};
|
||||||
|
print STDERR "$sql\n" if $_DEBUG;
|
||||||
|
my $sth = $DBH->prepare($sql) || die($DBH->errstr);
|
||||||
|
my $rv = $sth->execute || die($sth->errstr);
|
||||||
|
return $sth;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub DESTROY {
|
||||||
|
my $self = attr shift;
|
||||||
|
$DBH->disconnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1999 Paul Sharpe. England. All rights reserved. This
|
||||||
|
program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
195
lib/DbFramework/DataModel.pm
Normal file
195
lib/DbFramework/DataModel.pm
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::DataModel - Data Model class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::DataModel;
|
||||||
|
$dm = new DbFramework::DataModel($name,$dsn,$user,$password);
|
||||||
|
$dm->init_db_metadata($catalog_dsn,$user,$password);
|
||||||
|
@tables = @{$dm->collects_table_l};
|
||||||
|
%tables = %{$dm->collects_table_h};
|
||||||
|
@tables = @{$dm->collects_table_h_byname(@tables)};
|
||||||
|
$sql = $dm->as_sql;
|
||||||
|
$db = $dm->db;
|
||||||
|
$driver = $dm->driver;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
A B<DbFramework::DataModel> object represents a database schema. It
|
||||||
|
can be initialised using the metadata provided by a DBI driver and a
|
||||||
|
catalog database (see L<DbFramework::Catalog>).
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::DataModel;
|
||||||
|
use strict;
|
||||||
|
use vars qw( $NAME $_DEBUG @COLLECTS_TABLE_L $DBH $DSN );
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
use DbFramework::Table;
|
||||||
|
use DbFramework::ForeignKey;
|
||||||
|
use DBI;
|
||||||
|
use Alias;
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
NAME => undef,
|
||||||
|
# DataModel 0:N Collects 0:N DataModelObject
|
||||||
|
COLLECTS_L => undef,
|
||||||
|
# DataModel 0:N Collects 0:N Table
|
||||||
|
COLLECTS_TABLE_L => undef,
|
||||||
|
COLLECTS_TABLE_H => undef,
|
||||||
|
DBH => undef,
|
||||||
|
DSN => undef,
|
||||||
|
DRIVER => undef,
|
||||||
|
DB => undef,
|
||||||
|
TYPE_INFO_L => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
# arbitrary number to add to SQL type numbers as they can be negative
|
||||||
|
# and we want to store them in an array
|
||||||
|
my $_sql_type_adjust = 1000;
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# CLASS METHODS
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($name,$dsn,$user,$password)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::DataModel> object. I<$name> is the name of
|
||||||
|
the database associated with the data model. I<$dsn> is the DBI data
|
||||||
|
source name associated with the data model. I<$user> and I<$password>
|
||||||
|
are optional arguments specifying the username and password to use
|
||||||
|
when connecting to the database.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
$self->name(shift);
|
||||||
|
$self->dbh(DbFramework::Util::get_dbh(@_));
|
||||||
|
#$self->init_db_metadata;
|
||||||
|
# hack to record driver/db name until I confirm whether $dbh->{Name}
|
||||||
|
# has been implemented for mSQL and Mysql
|
||||||
|
$self->dsn($_[0]);
|
||||||
|
$self->driver($_[0] =~ /DBI:(.*):/);
|
||||||
|
$self->db($self->name);
|
||||||
|
# cache type_info here as it's an expensive function for ODBC
|
||||||
|
$self->type_info_l([$self->dbh->type_info($DBI::SQL_ALL_TYPES)]);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OBJECT METHODS
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
A data model has a number of tables. These tables can be accessed
|
||||||
|
using the methods I<COLLECTS_TABLE_L> and I<COLLECTS_TABLE_H>. See
|
||||||
|
L<DbFramework::Util/AUTOLOAD()> for the accessor methods for these
|
||||||
|
attributes.
|
||||||
|
|
||||||
|
=head2 name($name)
|
||||||
|
|
||||||
|
If I<$name> is supplied sets the name of the database associated with
|
||||||
|
the data model. Returns the database name.
|
||||||
|
|
||||||
|
=head2 dsn()
|
||||||
|
|
||||||
|
Returns the DBI DSN of the database associated with the data model.
|
||||||
|
|
||||||
|
=head2 db()
|
||||||
|
|
||||||
|
Synonym for name().
|
||||||
|
|
||||||
|
=head2 driver()
|
||||||
|
|
||||||
|
Returns the name of the driver associated with the data model.
|
||||||
|
|
||||||
|
=head2 as_sql()
|
||||||
|
|
||||||
|
Returns a SQL string which can be used to create the tables which make
|
||||||
|
up the data model.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_sql {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $sql;
|
||||||
|
for ( @COLLECTS_TABLE_L ) { $sql .= $_->as_sql($DBH) . ";\n" }
|
||||||
|
return $sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 init_db_metadata($catalog_dsn,$user,$password)
|
||||||
|
|
||||||
|
Returns a B<DbFramework::DataModel> object configured using metadata
|
||||||
|
from the database handle returned by dbh() and the catalog (see
|
||||||
|
L<DbFramework::Catalog>). I<$catalog_dsn> is the DBI data source name
|
||||||
|
associated with the catalog. I<$user> and I<$password> are used for
|
||||||
|
authorisation against the catalog database. Foreign keys will be
|
||||||
|
automatically configured for tables in the data model but this method
|
||||||
|
will die() unless the number of attributes in each foreign and related
|
||||||
|
primary key match.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub init_db_metadata {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $c = new DbFramework::Catalog(@_);
|
||||||
|
|
||||||
|
# add tables
|
||||||
|
my($table,@tables,@byname);
|
||||||
|
my $sth = $DBH->table_info;
|
||||||
|
while ( my @table_info = $sth->fetchrow_array ) {
|
||||||
|
my $table_name = $table_info[2];
|
||||||
|
print STDERR "table: $table_name, table_info = @table_info\n" if $_DEBUG;
|
||||||
|
my $table = DbFramework::Table->new($table_name,undef,undef,$DBH,$self);
|
||||||
|
push(@tables,$table->init_db_metadata($c));
|
||||||
|
print STDERR "table: ",$table->name," pk: ",join(',',$table->is_identified_by->attribute_names),"\n" if $_DEBUG;
|
||||||
|
}
|
||||||
|
$self->collects_table_l(\@tables);
|
||||||
|
for ( @tables ) { push(@byname,($_->name,$_)) }
|
||||||
|
$self->collects_table_h(\@byname);
|
||||||
|
|
||||||
|
# add foreign keys
|
||||||
|
$c->set_foreign_keys($self);
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::Catalog>, L<DbFramework::Table> and
|
||||||
|
L<DbFramework::Util>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998 Paul Sharpe. England. All rights reserved.
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=head1 ACKNOWLEDGEMENTS
|
||||||
|
|
||||||
|
This module was inspired by B<Msql::RDBMS>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
1;
|
56
lib/DbFramework/DataModelObject.pm
Normal file
56
lib/DbFramework/DataModelObject.pm
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::DataModelObject - DataModelObject class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::DataModelObject;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
Abstract class for CDIF Data Model objects.
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::DataModelObject;
|
||||||
|
use strict;
|
||||||
|
use vars qw( $NAME );
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
use Alias;
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
# DataModelObject 1:1 ActsAs 0:N RolePlayer
|
||||||
|
ACTS_AS => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
$self->name(shift);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1998 Paul Sharpe. England. All rights reserved. This
|
||||||
|
program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
142
lib/DbFramework/DataType/ANSII.pm
Normal file
142
lib/DbFramework/DataType/ANSII.pm
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::DataType::ANSII - ANSII data type class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::DataType::ANSII;
|
||||||
|
$dt = new DbFramework::DataType::ANSII($dm,$type,$ansii_type,$length);
|
||||||
|
$name = $dt->name($name);
|
||||||
|
$type = $type($type);
|
||||||
|
$length = $dt->length($length);
|
||||||
|
$extra = $dt->extra($extra);
|
||||||
|
$ansii = $dt->ansii_type;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
A B<DbFramework::DataType::ANSII> object represents an ANSII data type.
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::DefinitionObject>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::DataType::ANSII;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::DefinitionObject);
|
||||||
|
use Alias;
|
||||||
|
use vars qw();
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
LENGTH => undef,
|
||||||
|
EXTRA => undef,
|
||||||
|
TYPES_L => undef,
|
||||||
|
TYPE => undef,
|
||||||
|
ANSII_TYPE => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
# arbitrary number to add to SQL type numbers as they can be negative
|
||||||
|
# and we want to store them in an array
|
||||||
|
my $_sql_type_adjust = 1000;
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($dm,$type,$ansii_type,$length)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::DataType> object. I<$dm> is a
|
||||||
|
B<DbFramework::DataModle> object. I<$type> is a numeric ANSII type
|
||||||
|
e.g. a type contained in the array reference returned by $sth->{TYPE}.
|
||||||
|
This method will die() unless I<$type> is a member of the set of ANSII
|
||||||
|
types supported by the DBI driver. I<$ansii_type> is the same as
|
||||||
|
I<$type>. I<$length> is the length of the data type.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $_debug = 0;
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $dm = shift;
|
||||||
|
my $realtype = shift;
|
||||||
|
shift; # ansii_type is the same as type
|
||||||
|
my $type = $realtype + $_sql_type_adjust;
|
||||||
|
|
||||||
|
my(@types,@type_names);
|
||||||
|
print "type_info_l = ",@{$dm->type_info_l},"\n" if $_debug;
|
||||||
|
for my $t ( @{$dm->type_info_l} ) {
|
||||||
|
# first DATA_TYPE returned should be the ANSII type
|
||||||
|
unless ( $types[$t->{DATA_TYPE} + $_sql_type_adjust] ) {
|
||||||
|
$types[$t->{DATA_TYPE} + $_sql_type_adjust] = $t;
|
||||||
|
$type_names[$t->{DATA_TYPE} + $_sql_type_adjust] = uc($t->{TYPE_NAME});
|
||||||
|
print $type_names[$t->{DATA_TYPE} + $_sql_type_adjust],"\n" if $_debug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print STDERR "type = $type ($type_names[$type])\n" if $_debug;
|
||||||
|
$types[$type] || die "Invalid ANSII data type: $type";
|
||||||
|
|
||||||
|
my $self = bless($class->SUPER::new($type_names[$type]),$class);
|
||||||
|
for my $element (keys %fields) {
|
||||||
|
$self->{_PERMITTED}->{$element} = $fields{$element};
|
||||||
|
}
|
||||||
|
@{$self}{keys %fields} = values %fields;
|
||||||
|
|
||||||
|
$self->ansii_type($self->type($realtype));
|
||||||
|
$self->types_l(\@types);
|
||||||
|
$self->length(shift);
|
||||||
|
$self->extra('IDENTITY(0,1)') if $self->types_l->[$type]->{AUTO_INCREMENT};
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 name($name)
|
||||||
|
|
||||||
|
If I<$name> is supplied, sets the name of the ANSII data type.
|
||||||
|
Returns the name of the data type.
|
||||||
|
|
||||||
|
=head2 type($type)
|
||||||
|
|
||||||
|
If I<$type> is supplied, sets the number of the ANSII data type.
|
||||||
|
Returns the numeric data type.
|
||||||
|
|
||||||
|
=head2 ansii_type($ansii_type)
|
||||||
|
|
||||||
|
Returns the same type as type().
|
||||||
|
|
||||||
|
=head2 length($length)
|
||||||
|
|
||||||
|
If I<$length> is supplied, sets the length of the data type. Returns
|
||||||
|
the length of the data type.
|
||||||
|
|
||||||
|
=head2 extra($extra)
|
||||||
|
|
||||||
|
If I<$extra> is supplied, sets any extra information which applies to
|
||||||
|
the data type e.g. I<AUTO_INCREMENT>. Returns the extra information
|
||||||
|
which applies to the data type.
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::DefinitionObject>
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1999 Paul Sharpe. England. All rights reserved. This
|
||||||
|
program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
1;
|
140
lib/DbFramework/DataType/Mysql.pm
Normal file
140
lib/DbFramework/DataType/Mysql.pm
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::DataType::Mysql - Mysql data type class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::DataType::Mysql;
|
||||||
|
$dt = new DbFramework::DataType::ANSII($dm,$type,$ansii_type,$length);
|
||||||
|
$name = $dt->name($name);
|
||||||
|
$type = $dt->type($type);
|
||||||
|
$length = $dt->length($length);
|
||||||
|
$extra = $dt->extra($extra);
|
||||||
|
$ansii = $dt->ansii_type;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
A B<DbFramework::DataType::Mysql> object represents a Mysql data type.
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::DefinitionObject>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::DataType::Mysql;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::DefinitionObject);
|
||||||
|
use Alias;
|
||||||
|
use vars qw();
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
LENGTH => undef,
|
||||||
|
EXTRA => undef,
|
||||||
|
TYPES_L => undef,
|
||||||
|
TYPE => undef,
|
||||||
|
ANSII_TYPE => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($dm,$type,$ansii_type,$length,$extra)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::DataType> object. I<$dm> is a
|
||||||
|
B<DbFramework::DataModle> object. I<$type> is a numeric Mysql type
|
||||||
|
e.g. a type containd in the array reference returned by
|
||||||
|
$sth->{mysql_type}. This method will die() unless I<$type> is a
|
||||||
|
member of the set of types supported by B<DBD::mysql>. I<$ansii_type>
|
||||||
|
is the ANSII type that most closely resembles the native Mysql type.
|
||||||
|
I<$length> is the length of the data type. I<$extra> is any extra
|
||||||
|
stuff which applies to the type e.g. 'AUTO_INCREMENT'.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $_debug = 0;
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my($dm,$type,$ansii_type) = (shift,shift,shift);
|
||||||
|
|
||||||
|
my(@types,@type_names);
|
||||||
|
for my $t ( @{$dm->type_info_l} ) {
|
||||||
|
$types[$t->{mysql_native_type}] = $t;
|
||||||
|
$type_names[$t->{mysql_native_type}] = uc($t->{TYPE_NAME});
|
||||||
|
print STDERR "$t->{mysql_native_type}, $type_names[$t->{mysql_native_type}]\n" if $_debug
|
||||||
|
}
|
||||||
|
$types[$type] || die "Invalid Mysql data type: $type\n";
|
||||||
|
print STDERR "\ntype = $type ($type_names[$type])\n\n" if $_debug;
|
||||||
|
|
||||||
|
my $self = bless($class->SUPER::new($type_names[$type]),$class);
|
||||||
|
for my $element (keys %fields) {
|
||||||
|
$self->{_PERMITTED}->{$element} = $fields{$element};
|
||||||
|
}
|
||||||
|
@{$self}{keys %fields} = values %fields;
|
||||||
|
|
||||||
|
$self->type($type);
|
||||||
|
$self->ansii_type($ansii_type);
|
||||||
|
$self->types_l(\@types);
|
||||||
|
$self->length(shift);
|
||||||
|
$self->extra(shift);
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 name($name)
|
||||||
|
|
||||||
|
If I<$name> is supplied sets the name of the Mysql data type. Returns
|
||||||
|
the name of the data type.
|
||||||
|
|
||||||
|
=head2 type($type)
|
||||||
|
|
||||||
|
If I<$type> is supplied sets the number of the Mysql data type.
|
||||||
|
Returns the numeric data type.
|
||||||
|
|
||||||
|
=head2 ansii_type($ansii_type)
|
||||||
|
|
||||||
|
If I<$ansii_type> is supplied sets the number of the ANSII type which
|
||||||
|
most closely corresponds to the Mysql native type. Returns the ANSII
|
||||||
|
type which most closely corresponds to the Mysql native type.
|
||||||
|
|
||||||
|
=head2 length($length)
|
||||||
|
|
||||||
|
If I<$length> is supplied sets the length of the data type. Returns
|
||||||
|
the length of the data type.
|
||||||
|
|
||||||
|
=head2 extra($extra)
|
||||||
|
|
||||||
|
If I<$extra> is supplied sets any extra information which applies to
|
||||||
|
the data type e.g. I<AUTO_INCREMENT> in they case of a Mysql
|
||||||
|
I<INTEGER> data type. Returns the extra information which applies to
|
||||||
|
the data type.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::DefinitionObject>
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1999 Paul Sharpe. England. All rights reserved. This
|
||||||
|
program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
89
lib/DbFramework/DefinitionObject.pm
Normal file
89
lib/DbFramework/DefinitionObject.pm
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::DefinitionObject - DefinitionObject class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::DefinitionObject;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
Abstract class for CDIF Definition Object objects.
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::DefinitionObject;
|
||||||
|
use strict;
|
||||||
|
use vars qw( $NAME $_DEBUG);
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
use Alias;
|
||||||
|
use Carp;
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
NAME => undef,
|
||||||
|
# DefinitionObject 0:1 Contains 0:N Attribute
|
||||||
|
CONTAINS_L => undef,
|
||||||
|
CONTAINS_H => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
$self->name(shift);
|
||||||
|
$self->_init(shift);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
A definition object contains 0 or more B<DbFramework::Attribute>
|
||||||
|
objects. These objects can be accessed using the attributes
|
||||||
|
I<CONTAINS_L> and I<CONTAINS_H>. See L<DbFramework::Util/AUTOLOAD()>
|
||||||
|
for the accessor methods for these attributes.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _init {
|
||||||
|
my $self = attr shift;
|
||||||
|
my @by_name;
|
||||||
|
for ( @{$self->contains_l(shift)} ) { push(@by_name,($_->name,$_)) }
|
||||||
|
$self->contains_h(\@by_name);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::Util>
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1998 Paul Sharpe. England. All rights reserved. This
|
||||||
|
program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
171
lib/DbFramework/ForeignKey.pm
Normal file
171
lib/DbFramework/ForeignKey.pm
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::ForeignKey - Foreign Key class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::ForeignKey;
|
||||||
|
$fk = new DbFramework::ForeignKey($name,\@attributes,$primary);
|
||||||
|
$pk = $fk->references($primary);
|
||||||
|
$sql = $fk->as_sql;
|
||||||
|
$html = $fk->as_html_form_field(\%values);
|
||||||
|
$s = $fk->sql_where;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The B<DbFramework::ForeignKey> class implements foreign keys for a
|
||||||
|
table.
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Key>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::ForeignKey;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Key);
|
||||||
|
use Alias;
|
||||||
|
use vars qw( $NAME $BELONGS_TO @INCORPORATES_L $BGCOLOR $_DEBUG );
|
||||||
|
|
||||||
|
# CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
# ForeignKey 0:N References 1:1 PrimaryKey
|
||||||
|
REFERENCES => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($name,\@attributes,$primary)
|
||||||
|
|
||||||
|
Returns a new B<DbFramework::ForeignKey> object.
|
||||||
|
|
||||||
|
I<$name> is the name of the foreign key. I<@attributes> is a list of
|
||||||
|
B<DbFramework::Attribute> objects from a single B<DbFramework::Table>
|
||||||
|
object which make up the key. I<$primary> is the
|
||||||
|
B<DbFramework::Primary> object which the foreign key references.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless($class->SUPER::new(shift,shift),$class);
|
||||||
|
for my $element (keys %fields) {
|
||||||
|
$self->{_PERMITTED}->{$element} = $fields{$element};
|
||||||
|
}
|
||||||
|
@{$self}{keys %fields} = values %fields;
|
||||||
|
$self->references(shift);
|
||||||
|
$self->bgcolor('#777777');
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
=head2 references($primary)
|
||||||
|
|
||||||
|
I<$primary> should be a B<DbFramework::PrimaryKey> object. If
|
||||||
|
supplied it sets the primary key referenced by this foreign key.
|
||||||
|
Returns the B<DbFramework::PrimaryKey> object referenced by this
|
||||||
|
foreign key.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _input_template {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $t_name = $BELONGS_TO ? $BELONGS_TO->name : 'UNKNOWN_TABLE';
|
||||||
|
return qq{<TD><DbFKey ${t_name}.$NAME></TD>};
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _output_template {
|
||||||
|
my $self = attr shift;
|
||||||
|
# output template consists of attributes from related pk table
|
||||||
|
my $pk_table = $self->references->belongs_to;
|
||||||
|
my $name = $pk_table->name;
|
||||||
|
my $attributes = join(',',$pk_table->attribute_names);
|
||||||
|
my $out = qq{<TD BGCOLOR='$BGCOLOR'><DbValue ${name}.$attributes></TD>};
|
||||||
|
print STDERR "\$out = $out\n" if $_DEBUG;
|
||||||
|
$out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_form_field(\%values)
|
||||||
|
|
||||||
|
Returns an HTML selection box containing values and labels from the
|
||||||
|
primary key columns in the related table. I<%values> is a hash whose
|
||||||
|
keys are the attribute names of the foreign key and whose values
|
||||||
|
indicate the item in the selection box which should be selected by
|
||||||
|
default. See L<DbFramework::PrimaryKey/html_select_field()>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_form_field {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %values = $_[0] ? %{$_[0]} : ();
|
||||||
|
my $pk = $self->references;
|
||||||
|
my $name = join(',',$self->attribute_names);
|
||||||
|
# only handles single attribute foreign keys
|
||||||
|
my $t_name = $BELONGS_TO->name;
|
||||||
|
my @fk_value;
|
||||||
|
$fk_value[0] = $values{"${t_name}.${name}"};
|
||||||
|
if ( $_DEBUG ) {
|
||||||
|
print STDERR "\$t_name = $t_name, \$name = $name, \@fk_value = @fk_value\n";
|
||||||
|
print STDERR "pk table = ",$pk->belongs_to->name,"\n";
|
||||||
|
}
|
||||||
|
$pk->html_select_field(undef,undef,\@fk_value,$name);
|
||||||
|
}
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 sql_where()
|
||||||
|
|
||||||
|
Returns a string containing SQL 'WHERE' condition(s) to join the
|
||||||
|
foreign key against the primary key of the related table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub sql_where {
|
||||||
|
my $self = shift;
|
||||||
|
my $fk_table = $self->belongs_to->name;
|
||||||
|
my @fk_columns = $self->attribute_names;
|
||||||
|
my $pk_table = $self->references->belongs_to->name;
|
||||||
|
my @pk_columns = $self->references->attribute_names;
|
||||||
|
my $where;
|
||||||
|
for ( my $i = 0; $i <= $#fk_columns; $i++ ) {
|
||||||
|
$where .= ' AND ' if $where;
|
||||||
|
$where .= "$fk_table.$fk_columns[$i] = $pk_table.$pk_columns[$i]";
|
||||||
|
}
|
||||||
|
$where;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::Key>, L<DbFramework::PrimaryKey> and
|
||||||
|
L<DbFramework::Catalog>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998,1999 Paul Sharpe. England. All rights
|
||||||
|
reserved. This program is free software; you can redistribute it
|
||||||
|
and/or modify it under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
181
lib/DbFramework/Key.pm
Normal file
181
lib/DbFramework/Key.pm
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::Key - Key class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::Key;
|
||||||
|
$k = new DbFramework::Key($name,\@attributes);
|
||||||
|
$name = $k->name($name);
|
||||||
|
@a = @{$k->incorporates_l(\@attributes)};
|
||||||
|
@names = $k->attribute_names;
|
||||||
|
$sql = $k->as_sql;
|
||||||
|
$table = $k->belongs_to($table);
|
||||||
|
$html = $k->as_html_heading;
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The B<DbFramework::Key> class implements keys (indexes) for a table.
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::Key;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
use Alias;
|
||||||
|
use vars qw( $NAME @INCORPORATES_L $BELONGS_TO $BGCOLOR );
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
NAME => undef,
|
||||||
|
# Key 0:N Incorporates 0:N Attribute
|
||||||
|
INCORPORATES_L => undef,
|
||||||
|
# Key 1:1 BelongsTo 1:1 Table
|
||||||
|
BELONGS_TO => undef,
|
||||||
|
BGCOLOR => '#ffffff',
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($name,\@attributes)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::Key> object. I<$name> is the name of the
|
||||||
|
key. I<@attributes> is a list of B<DbFramework::Attribute> objects
|
||||||
|
from a single B<DbFramework::Table> object which make up the key.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $DEBUG = 0;
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
print STDERR "=>$class::new(@_)\n" if $DEBUG;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
$self->name(shift);
|
||||||
|
$self->incorporates_l(shift);
|
||||||
|
print STDERR "<=$class::new()\n" if $DEBUG;
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
A key incorporates 0 or more attributes. These attributes can be
|
||||||
|
accessed using the attribute I<INCORPORATES_L>. See
|
||||||
|
L<DbFramework::Util/AUTOLOAD()> for the accessor methods for this
|
||||||
|
attribute.
|
||||||
|
|
||||||
|
=head2 name($name)
|
||||||
|
|
||||||
|
If I<$name> is supplied sets the data model name. Returns the data
|
||||||
|
model name.
|
||||||
|
|
||||||
|
=head2 belongs_to($table)
|
||||||
|
|
||||||
|
I<$table> is a B<DbFramework::Table> object. If supplied sets the
|
||||||
|
table to which this key refers to I<$table>. Returns a
|
||||||
|
B<DbFramework::Table>.
|
||||||
|
|
||||||
|
=head2 bgcolor($bgcolor)
|
||||||
|
|
||||||
|
If I<$color> is supplied sets the background colour for HTML table
|
||||||
|
cells. Returns the current background colour.
|
||||||
|
|
||||||
|
=head2 attribute_names()
|
||||||
|
|
||||||
|
Returns a list of the names of the attributes which make up the key.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub attribute_names {
|
||||||
|
my $self = attr shift;
|
||||||
|
my @names;
|
||||||
|
for ( @INCORPORATES_L ) { push(@names,$_->name) }
|
||||||
|
return @names;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_sql()
|
||||||
|
|
||||||
|
Returns a string which can be used in an SQL 'CREATE TABLE' statement
|
||||||
|
to create the key.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_sql {
|
||||||
|
my $self = attr shift;
|
||||||
|
return "KEY $NAME (" . join(',',$self->attribute_names) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _input_template {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $t_name = $BELONGS_TO ? $BELONGS_TO->name : 'UNKNOWN_TABLE';
|
||||||
|
my $in;
|
||||||
|
my $bgcolor = $self->bgcolor;
|
||||||
|
for ( @INCORPORATES_L ) {
|
||||||
|
my $a_name = $_->name;
|
||||||
|
$in .= qq{<TD><DbField ${t_name}.${a_name}></TD>};
|
||||||
|
}
|
||||||
|
$in;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _output_template {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $t_name = $BELONGS_TO ? $BELONGS_TO->name : 'UNKNOWN_TABLE';
|
||||||
|
my $out;
|
||||||
|
for ( @INCORPORATES_L ) {
|
||||||
|
my $a_name = $_->name;
|
||||||
|
$out .= qq{<TD BGCOLOR='$BGCOLOR'><DbValue ${t_name}.${a_name}></TD>};
|
||||||
|
}
|
||||||
|
$out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_heading()
|
||||||
|
|
||||||
|
Returns a string for use as a column heading cell in an HTML table;
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_heading {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $html = "<TD BGCOLOR='$BGCOLOR' COLSPAN=".scalar(@INCORPORATES_L).">";
|
||||||
|
for ( @INCORPORATES_L ) { $html .= $_->name . ',' }
|
||||||
|
chop($html);
|
||||||
|
"$html</TD>";
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::ForeignKey>, L<DbFramework::PrimaryKey> and
|
||||||
|
L<DbFramework::Catalog>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998 Paul Sharpe. England. All rights reserved.
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
349
lib/DbFramework/Persistent.pm
Normal file
349
lib/DbFramework/Persistent.pm
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::Persistent - Persistent Perl object base class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
package Foo;
|
||||||
|
use base qw(DbFramework::Persistent);
|
||||||
|
|
||||||
|
package main;
|
||||||
|
$foo = new Foo($table,$dbh,$catalog);
|
||||||
|
$foo->attributes_h(\%foo};
|
||||||
|
$foo->insert;
|
||||||
|
$foo->attributes_h(\%new_foo);
|
||||||
|
$foo->update(\%attributes);
|
||||||
|
$foo->delete;
|
||||||
|
$foo->init_pk;
|
||||||
|
@foo = $foo->select($condition,$order);
|
||||||
|
$hashref = $foo->table_qualified_attribute_hashref;
|
||||||
|
$code = DbFramework::Persistent::make_class($name);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
Base class for persistent objects which use a DBI database for
|
||||||
|
storage. To create your own persistent object classes subclass
|
||||||
|
B<DbFramework::Persistent> (see the make_class() class method.)
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::Persistent;
|
||||||
|
use strict;
|
||||||
|
use vars qw( $TABLE $_DEBUG $VERSION %ATTRIBUTES_H $CATALOG );
|
||||||
|
$VERSION = '1.10';
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
use Alias;
|
||||||
|
use DbFramework::Table;
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my $Debugging = 0;
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
TABLE => undef,
|
||||||
|
ATTRIBUTES_H => undef,
|
||||||
|
CATALOG => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($table,$dbh,$catalog)
|
||||||
|
|
||||||
|
Create a new persistent object. I<$table> is a B<DbFramework::Table>
|
||||||
|
object or the name of a database table. I<$dbh> is a B<DBI> database
|
||||||
|
handle which refers to a database containing a table associated with
|
||||||
|
I<$table>. I<$catalog> is a B<DbFramework::Catalog> object.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my($table,$dbh,$catalog) = @_;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
$table = new DbFramework::Table($table,undef,undef,$dbh)
|
||||||
|
unless (ref($table) eq 'DbFramework::Table');
|
||||||
|
$self->table($table->init_db_metadata($catalog));
|
||||||
|
$self->catalog($catalog);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 make_class($name)
|
||||||
|
|
||||||
|
Returns some Perl code which can be used with eval() to create a new
|
||||||
|
persistent object (sub)class called I<$name>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub make_class {
|
||||||
|
my($proto,$name) = @_;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
|
||||||
|
my $code = qq{package $name;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Persistent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
Attributes in a persistent object which relate to columns in the
|
||||||
|
associated table are made available through the attribute
|
||||||
|
I<ATTRIBUTES_H>. See L<DbFramework::Util/AUTOLOAD()> for the accessor
|
||||||
|
methods for this attribute.
|
||||||
|
|
||||||
|
=head2 delete()
|
||||||
|
|
||||||
|
Delete this object from the associated table based on the values of
|
||||||
|
it's primary key attributes. Returns the number of rows deleted if
|
||||||
|
supplied by the DBI driver.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub delete {
|
||||||
|
my $self = attr shift;
|
||||||
|
return $TABLE->delete($self->_pk_conditions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 insert()
|
||||||
|
|
||||||
|
Insert this object in the associated table. Returns the primary key
|
||||||
|
of the inserted row if it is a Mysql 'AUTO_INCREMENT' column or -1.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub insert {
|
||||||
|
my $self = attr shift;
|
||||||
|
return $TABLE->insert($self->attributes_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 update(\%attributes)
|
||||||
|
|
||||||
|
Update this object in the associated table. I<%attributes> is a hash
|
||||||
|
whose keys contain primary key column names and whose values will be
|
||||||
|
concatenated with 'ANDs' to form a SQL 'WHERE' clause. The default
|
||||||
|
values of I<%attributes> is the hash returned by attributes_h(). Pass
|
||||||
|
the B<current> primary key attributes as an argument in I<%attributes>
|
||||||
|
when you need to update one or more primary key columns. Returns the
|
||||||
|
number of rows updated if supplied by the DBI driver.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub update {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %attributes = defined($_[0]) ? %{$_[0]} : %{$self->attributes_h};
|
||||||
|
# get pk attributes
|
||||||
|
my %pk_attributes;
|
||||||
|
for ( $TABLE->is_identified_by->attribute_names ) {
|
||||||
|
$pk_attributes{$_} = $attributes{$_};
|
||||||
|
}
|
||||||
|
return $TABLE->update($self->attributes_h,$self->where_and(\%pk_attributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 select($conditions,$order)
|
||||||
|
|
||||||
|
Returns a list of objects of the same class as the object which
|
||||||
|
invokes it. Each object in the list has its attributes initialised
|
||||||
|
from the values returned by selecting all columns from the associated
|
||||||
|
table matching I<$conditions> ordered by the list of columns in
|
||||||
|
I<$order>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub select {
|
||||||
|
my $self = attr shift;
|
||||||
|
|
||||||
|
my @things;
|
||||||
|
my @columns = $TABLE->attribute_names;
|
||||||
|
for ( $TABLE->select(\@columns,shift,shift) ) {
|
||||||
|
print STDERR "\@{\$_} = @{$_}\n" if $_DEBUG;
|
||||||
|
# pass Table *object* to new to retain any fk relationships
|
||||||
|
my $thing = $self->new($TABLE,$TABLE->dbh,$CATALOG);
|
||||||
|
my %attributes;
|
||||||
|
for ( my $i = 0; $i <= $#columns; $i++ ) {
|
||||||
|
print STDERR "assigning $columns[$i] = $_->[$i]\n" if $_DEBUG;
|
||||||
|
$attributes{$columns[$i]} = $_->[$i];
|
||||||
|
}
|
||||||
|
$thing->attributes_h([%attributes]);
|
||||||
|
push(@things,$thing);
|
||||||
|
}
|
||||||
|
return @things;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#=head2 validate_required()
|
||||||
|
|
||||||
|
#Returns a list of attribute names which must B<not> be NULL but are
|
||||||
|
#undefined. If I<@attributes> is undefined, validates all attributes.
|
||||||
|
|
||||||
|
#=cut
|
||||||
|
|
||||||
|
#sub validate_required {
|
||||||
|
# my $self = attr shift; my $table = $self->table;
|
||||||
|
# my($attribute,@invalid);
|
||||||
|
|
||||||
|
# my @attributes = @_ ? @_ : sort keys(%STATE);
|
||||||
|
# foreach $attribute ( @attributes ) {
|
||||||
|
# my $column = $table->get_column($attribute);
|
||||||
|
# if ( ! $column->null && ! defined($self->get_attribute($attribute)) ) {
|
||||||
|
# my $heading = $column->heading;
|
||||||
|
# if ( $heading ) {
|
||||||
|
# push(@invalid,$heading)
|
||||||
|
# } else {
|
||||||
|
# push(@invalid,$attribute);
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# return @invalid;
|
||||||
|
#}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# return a SQL 'WHERE' clause condition consisting of primary key
|
||||||
|
# attributes and their corresponding values joined by 'AND'
|
||||||
|
|
||||||
|
sub _pk_conditions {
|
||||||
|
my $self = attr shift;
|
||||||
|
my @attributes = @{$TABLE->is_identified_by->incorporates_l};
|
||||||
|
my %values = %{$self->attributes_h};
|
||||||
|
my %pk_attributes;
|
||||||
|
for ( @attributes ) {
|
||||||
|
my $column = $_->name;
|
||||||
|
$pk_attributes{$column} = $values{$column};
|
||||||
|
}
|
||||||
|
return $self->where_and(\%pk_attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# return a SQL 'WHERE' clause condition consisting of attributes named
|
||||||
|
# after keys in %attributes and their corresponding values joined by
|
||||||
|
# 'AND'
|
||||||
|
|
||||||
|
sub where_and {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %attributes = %{$_[0]};
|
||||||
|
my $conditions;
|
||||||
|
for ( keys %attributes ) {
|
||||||
|
my($attribute) = $TABLE->get_attributes($_);
|
||||||
|
$conditions .= ' AND ' if $conditions;
|
||||||
|
my($name,$type) = ($attribute->name,$attribute->references->type);
|
||||||
|
$conditions .= "$name = " . $TABLE->dbh->quote($attributes{$name},$type);
|
||||||
|
}
|
||||||
|
print STDERR "$conditions\n" if $_DEBUG;
|
||||||
|
$conditions;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#=head2 fill_template($name)
|
||||||
|
|
||||||
|
#Returns the template named I<$name> in the table associated with this
|
||||||
|
#object filled with the object's attribute values. See
|
||||||
|
#L<DbFramework::Table/"fill_template()">.
|
||||||
|
|
||||||
|
#=cut
|
||||||
|
|
||||||
|
sub fill_template {
|
||||||
|
my($self,$name) = (attr shift,shift);
|
||||||
|
$TABLE->fill_template($name,$self->attributes_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_form()
|
||||||
|
|
||||||
|
Returns an HTML form representing the object, filled with the object's
|
||||||
|
attribute values.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_form {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %attributes = %{$self->attributes_h};
|
||||||
|
my $html;
|
||||||
|
for ( @{$self->table->contains_l} ) {
|
||||||
|
next if $self->table->in_foreign_key($_);
|
||||||
|
my $name = $_->name;
|
||||||
|
$html .= "<TR><TD><STRONG>$name</STRONG></TD><TD>"
|
||||||
|
. $_->as_html_form_field($attributes{$name})
|
||||||
|
. "</TD></TR>\n";
|
||||||
|
}
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 init_pk()
|
||||||
|
|
||||||
|
Initialise an object by setting its attributes based on the current
|
||||||
|
value of the its primary key attributes.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub init_pk {
|
||||||
|
my $self = attr shift;
|
||||||
|
my @loh = $TABLE->select_loh(undef,$self->_pk_conditions);
|
||||||
|
$self->attributes_h([ %{$loh[0]} ]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 table_qualified_attribute_hashref()
|
||||||
|
|
||||||
|
Returns a reference to a hash whose keys are the keys of
|
||||||
|
I<%ATTRIBUTES_H> with a prefix of I<$table>, where I<$table> is the
|
||||||
|
table associated with the object and whose values are values from
|
||||||
|
I<%ATTRIBUTES_H>. This is useful for filling a template (see
|
||||||
|
L<DbFramework::Template/fill()>.)
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub table_qualified_attribute_hashref {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $t_name = $TABLE->name;
|
||||||
|
my %tq;
|
||||||
|
for ( keys %ATTRIBUTES_H ) { $tq{"$t_name.$_"} = $ATTRIBUTES_H{$_} }
|
||||||
|
return \%tq;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::Util>, L<DbFramework::Table> and
|
||||||
|
L<DbFramework::Template>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998,1999 Paul Sharpe. England. All rights reserved.
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
341
lib/DbFramework/PrimaryKey.pm
Normal file
341
lib/DbFramework/PrimaryKey.pm
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::PrimaryKey - Primary key class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::PrimaryKey;
|
||||||
|
$pk = new DbFramework::Primary(\@attributes,$table,\@labels);
|
||||||
|
$sql = $pk->as_sql;
|
||||||
|
$html = $pk->html_select_field(\@column_names,$multiple,\@default);
|
||||||
|
$html = $pk->as_html_heading;
|
||||||
|
$html = $pk->as_hidden_html(\%values);
|
||||||
|
$qw = $pk->as_query_string(\%values);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The B<DbFramework::PrimaryKey> class implements primary keys for a
|
||||||
|
table.
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Key>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::PrimaryKey;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Key);
|
||||||
|
use Alias;
|
||||||
|
use vars qw( $NAME $BELONGS_TO @INCORPORATES_L $BGCOLOR $_DEBUG );
|
||||||
|
use CGI;
|
||||||
|
use URI::Escape;
|
||||||
|
|
||||||
|
# CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
# PrimaryKey 0:N Incorporates 0:N ForeignKey
|
||||||
|
INCORPORATES => undef,
|
||||||
|
LABELS_L => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new(\@attributes,$table,\@labels)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::PrimaryKey> object. I<@attributes> is a
|
||||||
|
list of B<DbFramework::Attribute> objects from a single
|
||||||
|
B<DbFramework::Table> object which make up the key. I<$table> is the
|
||||||
|
B<DbFramework::Table> to which the primary key belongs. I<@labels> is
|
||||||
|
a list of column names which should be used as labels when calling
|
||||||
|
html_select_field(). I<@labels> will default to all columns in
|
||||||
|
I<$table>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless($class->SUPER::new('PRIMARY',shift),$class);
|
||||||
|
for my $element (keys %fields) {
|
||||||
|
$self->{_PERMITTED}->{$element} = $fields{$element};
|
||||||
|
}
|
||||||
|
@{$self}{keys %fields} = values %fields;
|
||||||
|
my $table = shift;
|
||||||
|
$self->belongs_to($table);
|
||||||
|
|
||||||
|
my(@bad,@labels);
|
||||||
|
if ( defined($_[0]) ) {
|
||||||
|
my @columns = $table->attribute_names;
|
||||||
|
@labels = @{$_[0]};
|
||||||
|
for my $label ( @labels ) {
|
||||||
|
push(@bad,$label) unless grep(/^$label$/,@columns);
|
||||||
|
}
|
||||||
|
die "label column(s) '@bad' do not exist in '",$table->name,"'" if @bad;
|
||||||
|
} else {
|
||||||
|
@labels = $table->attribute_names;
|
||||||
|
}
|
||||||
|
$self->labels_l(\@labels);
|
||||||
|
$self->bgcolor('#00ff00');
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
=head2 as_sql()
|
||||||
|
|
||||||
|
Returns a string which can be used in an SQL 'CREATE TABLE' statement
|
||||||
|
to create the primary key.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_sql {
|
||||||
|
my $self = attr shift;
|
||||||
|
return "PRIMARY KEY (" . join(',',$self->attribute_names) . ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
##----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 html_select_field(\@column_names,$multiple,\@default,$name)
|
||||||
|
|
||||||
|
Returns an HTML form select field where the value consists of the
|
||||||
|
values from the columns which make up the primary key and the labels
|
||||||
|
consist of the corresponding values from I<@column_names>. If
|
||||||
|
I<@column_names> is undefined the labels consist of the values from
|
||||||
|
all column names. If I<$multiple> is defined the field will allow
|
||||||
|
multiple selections. I<@default> is a list of values in the select
|
||||||
|
field which should be selected by default. For fields which allow
|
||||||
|
only a single selection the first value in I<@default> will be used as
|
||||||
|
the default. If I<$name> is defined it will be used as the name of
|
||||||
|
the select field, otherwise the name will consist of the attribute
|
||||||
|
names of the primary key joined by ',' (comma) and the values will
|
||||||
|
consist of the corresponding attribute values joined by ',' (comma).
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub html_select_field {
|
||||||
|
my $self = attr shift;
|
||||||
|
|
||||||
|
my @labels = $_[0] || @{$self->labels_l};
|
||||||
|
my $multiple = $_[1];
|
||||||
|
# this is hard-coded for single-attribute primary keys
|
||||||
|
my $default = $multiple ? $_[2] : $_[2]->[0];
|
||||||
|
my $name = $_[3];
|
||||||
|
my @pk_columns = $self->attribute_names;
|
||||||
|
my $pk = join(',',@pk_columns);
|
||||||
|
my @columns = (@pk_columns,@labels);
|
||||||
|
|
||||||
|
# build SELECT statement
|
||||||
|
my(%tables,%where);
|
||||||
|
my $table_name = $self->BELONGS_TO->name;
|
||||||
|
@{$tables{$table_name}} = @pk_columns;
|
||||||
|
my $order = 'ORDER BY ';
|
||||||
|
for my $label ( @labels ) {
|
||||||
|
my($table_name,@labels);
|
||||||
|
my($attribute) = $BELONGS_TO->get_attributes($label);
|
||||||
|
# handle foreign keys with > 1 attribute here!
|
||||||
|
if ( my($fk) = $BELONGS_TO->in_foreign_key($attribute) ) {
|
||||||
|
# get label columns from related table
|
||||||
|
$table_name = $fk->references->belongs_to->name;
|
||||||
|
@labels = @{$fk->references->labels_l};
|
||||||
|
$where{$table_name} = $fk->sql_where;
|
||||||
|
} else {
|
||||||
|
$table_name = $BELONGS_TO->name;
|
||||||
|
@labels = ($label);
|
||||||
|
}
|
||||||
|
push @{$tables{$table_name}},@labels;
|
||||||
|
for ( @labels ) { $order .= "$table_name.$_," }
|
||||||
|
}
|
||||||
|
chop $order;
|
||||||
|
|
||||||
|
my $from = 'FROM ' . join(',',keys(%tables));
|
||||||
|
my $select = 'SELECT ';
|
||||||
|
# do this table first so that pk columns are returned at the front
|
||||||
|
for ( @{$tables{$table_name}} ) { $select .= "$table_name.$_," }
|
||||||
|
delete $tables{$table_name};
|
||||||
|
while ( my($table,$col_ref) = each %tables ) {
|
||||||
|
for ( @$col_ref ) { $select .= "$table.$_," }
|
||||||
|
}
|
||||||
|
chop $select;
|
||||||
|
my @where = values(%where);
|
||||||
|
my $where = @where ? 'WHERE ' : '';
|
||||||
|
for ( my $i = 0; $i <= $#where; $i++ ) {
|
||||||
|
$where .= ' AND ' if $i;
|
||||||
|
$where .= $where[$i];
|
||||||
|
}
|
||||||
|
my $sql = "$select\n$from\n$where\n$order\n";
|
||||||
|
print STDERR $sql if $_DEBUG;
|
||||||
|
my $sth = DbFramework::Util::do_sql($BELONGS_TO->dbh,$sql);
|
||||||
|
|
||||||
|
# prepare arguments for CGI methods
|
||||||
|
my (@pk_values,%labels,@row);
|
||||||
|
my $i = 0;
|
||||||
|
$pk_values[$i++] = ''; $labels{''} = '** Any Value **';
|
||||||
|
$pk_values[$i++] = 'NULL'; $labels{'NULL'} = 'NULL';
|
||||||
|
while ( my $row_ref = $sth->fetchrow_arrayref ) {
|
||||||
|
@row = @{$row_ref};
|
||||||
|
my $pk = join(',',@row[0..$#pk_columns]); # pk fields
|
||||||
|
$pk_values[$i++] = $pk;
|
||||||
|
|
||||||
|
# label fields
|
||||||
|
for ( @row[$#pk_columns+1..$#row] ) {
|
||||||
|
$labels{$pk} .= ' ' if defined($labels{$pk});
|
||||||
|
$labels{$pk} .= defined($_) ? $_ : 'NULL';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $pk unless $name;
|
||||||
|
|
||||||
|
my $html;
|
||||||
|
my $cgi = new CGI(''); # we just want this object for its methods
|
||||||
|
if ( $multiple ) {
|
||||||
|
$html = $cgi->scrolling_list(-name=>$name,
|
||||||
|
-values=>\@pk_values,
|
||||||
|
-labels=>\%labels,
|
||||||
|
-multiple=>'true',
|
||||||
|
-default=>$default,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$html = $cgi->popup_menu(-name=>$name,
|
||||||
|
-values=>\@pk_values,
|
||||||
|
-labels=>\%labels,
|
||||||
|
-default=>$default,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _input_template {
|
||||||
|
my($self,@fk_attributes) = @_;
|
||||||
|
attr $self;
|
||||||
|
print STDERR "$self: _input_template(@fk_attributes)\n" if $_DEBUG;
|
||||||
|
my $t_name = $BELONGS_TO ? $BELONGS_TO->name : 'UNKNOWN_TABLE';
|
||||||
|
my $in;
|
||||||
|
for my $attribute ( @INCORPORATES_L ) {
|
||||||
|
my $a_name = $attribute->name;
|
||||||
|
unless ( grep(/^$a_name$/,@fk_attributes) ) { # part of foreign key
|
||||||
|
print STDERR "Adding $a_name to input template for pk in $t_name\n" if $_DEBUG;
|
||||||
|
$in .= qq{<TD><DbField ${t_name}.${a_name}></TD>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$in;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _output_template {
|
||||||
|
my($self,@fk_attributes) = @_;
|
||||||
|
attr $self;
|
||||||
|
my $t_name = $BELONGS_TO ? $BELONGS_TO->name : 'UNKNOWN_TABLE';
|
||||||
|
my $out;
|
||||||
|
for ( @INCORPORATES_L ) {
|
||||||
|
my $a_name = $_->name;
|
||||||
|
unless ( grep(/^$a_name$/,@fk_attributes) ) { # part of foreign key
|
||||||
|
$out .= qq{<TD BGCOLOR='$BGCOLOR'><DbValue ${t_name}.${a_name}></TD>};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_heading()
|
||||||
|
|
||||||
|
Returns a string for use as a column heading cell in an HTML table;
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_heading {
|
||||||
|
my $self = attr shift;
|
||||||
|
my @fk_attributes = @_;
|
||||||
|
my @attributes;
|
||||||
|
for ( @INCORPORATES_L ) {
|
||||||
|
my $a_name = $_->name;
|
||||||
|
push(@attributes,$_)
|
||||||
|
unless grep(/^$a_name$/,@fk_attributes); # part of foreign key
|
||||||
|
}
|
||||||
|
return '' unless @attributes;
|
||||||
|
my $html = "<TD BGCOLOR='$BGCOLOR' COLSPAN=".scalar(@attributes).">";
|
||||||
|
for ( @attributes ) {
|
||||||
|
my $a_name = $_->name;
|
||||||
|
my $extra = $_->references->extra
|
||||||
|
? ' ('.$_->references->extra.')'
|
||||||
|
: '';
|
||||||
|
$html .= "$a_name$extra,";
|
||||||
|
}
|
||||||
|
chop($html);
|
||||||
|
"$html</TD>";
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_query_string(\%values)
|
||||||
|
|
||||||
|
Returns a CGI query string consisting of attribute names from the
|
||||||
|
primary key and their corresponding values from I<%values>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_query_string {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %values = $_[0] ? %{$_[0]} : ();
|
||||||
|
my $qs;
|
||||||
|
for ( $self->attribute_names ) {
|
||||||
|
my $value = $values{$_} ? $values{$_} : '';
|
||||||
|
$qs .= "$_=$value&";
|
||||||
|
}
|
||||||
|
chop($qs);
|
||||||
|
uri_escape($qs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_hidden_html(\%values)
|
||||||
|
|
||||||
|
Returns hidden HTML form fields for each primary key attribute. The
|
||||||
|
field name is B<pk_$attribute_name>. The field value is the value in
|
||||||
|
I<%values> whose key is I<$attribute_name>. This method is useful for
|
||||||
|
tracking the previous value of a primary key when you need to update a
|
||||||
|
primary key.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_hidden_html {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %values = $_[0] ? %{$_[0]} : ();
|
||||||
|
my $table_name = $self->BELONGS_TO->name;
|
||||||
|
my $html;
|
||||||
|
for ( $self->attribute_names ) {
|
||||||
|
my $value = defined($values{$_}) ? $values{$_} : '';
|
||||||
|
$html .= qq{<input type="hidden" name="pk_$_" value="$value">\n};
|
||||||
|
}
|
||||||
|
$html;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::Key>
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998,1999 Paul Sharpe. England. All rights reserved.
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
211
lib/DbFramework/Relationship.pm
Normal file
211
lib/DbFramework/Relationship.pm
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
package DbFramework::Relationship;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::DefinitionObject DbFramework::DataModelObject);
|
||||||
|
use DbFramework::ForeignKey;
|
||||||
|
use Alias;
|
||||||
|
use vars qw( $NAME $SRC $DEST @COLUMNS );
|
||||||
|
use Carp;
|
||||||
|
|
||||||
|
# CLASS DATA
|
||||||
|
|
||||||
|
my %relationships;
|
||||||
|
my %fields = (SRC => undef,
|
||||||
|
DEST => undef,
|
||||||
|
ROLES => [], # DbFramework::Role
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $DEBUG = 0;
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless($class->SUPER::new(shift),$class);
|
||||||
|
for my $element (keys %fields) {
|
||||||
|
$self->{_PERMITTED}->{$element} = $fields{$element};
|
||||||
|
}
|
||||||
|
@{$self}{keys %fields} = values %fields;
|
||||||
|
|
||||||
|
attr $self;
|
||||||
|
($SRC,$DEST) = (shift,shift);
|
||||||
|
$self->{COLUMNS} = shift || [];
|
||||||
|
$relationships{$NAME} = [ $SRC,$DEST ];
|
||||||
|
|
||||||
|
if ( $DEBUG ) {
|
||||||
|
carp "relationship name: $NAME";
|
||||||
|
for ( @{$self->{COLUMNS}} ) {
|
||||||
|
carp "column: " . $_->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# if ( ( $_[2] !~ /(1|N)/ && $_[3] !~ /(1|N)/ ) ||
|
||||||
|
# ( $_[6] !~ /(1|N)/ && $_[7] !~ /(1|N)/ ) ) {
|
||||||
|
# print STDERR $_[0]->name, "($_[2],$_[3]) ", $_[4]->name, "($_[6],$_[7])\n";
|
||||||
|
# die "invalid cardinality";
|
||||||
|
# }
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub create_ddl {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $t = shift;
|
||||||
|
my %values = ('rel_id' => 0,
|
||||||
|
'rel_name' => $NAME,
|
||||||
|
'rel_srctbl' => $SRC->table->name,
|
||||||
|
'rel_srcrole' => $SRC->role,
|
||||||
|
'rel_srcmin' => $SRC->min,
|
||||||
|
'rel_srcmax' => $SRC->max,
|
||||||
|
'rel_desttbl' => $DEST->table->name,
|
||||||
|
'rel_destrole' => $DEST->role,
|
||||||
|
'rel_destmin' => $DEST->min,
|
||||||
|
'rel_destmax' => $DEST->max );
|
||||||
|
$t->insert_ddl(\%values);
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# requires (DbFramework::*::Table,DbFramework::*::Table)
|
||||||
|
sub many_to_many {
|
||||||
|
my $class = shift;
|
||||||
|
my($table1,$table2) = @_;
|
||||||
|
|
||||||
|
for ( values(%relationships) ) {
|
||||||
|
if ( ($_->[0][0] == $table1 && $_[1][0] == $table2) ||
|
||||||
|
($_->[0][0] == $table2 && $_[1][0] == $table1) ) {
|
||||||
|
return 1 if ( $_->[0]->max eq 'N' && $_->[1]->max eq 'N' ); # M:N
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# See Fundamentals of Database Systems by Elmasri/Navathe, 1989 p 329ff
|
||||||
|
# for relationship mapping rules
|
||||||
|
# requires: $DbFramework::*::Schema
|
||||||
|
sub set_foreign_key {
|
||||||
|
my $DEBUG = 0;
|
||||||
|
my $self = attr shift;
|
||||||
|
my $schema = shift;
|
||||||
|
|
||||||
|
if ( $DEBUG ) {
|
||||||
|
carp "in DbFramework::Relationship::set_foreign_key";
|
||||||
|
carp "(src)", $SRC->table->name, " ", $SRC->role, " ", $SRC->min, ",", $SRC->max, "\n";
|
||||||
|
carp "(dest)", $DEST->table->name, " ", $DEST->role, " ", $DEST->min, ",", $DEST->max, "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
my $s; # role player to add relationship attributes to
|
||||||
|
|
||||||
|
if ( ($SRC->max == 1) && ($DEST->max == 1) ) { # 1:1
|
||||||
|
# add fk in relation with highest min cardinality
|
||||||
|
my @roles = sort _by_min ($DEST,$SRC);
|
||||||
|
my $null = ($roles[0]->min == 0) ? 1 : 0;
|
||||||
|
$self->_pk_as_fk($null,@roles);
|
||||||
|
$s = $roles[0]->table;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ($SRC->max == 1) && ($DEST->max eq 'N') ||
|
||||||
|
($SRC->max eq 'N') && ($DEST->max == 1) ) { # 1:N
|
||||||
|
# add fk in relation with N cardinality
|
||||||
|
my @roles = sort _by_max ($DEST,$SRC);
|
||||||
|
if ( $DEBUG ) {
|
||||||
|
carp $roles[0]->min, ",", $roles[1]->min;
|
||||||
|
}
|
||||||
|
my $null = ($roles[0]->min == 0) ? 1 : 0;
|
||||||
|
$self->_pk_as_fk($null,@roles);
|
||||||
|
$s = $roles[0]->table;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ($SRC->max eq'N') && ($DEST->max eq 'N') ) { # M:N
|
||||||
|
carp "M:N ", $SRC->table->name, ",", $DEST->table->name if ( $DEBUG );
|
||||||
|
# an M:N can be re-defined as two 1:N relationships with a new table
|
||||||
|
# we don't store these conceptual relationships atm
|
||||||
|
|
||||||
|
my $table_name = $SRC->table->name . '_' . $DEST->table->name;
|
||||||
|
# primary key consists of pk from each table in the M:N (NOT NULL)
|
||||||
|
my(@src_pk,@dest_pk);
|
||||||
|
foreach ( @{$SRC->table->primary_key->columns} ) {
|
||||||
|
push(@src_pk,$_->new($_->name,$_->type,$_->length,0,undef));
|
||||||
|
}
|
||||||
|
foreach ( @{$DEST->table->primary_key->columns} ) {
|
||||||
|
push(@dest_pk,$_->new($_->name,$_->type,$_->length,0,undef));
|
||||||
|
}
|
||||||
|
my $pk = DbFramework::PrimaryKey->new([@src_pk,@dest_pk]);
|
||||||
|
my $n_side = $SRC->table->new($table_name,[@src_pk,@dest_pk],$pk,undef);
|
||||||
|
$n_side->foreign_key(DbFramework::ForeignKey->new($SRC->table,\@src_pk));
|
||||||
|
$n_side->foreign_key(DbFramework::ForeignKey->new($DEST->table,\@dest_pk));
|
||||||
|
$schema->tables( [ $n_side ] );
|
||||||
|
$s = $n_side;
|
||||||
|
}
|
||||||
|
|
||||||
|
# add attributes of relationship to appropriate table
|
||||||
|
if ( @COLUMNS ) {
|
||||||
|
print STDERR "adding columns from relationship $NAME to ",$s->name,"\n"
|
||||||
|
if $DEBUG;
|
||||||
|
$s->add_columns(\@COLUMNS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ascending sort by max cardinality
|
||||||
|
sub _by_max {
|
||||||
|
my $DEBUG = 0;
|
||||||
|
if ( $DEBUG ) {
|
||||||
|
carp "in DbFramework::Relationship::_sort_relationship";
|
||||||
|
carp $a->max, ",", $b->max;
|
||||||
|
}
|
||||||
|
if ( $b->max == 1 ) {
|
||||||
|
return 0 if ( $a->max == 1 );
|
||||||
|
return -1 if ( $a->max eq 'N' );
|
||||||
|
}
|
||||||
|
if ( $b->max eq 'N' ) {
|
||||||
|
return 0 if ( $a->max == 'N' );
|
||||||
|
return 1 if ( $a->max == 1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ascending sort by min cardinality
|
||||||
|
sub _by_min {
|
||||||
|
return ( $a->min <=> $b->min );
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# add primary key from table as a foreign key in related table
|
||||||
|
# require: ($DbFramework::RolePlayer,$DbFramework::RolePlayer)
|
||||||
|
sub _pk_as_fk {
|
||||||
|
my $DEBUG = 0;
|
||||||
|
carp "in DbFramework::Relationship::_pk_as_fk" if ( $DEBUG );
|
||||||
|
my $self = attr shift;
|
||||||
|
my($null,$pk_side,$fk_side) = @_;
|
||||||
|
|
||||||
|
if ( $DEBUG ) {
|
||||||
|
carp "pk side: ", $pk_side->table->name, " fk side: ", $fk_side->table->name;
|
||||||
|
}
|
||||||
|
my $column_name_suffix;
|
||||||
|
if ( $pk_side->table->name eq $fk_side->table->name ) { # recursive
|
||||||
|
$column_name_suffix = '_' . $NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @columns;
|
||||||
|
foreach ( @{$pk_side->table->primary_key->columns} ) {
|
||||||
|
# be sure to create new columns from the same (sub)class by calling
|
||||||
|
# new() on an object
|
||||||
|
push(@columns,$_->new($_->name . $column_name_suffix,$_->type,$_->length,$null,undef));
|
||||||
|
}
|
||||||
|
|
||||||
|
$fk_side->table->add_columns(\@columns);
|
||||||
|
$fk_side->table->foreign_key(DbFramework::ForeignKey->new($pk_side->table,\@columns));
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
886
lib/DbFramework/Table.pm
Normal file
886
lib/DbFramework/Table.pm
Normal file
@ -0,0 +1,886 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::Table - Table class
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::Table;
|
||||||
|
|
||||||
|
$t = new DbFramework::Table new($name,\@attributes,$pk,$dbh,$dm);
|
||||||
|
$t->init_db_metadata($catalog);
|
||||||
|
$dbh = $t->dbh($dbh);
|
||||||
|
$pk = $t->is_identified_by($pk);
|
||||||
|
@fks = @{$t->has_foreign_keys_l};
|
||||||
|
%fks = %{$t->has_foreign_keys_h};
|
||||||
|
@keys = @{$t->is_accessed_using_l};
|
||||||
|
@a = $t->get_attributes(@names);
|
||||||
|
@n = $t->attribute_names;
|
||||||
|
$html = $t->as_html_form;
|
||||||
|
$s = $t->as_string;
|
||||||
|
$sql = $t->as_sql;
|
||||||
|
$rows = $t->delete($conditions);
|
||||||
|
$pk = $t->insert(\%values);
|
||||||
|
$rows = $t->update(\%values,$conditions);
|
||||||
|
@lol = $t->select(\@columns,$conditions,$order);
|
||||||
|
@loh = $t->select_loh(\@columns,$conditions,$order);
|
||||||
|
@a = $t->non_key_attributes;
|
||||||
|
$dm = $t->belongs_to;
|
||||||
|
@fks = $t->in_foreign_key($attribute);
|
||||||
|
do_something if $t->in_key($attribute);
|
||||||
|
do_something if $t->in_primary_key($attribute);
|
||||||
|
do_something if $t->in_any_key($attribute);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
A B<DbFramework::Table> object represents a database table (entity).
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::DefinitionObject>
|
||||||
|
|
||||||
|
B<DbFramework::DataModelObject>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::Table;
|
||||||
|
use strict;
|
||||||
|
use vars qw( $NAME @CONTAINS_L $IS_IDENTIFIED_BY $_DEBUG @IS_ACCESSED_USING_L
|
||||||
|
@HAS_FOREIGN_KEYS_L $DBH %TEMPLATE_H @CGI_PK %FORM_H
|
||||||
|
$BELONGS_TO );
|
||||||
|
use base qw(DbFramework::DefinitionObject DbFramework::DataModelObject);
|
||||||
|
use DbFramework::PrimaryKey;
|
||||||
|
use DbFramework::DataType::ANSII;
|
||||||
|
use DbFramework::DataType::Mysql;
|
||||||
|
use DbFramework::Attribute;
|
||||||
|
use DbFramework::Catalog;
|
||||||
|
use Alias;
|
||||||
|
use Carp;
|
||||||
|
use CGI;
|
||||||
|
|
||||||
|
# CLASS DATA
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
# Entity 1:1 IsIdentifiedBy 1:1 PrimaryKey
|
||||||
|
IS_IDENTIFIED_BY => undef,
|
||||||
|
# Entity 1:1 HasForeignKeys 0:N ForeignKey
|
||||||
|
HAS_FOREIGN_KEYS_L => undef,
|
||||||
|
HAS_FOREIGN_KEYS_H => undef,
|
||||||
|
# Table 1:1 IsAccessedUsing 0:N Key
|
||||||
|
IS_ACCESSED_USING_L => undef,
|
||||||
|
# Table 1:1 BelongsTo 1:1 DataModel
|
||||||
|
BELONGS_TO => undef,
|
||||||
|
DBH => undef,
|
||||||
|
TEMPLATE_H => undef,
|
||||||
|
FORM_H => undef,
|
||||||
|
);
|
||||||
|
my $formsdir = '/usr/local/etc/dbframework/forms';
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($name,\@attributes,$pk,$dbh,$dm)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::Table> object. I<$dbh> is a DBI database
|
||||||
|
handle which refers to a database containing a table named I<$name>.
|
||||||
|
I<@attribues> is a list of B<DbFramework::Attribute> objects.
|
||||||
|
I<$primary> is a B<DbFramework::PrimaryKey> object. I<@attributes>
|
||||||
|
and I<$primary> can be omitted if you plan to use the
|
||||||
|
B<init_db_metadata()> object method (see below). I<$dm> is a
|
||||||
|
B<DbFramework::DataModel> object to which this table belongs.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless($class->SUPER::new(shift,shift),$class);
|
||||||
|
for my $element (keys %fields) {
|
||||||
|
$self->{_PERMITTED}->{$element} = $fields{$element};
|
||||||
|
}
|
||||||
|
@{$self}{keys %fields} = values %fields;
|
||||||
|
$self->is_identified_by(shift);
|
||||||
|
$self->dbh(shift);
|
||||||
|
$self->belongs_to(shift);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
Foreign keys in a table can be accessed using the
|
||||||
|
I<HAS_FOREIGN_KEYS_L> and I<HAS_FOREIGN_KEYS_H> attributes. B<Note>
|
||||||
|
that foreign key objects will not be created automatically by calling
|
||||||
|
init_db_metadata() on a table object. If you want to automatically
|
||||||
|
create foreign key objects for your tables you should use call
|
||||||
|
init_db_metadata() on a B<DbFramework::DataModel> object (see
|
||||||
|
L<DbFramework::Datamodel/init_db_metadata()>). Other keys (indexes)
|
||||||
|
defined for a table can be accessed using the I<IS_ACCESSED_USING_L>
|
||||||
|
attribute. See L<DbFramework::Util/AUTOLOAD()> for the accessor
|
||||||
|
methods for these attributes.
|
||||||
|
|
||||||
|
=head2 is_identified_by($primary)
|
||||||
|
|
||||||
|
I<$primary> is a B<DbFramework::PrimaryKey> object. If supplied sets
|
||||||
|
the table's primary key to I<$primary>. Returns a
|
||||||
|
B<DbFramework::PrimaryKey> object with is the table's primary key.
|
||||||
|
|
||||||
|
=head2 dbh($dbh)
|
||||||
|
|
||||||
|
I<$dbh> is a DBI database handle. If supplied sets the database
|
||||||
|
handle associated with the table. Returns the database handle
|
||||||
|
associated with the table.
|
||||||
|
|
||||||
|
=head2 belongs_to($dm)
|
||||||
|
|
||||||
|
I<$dm> is a B<DbFramework::DataModel> object. If supplied sets the
|
||||||
|
data model to which the table belongs. Returns the data model to
|
||||||
|
which the table belongs.
|
||||||
|
|
||||||
|
=head2 get_attributes(@names)
|
||||||
|
|
||||||
|
Returns a list of B<DbFramework::Attribute> objects. I<@names> is a
|
||||||
|
list of attribute names to return. If I<@names> is undefined all
|
||||||
|
attributes associated with the table are returned.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_attributes {
|
||||||
|
my $self = attr shift;
|
||||||
|
print STDERR "getting attributes for (",join(',',@_),")\n" if $_DEBUG;
|
||||||
|
return @_ ? $self->contains_h_byname(@_) # specific attributes
|
||||||
|
: @{$self->contains_l}; # all attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 attribute_names()
|
||||||
|
|
||||||
|
Returns a list of attribute names for the table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub attribute_names {
|
||||||
|
my $self = attr shift;
|
||||||
|
my @names;
|
||||||
|
for ( @CONTAINS_L ) { push(@names,$_->name) }
|
||||||
|
@names;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_form()
|
||||||
|
|
||||||
|
Returns HTML form fields for all attributes in the table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_form {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $form;
|
||||||
|
for ( @CONTAINS_L ) { $form .= "<tr><td>" . $_->as_html_form_field . "</td></tr>\n" }
|
||||||
|
$form;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 in_foreign_key($attribute)
|
||||||
|
|
||||||
|
I<$attribute> is a B<DbFramework::Attribute> object. Returns a list
|
||||||
|
of B<DbFramework::ForeignKey> objects which contain I<$attribute>.
|
||||||
|
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub in_foreign_key {
|
||||||
|
my($self,$attribute) = (attr shift,shift);
|
||||||
|
my $name = $attribute->name;
|
||||||
|
my @in = ();
|
||||||
|
print STDERR "foreign keys: @HAS_FOREIGN_KEYS_L\n" if $_DEBUG;
|
||||||
|
for ( @HAS_FOREIGN_KEYS_L ) {
|
||||||
|
my @fk_names = $_->attribute_names;
|
||||||
|
push @in,$_ if grep(/^$name$/,@fk_names);
|
||||||
|
}
|
||||||
|
return @in;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 in_primary_key($attribute)
|
||||||
|
|
||||||
|
I<$attribute> is a B<DbFramework::Attribute> object. Returns true if
|
||||||
|
I<$attribute> is a part of the primary key in the table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub in_primary_key {
|
||||||
|
my($self,$attribute) = (attr shift,shift);
|
||||||
|
my $name = $attribute->name;
|
||||||
|
my @pk_names = $self->is_identified_by->attribute_names;
|
||||||
|
print STDERR "Looking for $name in @pk_names\n" if $_DEBUG;
|
||||||
|
return grep(/^$name$/,@pk_names) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 in_key($attribute)
|
||||||
|
|
||||||
|
I<$attribute> is a B<DbFramework::Attribute> object. Returns true if
|
||||||
|
I<$attribute> is a part of a key (index) in the table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub in_key {
|
||||||
|
my($self,$attribute) = (attr shift,shift);
|
||||||
|
my @k_names = ();
|
||||||
|
my $name = $attribute->name;
|
||||||
|
for ( @IS_ACCESSED_USING_L ) { push(@k_names,$_->attribute_names) }
|
||||||
|
print STDERR "Looking for $name in @k_names\n" if $_DEBUG;
|
||||||
|
return grep(/^$name$/,@k_names) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 in_any_key($attribute)
|
||||||
|
|
||||||
|
I<$attribute> is a B<DbFramework::Attribute> object. Returns true if
|
||||||
|
I<$attribute> is a part of a key (index), a primary key or a foreign
|
||||||
|
key in the table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub in_any_key {
|
||||||
|
my($self,$attribute) = (attr shift,shift);
|
||||||
|
print STDERR "$self->in_any_key($attribute)\n" if $_DEBUG;
|
||||||
|
return ($self->in_key($attribute) ||
|
||||||
|
$self->in_primary_key($attribute) ||
|
||||||
|
$self->in_foreign_key($attribute)) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 non_key_attributes()
|
||||||
|
|
||||||
|
Returns a list of B<DbFramework::Attribute> objects which are not
|
||||||
|
members of any key, primary key or foreign key.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub non_key_attributes {
|
||||||
|
my $self = attr shift;
|
||||||
|
my @non_key;
|
||||||
|
for ( @CONTAINS_L ) { push(@non_key,$_) unless $self->in_any_key($_) }
|
||||||
|
@non_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#=head2 html_hidden_pk_list()
|
||||||
|
|
||||||
|
#Returns a 'hidden' HTML form field whose key consists of the primary
|
||||||
|
#key column names separated by '+' characters and whose value is the
|
||||||
|
#current list of @CGI_PK
|
||||||
|
|
||||||
|
#=cut
|
||||||
|
|
||||||
|
#sub html_hidden_pk_list {
|
||||||
|
# my $self = attr shift;
|
||||||
|
# my $cgi = new CGI('');
|
||||||
|
# return $cgi->hidden(join('+',@{$PRIMARY->column_names}),@CGI_PK) . "\n";
|
||||||
|
#}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_string()
|
||||||
|
|
||||||
|
Returns table details as a string.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_string {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $s = "Table: $NAME\n";
|
||||||
|
for ( @{$self->contains_l} ) { $s .= $_->as_string }
|
||||||
|
return $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 init_db_metadata($catalog)
|
||||||
|
|
||||||
|
Returns an initialised B<DbFramework::Table> object for the table
|
||||||
|
matching this object's name() in the database referenced by dbh().
|
||||||
|
I<$catalog> is a B<DbFramework::Catalog> object.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub init_db_metadata {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $catalog = shift;
|
||||||
|
|
||||||
|
my $driver = $self->belongs_to->driver;
|
||||||
|
my($sql,$sth,$rows,$rv);
|
||||||
|
# query to get typeinfo
|
||||||
|
if ( ! defined($self->belongs_to) || $driver eq 'mSQL' ) {
|
||||||
|
$sql = qq{SELECT * FROM $NAME};
|
||||||
|
} else {
|
||||||
|
# more efficient query for getting typeinfo but not supported by mSQL
|
||||||
|
$sql = qq{SELECT * FROM $NAME WHERE 1 = 0};
|
||||||
|
}
|
||||||
|
$sth = DbFramework::Util::do_sql($DBH,$sql);
|
||||||
|
|
||||||
|
my %datatypes = ( mysql => 'Mysql' ); # driver-specific datatype classes
|
||||||
|
my @columns;
|
||||||
|
for ( my $i = 0; $i < $sth->{NUM_OF_FIELDS}; $i++ ) {
|
||||||
|
my $class = ( defined($self->belongs_to) &&
|
||||||
|
exists($datatypes{$driver})
|
||||||
|
)
|
||||||
|
? $datatypes{$driver}
|
||||||
|
: 'ANSII';
|
||||||
|
my $name = $sth->{NAME}->[$i];
|
||||||
|
# if driver-specific class exists, get the driver-specific type
|
||||||
|
my($type,$ansii_type,$default,$extra);
|
||||||
|
SWITCH: for ( $class ) {
|
||||||
|
/Mysql/ && do {
|
||||||
|
print STDERR "mysql_type = ",join(',',@{$sth->{mysql_type}}),"\n"
|
||||||
|
if $_DEBUG;
|
||||||
|
$type = $sth->{mysql_type}->[$i];
|
||||||
|
$ansii_type = $sth->{TYPE}->[$i];
|
||||||
|
my $sth = DbFramework::Util::do_sql($DBH,"DESCRIBE $NAME $name");
|
||||||
|
my $metadata = $sth->fetchrow_hashref;
|
||||||
|
($default,$extra) = ($metadata->{Default},uc($metadata->{Extra}));
|
||||||
|
$sth->finish;
|
||||||
|
last SWITCH;
|
||||||
|
};
|
||||||
|
/ANSII/ && do {
|
||||||
|
$ansii_type = $type = $sth->{TYPE}->[$i];
|
||||||
|
last SWITCH;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$class = "DbFramework::DataType::$class";
|
||||||
|
my $precision = $sth->{PRECISION}->[$i];
|
||||||
|
|
||||||
|
my $d = $class->new($self->belongs_to,
|
||||||
|
$type,
|
||||||
|
$ansii_type,
|
||||||
|
$precision,
|
||||||
|
$extra,
|
||||||
|
);
|
||||||
|
my $a = new DbFramework::Attribute($sth->{NAME}->[$i],
|
||||||
|
$default,
|
||||||
|
$sth->{NULLABLE}->[$i],
|
||||||
|
$d
|
||||||
|
);
|
||||||
|
push(@columns,$a);
|
||||||
|
}
|
||||||
|
$self->_init(\@columns);
|
||||||
|
|
||||||
|
## add keys
|
||||||
|
$catalog->set_primary_key($self);
|
||||||
|
$catalog->set_keys($self);
|
||||||
|
|
||||||
|
#$self->_templates; # set default templates
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_sql()
|
||||||
|
|
||||||
|
Returns a string which can be used to create a table in an SQL 'CREATE
|
||||||
|
TABLE' statement.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_sql {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $sql = "CREATE TABLE $NAME (\n";
|
||||||
|
for ( @{$self->contains_l} ) { $sql .= "\t" . $_->as_sql($DBH) . ",\n"; }
|
||||||
|
$sql .= "\t" . $IS_IDENTIFIED_BY->as_sql;
|
||||||
|
for ( @IS_ACCESSED_USING_L ) { $sql .= ",\n\t" . $_->as_sql }
|
||||||
|
for ( @HAS_FOREIGN_KEYS_L ) { $sql .= ",\n\t" . $_->as_sql }
|
||||||
|
return "$sql\n)";
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#=head2 validate_foreign_keys()
|
||||||
|
|
||||||
|
#Ensure that foreign key definitions match related primary key
|
||||||
|
#definitions.
|
||||||
|
|
||||||
|
#=cut
|
||||||
|
|
||||||
|
sub validate_foreign_keys {
|
||||||
|
my $self = shift;
|
||||||
|
attr $self;
|
||||||
|
|
||||||
|
for my $fk ( @HAS_FOREIGN_KEYS_L ) {
|
||||||
|
my $fk_name = $fk->name;
|
||||||
|
my @fk_attributes = @{$fk->incorporates_l};
|
||||||
|
my @pk_attributes = @{$fk->references->incorporates_l};
|
||||||
|
@fk_attributes == @pk_attributes ||
|
||||||
|
die "Number of attributes in foreign key $NAME:$fk_name(",scalar(@fk_attributes),") doesn't match that of related primary key (",scalar(@pk_attributes),")";
|
||||||
|
for ( my $i = 0; $i <= $#fk_attributes; $i++) {
|
||||||
|
my($fk_aname,$pk_aname) =
|
||||||
|
($fk_attributes[$i]->name,$pk_attributes[$i]->name);
|
||||||
|
print STDERR "$fk_aname eq $pk_aname\n" if $_DEBUG;
|
||||||
|
#$fk_aname eq $pk_aname ||
|
||||||
|
# die "foreign key component $NAME:$fk_aname ne primary key component $pk_aname\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 delete($conditions)
|
||||||
|
|
||||||
|
DELETE rows FROM the table associated with this object WHERE the
|
||||||
|
conditions in I<$conditions> are met. Returns the number of rows
|
||||||
|
deleted if supplied by the DBI driver.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub delete {
|
||||||
|
my($self,$conditions) = (attr shift,shift);
|
||||||
|
|
||||||
|
my $sql = "DELETE FROM $NAME";
|
||||||
|
$sql .= " WHERE $conditions" if $conditions;
|
||||||
|
print STDERR "$sql\n" if $_DEBUG;
|
||||||
|
return $DBH->do($sql) || die($DBH->errstr);
|
||||||
|
}
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 insert(\%values)
|
||||||
|
|
||||||
|
INSERT INTO the table columns corresponding to the keys of I<%values>
|
||||||
|
the VALUES corresponding to the values of I<%values>. Returns the
|
||||||
|
primary key of the inserted row if it is a Mysql 'AUTO_INCREMENT'
|
||||||
|
column or -1.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub insert {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %values = %{$_[0]};
|
||||||
|
|
||||||
|
my(@columns,$values);
|
||||||
|
for ( keys(%values) ) {
|
||||||
|
next unless defined($values{$_});
|
||||||
|
push(@columns,$_);
|
||||||
|
my $type = $self->get_attributes($_)->references->ansii_type;
|
||||||
|
print STDERR "value = $values{$_}, type = $type\n" if $_DEBUG;
|
||||||
|
$values .= $self->_quote($values{$_},$type) . ',';
|
||||||
|
}
|
||||||
|
chop $values;
|
||||||
|
my $columns = '(' . join(',',@columns). ')';
|
||||||
|
|
||||||
|
my $sql = "INSERT INTO $NAME $columns VALUES ($values)";
|
||||||
|
print STDERR "$sql\n" if $_DEBUG;
|
||||||
|
|
||||||
|
my $sth = $DBH->prepare($sql) || die $DBH->errstr;
|
||||||
|
my $rv = $sth->execute || die "$sql\n" . $sth->errstr . "\n";
|
||||||
|
my $rc = $sth->finish;
|
||||||
|
|
||||||
|
if ( $self->belongs_to->driver eq 'mysql' ) {
|
||||||
|
# id of auto_increment field
|
||||||
|
return $sth->{mysql_insertid};
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 update(\%values,$conditions)
|
||||||
|
|
||||||
|
UPDATE the table SETting the columns matching the keys in %values to
|
||||||
|
the values in %values WHERE I<$conditions> are met. Returns the
|
||||||
|
number of rows updated if supplied by the DBI driver.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub update {
|
||||||
|
my $self = attr shift;
|
||||||
|
my %values = %{$_[0]};
|
||||||
|
my $conditions = $_[1];
|
||||||
|
|
||||||
|
my $values;
|
||||||
|
for ( keys %values ) {
|
||||||
|
next unless $values{$_};
|
||||||
|
my $dt = $self->get_attributes($_)->references;
|
||||||
|
my $type = $dt->ansii_type;
|
||||||
|
print STDERR "\$type = ",$dt->name,"($type)\n" if $_DEBUG;
|
||||||
|
$values .= "$_ = " . $self->_quote($values{$_},$type) . ',';
|
||||||
|
}
|
||||||
|
chop $values;
|
||||||
|
|
||||||
|
my $sql = "UPDATE $NAME SET $values";
|
||||||
|
$sql .= " WHERE $conditions" if $conditions;
|
||||||
|
print STDERR "$sql\n" if $_DEBUG;
|
||||||
|
return $DBH->do($sql) || die($DBH->errstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 select(\@columns,$conditions,$order)
|
||||||
|
|
||||||
|
Returns a list of lists of values by SELECTing values FROM I<@columns>
|
||||||
|
WHERE rows meet I<$conditions> ORDERed BY the list of columns in
|
||||||
|
I<$order>. Strings in I<@columns> can refer to functions supported by
|
||||||
|
the database in a SELECT clause e.g.
|
||||||
|
|
||||||
|
C<@columns = q/sin(foo),cos(bar),tan(baz)/;>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub select {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $sth = $self->_do_select(@_);
|
||||||
|
my @things;
|
||||||
|
# WARNING!
|
||||||
|
# Can't use fetchrow_arrayref here as it returns the *same* ref (man DBI)
|
||||||
|
while ( my @attributes = $sth->fetchrow_array ) {
|
||||||
|
print "@attributes\n" if $_DEBUG;
|
||||||
|
push(@things,\@attributes);
|
||||||
|
}
|
||||||
|
if ( $_DEBUG ) {
|
||||||
|
print "@things\n";
|
||||||
|
for ( @things ) { print "@{$_}\n" }
|
||||||
|
}
|
||||||
|
return @things;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 select_loh(\@columns,$conditions,$order)
|
||||||
|
|
||||||
|
Returns a list of hashrefs containing B<(column_name,value)> pairs by
|
||||||
|
SELECTing values FROM I<@columns> WHERE rows meet I<$conditions>
|
||||||
|
ORDERed BY the list of columns in I<$order>. Strings in I<@columns>
|
||||||
|
can refer to functions supported by the database in a SELECT clause
|
||||||
|
e.g.
|
||||||
|
|
||||||
|
C<@columns = q/sin(foo),cos(bar),tan(baz)/;>
|
||||||
|
|
||||||
|
The keys in the hashrefs will match the name of the function applied
|
||||||
|
to the column i.e.
|
||||||
|
|
||||||
|
C<@loh = $foo-E<gt>select(\@columns);>
|
||||||
|
|
||||||
|
C<print "sin(foo) = $loh[0]-E<gt>{sin(foo)}\n";>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub select_loh {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $sth = $self->_do_select(@_);
|
||||||
|
my @things;
|
||||||
|
while ( $_ = $sth->fetchrow_hashref ) {
|
||||||
|
# fetchrow_hashref may not return a fresh hashref in future (man DBI)
|
||||||
|
my %hash = %{$_};
|
||||||
|
push(@things,\%hash);
|
||||||
|
}
|
||||||
|
return @things;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# select(\@columns,$conditions,$order)
|
||||||
|
# returns a statement handle for a SELECT
|
||||||
|
|
||||||
|
sub _do_select {
|
||||||
|
my $self = attr shift;
|
||||||
|
my($columns_ref,$conditions,$order,$function_ref) = @_;
|
||||||
|
my @columns = defined($columns_ref) ? @$columns_ref : $self->attribute_names;
|
||||||
|
my $sql = "SELECT " . join(',',@columns) . " FROM $NAME";
|
||||||
|
$sql .= " WHERE $conditions" if $conditions;
|
||||||
|
$sql .= " ORDER BY $order" if $order;
|
||||||
|
print STDERR "$sql\n" if $_DEBUG;
|
||||||
|
my $sth = $DBH->prepare($sql) || die($DBH->errstr);
|
||||||
|
my $rv = $sth->execute || die "$sql\n" . $sth->errstr . "\n";
|
||||||
|
return $sth;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#=head2 fill_template($name,\%values)
|
||||||
|
|
||||||
|
#Return the filled HTML template named I<$name>. A template can
|
||||||
|
#contain special placeholders representing columns in a database table.
|
||||||
|
#Placeholders in I<$template> can take the following forms:
|
||||||
|
|
||||||
|
#=over 4
|
||||||
|
|
||||||
|
#=item B<E<lt>DbField table.column [value=value] [type=type]E<gt>>
|
||||||
|
|
||||||
|
#If the table's name() matches I<table> in a B<DbField> placeholder,
|
||||||
|
#the placeholder will be replaced with the corresponding HTML form
|
||||||
|
#field for the column named I<column> with arguments I<value> and
|
||||||
|
#I<type> (see L<DbFramework::Attribute/html_form_field()>). If
|
||||||
|
#I<%values> is supplied placeholders will have the values in I<%values>
|
||||||
|
#added where a key in I<%values> matches a column name in the table.
|
||||||
|
|
||||||
|
#=item B<E<lt>DbFKey table.fk_name[,column...]E<gt>>
|
||||||
|
|
||||||
|
#If the table's name() matches I<table> in a B<DbFKey> placeholder, the
|
||||||
|
#placeholder will be replaced with the a selection box containing
|
||||||
|
#values and labels from the primary key columns in the related table.
|
||||||
|
#Primary key attribute values in I<%values> will be used to select the
|
||||||
|
#default item in the selection box.
|
||||||
|
|
||||||
|
#=item B<E<lt>DbValue table.column[,column...]E<gt>>
|
||||||
|
|
||||||
|
#If the table's name() matches I<table> in a B<DbValue> placeholder,
|
||||||
|
#the placeholder will be replaced with the values in I<%values> where a
|
||||||
|
#key in I<%values> matches a column name in the table.
|
||||||
|
|
||||||
|
#=item B<E<lt>DbJoin table.column.template[.order][.column_name[;column_name...]]E<gt>>
|
||||||
|
|
||||||
|
#A B<DbJoin> placeholder will cause a join to be performed between this
|
||||||
|
#table and the table specified in I<table> over the column I<column>
|
||||||
|
#where the value equals I<%values{column}> orderd by I<order>. Values
|
||||||
|
#will be selected from columns specified with I<column_name>.
|
||||||
|
#I<column_name> may refer to functions supported by the database in a
|
||||||
|
#B<SELECT> clause. If no I<column_name>s are specified, the values
|
||||||
|
#from all columns from I<table> will be selected. The placeholder will
|
||||||
|
#be replaced by the concatenation of I<template> filled with the values
|
||||||
|
#from each row returned by the join. B<DbJoin> placeholders may be
|
||||||
|
#chained.
|
||||||
|
|
||||||
|
#=back
|
||||||
|
|
||||||
|
#The easiest way to pass the values required to fill a template is by
|
||||||
|
#calling fill_template() with the name of the template and the hashrefs
|
||||||
|
#returned by select_loh() e.g.
|
||||||
|
|
||||||
|
# for ( $foo->select_loh(\@columns) ) {
|
||||||
|
# $html .= $foo->fill_template($template,$_)
|
||||||
|
# }
|
||||||
|
|
||||||
|
#=cut
|
||||||
|
|
||||||
|
sub fill_template {
|
||||||
|
my($self,$name,$values) = (attr shift,shift,shift);
|
||||||
|
print STDERR "filling template '$name' for table '$NAME'\n" if $_DEBUG;
|
||||||
|
return '' unless exists $TEMPLATE_H{$name};
|
||||||
|
|
||||||
|
my $template = $TEMPLATE_H{$name};
|
||||||
|
# if ( $_DEBUG ) {
|
||||||
|
# print STDERR "\$template = $template\n";
|
||||||
|
# print STDERR "\$values = ", defined($values) ? %$values : 'undef',"\n" ;
|
||||||
|
# }
|
||||||
|
# my $error;
|
||||||
|
# my $rc = Parse::ePerl::Expand({
|
||||||
|
# Script => $template,
|
||||||
|
# Result => \$template,
|
||||||
|
# Error => \$error,
|
||||||
|
# });
|
||||||
|
# die "Error parsing ePerl in template $name: $error" unless $rc;
|
||||||
|
# if ( $_DEBUG ) {
|
||||||
|
# print STDERR "\$rc = $rc\n";
|
||||||
|
# print STDERR "\$template = ",defined($template) ? $template : 'undef',"\n";
|
||||||
|
# }
|
||||||
|
|
||||||
|
my %fk = %{$self->has_foreign_keys_h};
|
||||||
|
|
||||||
|
# insert values into template
|
||||||
|
if ( defined($values) ) {
|
||||||
|
# only works for single column foreign keys
|
||||||
|
$template =~ s/<DbJoin\s+(\w+)\.(\w+)\.(\w+)(\.(\w*))?(\.(.*))?\s*>/$self->_join_fill_template($1,$2,$3,$5,$values->{$2},$7)/eg;
|
||||||
|
|
||||||
|
$template =~ s/(<DbField\s+$NAME\.)(\w+)(\s+value=)(.*?\s*)>/$1$2 value=$values->{$2}>/g;
|
||||||
|
$template =~ s/(<DbField $NAME\.)(\w+)>/$1$2 value=$values->{$2}>/g;
|
||||||
|
# handle multiple attributes here for foreign key values
|
||||||
|
$template =~ s/<DbValue\s+$NAME\.([\w,]+)\s*>/join(',',@{$values}{split(m{,},$1)})/eg;
|
||||||
|
# values which are the result of applying functions to a column
|
||||||
|
$template =~ s/<DbValue\s+$NAME\.(.+)\s*>/$values->{$1}/g;
|
||||||
|
}
|
||||||
|
|
||||||
|
#print STDERR "template = \n$TEMPLATE_H{$name}\n\$values = ",%$values,"\n" if $_DEBUG;
|
||||||
|
|
||||||
|
# foreign key placeholders
|
||||||
|
$template =~ s/<DbFKey\s+$NAME\.(\w+)\s*>/$fk{$1}->as_html_form_field($values)/eg;
|
||||||
|
|
||||||
|
# form field placeholders
|
||||||
|
$template =~ s/<DbField\s+$NAME\.(\w+)\s+value=(.*?)\s+type=(.*?)>/$self->_as_html_form_field($1,$2,$3)/eg;
|
||||||
|
$template =~ s/<DbField\s+$NAME\.(\w+)\s+value=(.*?)>/$self->_as_html_form_field($1,$2)/eg;
|
||||||
|
$template =~ s/<DbField $NAME\.(\w+)>/$self->_as_html_form_field($1)/eg;
|
||||||
|
|
||||||
|
$template;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _as_html_form_field {
|
||||||
|
my($self,$attribute) = (shift,shift);
|
||||||
|
my @attributes = $self->get_attributes($attribute);
|
||||||
|
$attributes[0]->as_html_form_field(@_);
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#=head2 set_templates(%templates)
|
||||||
|
|
||||||
|
#Adds the contents of the files which are the values in I<%templates>
|
||||||
|
#as templates named by the keys in I<%templates>. Returns a reference
|
||||||
|
#to a hash of all templates.
|
||||||
|
|
||||||
|
#=cut
|
||||||
|
|
||||||
|
sub set_templates {
|
||||||
|
my $self = attr shift;
|
||||||
|
if ( @_ ) {
|
||||||
|
my %templates = @_;
|
||||||
|
my @templates;
|
||||||
|
for ( keys %templates ) {
|
||||||
|
open(T,"<$templates{$_}") || die "Couldn't open template $templates{$_}";
|
||||||
|
my @t = <T>;
|
||||||
|
close T;
|
||||||
|
push(@templates,$_,"@t");
|
||||||
|
}
|
||||||
|
$self->template_h(\@templates);
|
||||||
|
}
|
||||||
|
\%TEMPLATE_H;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _templates {
|
||||||
|
my $self = attr shift;
|
||||||
|
$self->_template('input','_input_template');
|
||||||
|
$self->_template('output','_output_template');
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _template {
|
||||||
|
my($self,$method) = (attr shift,shift);
|
||||||
|
my @fk_attributes;
|
||||||
|
for ( @HAS_FOREIGN_KEYS_L ) { push(@fk_attributes,$_->attribute_names) }
|
||||||
|
my $t = $IS_IDENTIFIED_BY->$method(@fk_attributes) || '';
|
||||||
|
for ( $self->non_key_attributes ) { $t .= $_->$method($NAME) }
|
||||||
|
for ( @IS_ACCESSED_USING_L ) { $t .= $_->$method() }
|
||||||
|
for ( @HAS_FOREIGN_KEYS_L ) { $t .= $_->$method() }
|
||||||
|
$t;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#=head2 read_form($name,$path)
|
||||||
|
|
||||||
|
#Assigns the contents of a file to a template. I<$name> is the name of
|
||||||
|
#the template and I<$path> is the path to the file. If I<$path> is
|
||||||
|
#undefined, tries to read
|
||||||
|
#F</usr/local/etc/dbframework/$db/$table/$name.form>, where I<$db> is
|
||||||
|
#the name of the database containing the table and I<$table> is the
|
||||||
|
#name of the table. See L<Forms and Templates>.
|
||||||
|
|
||||||
|
#=cut
|
||||||
|
|
||||||
|
sub read_form {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $name = shift;
|
||||||
|
my $db = $self->belongs_to ? $self->belongs_to->db : 'UNKNOWN_DB';
|
||||||
|
my $path = shift || "$formsdir/$db/$NAME/$name.form";
|
||||||
|
$TEMPLATE_H{$name} = _readfile_no_comments($path,"Couldn't open form");
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub _readfile_no_comments {
|
||||||
|
my($file,$error) = @_;
|
||||||
|
open FH,"<$file" or die "$error: $file: $!";
|
||||||
|
my $lines;
|
||||||
|
while (<FH>) {
|
||||||
|
next if /^\s*#/;
|
||||||
|
$lines .= $_;
|
||||||
|
}
|
||||||
|
close FH;
|
||||||
|
$lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 as_html_heading()
|
||||||
|
|
||||||
|
Returns a string for use as a table heading row in an HTML table.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub as_html_heading {
|
||||||
|
my $self = attr shift;
|
||||||
|
my $method = 'as_html_heading';
|
||||||
|
my @fk_attributes;
|
||||||
|
for ( @HAS_FOREIGN_KEYS_L ) { push(@fk_attributes,$_->attribute_names) }
|
||||||
|
my $html = $IS_IDENTIFIED_BY->$method(@fk_attributes);
|
||||||
|
for ( $self->non_key_attributes ) { $html .= $_->$method() }
|
||||||
|
my @key_attributes = (@fk_attributes, $IS_IDENTIFIED_BY->attribute_names);
|
||||||
|
my(%key_attributes,$bgcolor);
|
||||||
|
for my $key ( @IS_ACCESSED_USING_L ) {
|
||||||
|
# get unique hash of key attributes
|
||||||
|
for ( @{$key->incorporates_l} ) {
|
||||||
|
my $name = $_->name;
|
||||||
|
$key_attributes{$_->name} = $_ unless grep(/^$name$/,@key_attributes);
|
||||||
|
}
|
||||||
|
$bgcolor = $key->bgcolor;
|
||||||
|
}
|
||||||
|
for ( values(%key_attributes) ) { $html .= $_->$method($bgcolor) }
|
||||||
|
for ( @HAS_FOREIGN_KEYS_L ) { $html .= $_->$method() }
|
||||||
|
"<TR>$html</TR>";
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Returns an HTML string by filling I<$template> in I<table> with the
|
||||||
|
# values SELECTed WHERE the values in the I<$column_name> match $value.
|
||||||
|
|
||||||
|
sub _join_fill_template {
|
||||||
|
my $self = attr shift;
|
||||||
|
my($table_name,$column_name,$template,$order,$value,$columns) = @_;
|
||||||
|
print STDERR "\@_ = @_\n\$columns = $columns\n" if $_DEBUG;
|
||||||
|
my($table) = $BELONGS_TO->collects_table_h_byname($table_name)
|
||||||
|
or die("Can't find table $table_name in data model ".$BELONGS_TO->name);
|
||||||
|
my @columns = $columns ? split(';',$columns) : $table->attribute_names;
|
||||||
|
my $html;
|
||||||
|
$table->read_form($template);
|
||||||
|
for my $hashref ( $table->select_loh(\@columns,"$column_name = $value",$order) ) {
|
||||||
|
print STDERR "\$template = $template, \$hashref = ",%$hashref,"\n" if $_DEBUG;
|
||||||
|
$html .= $table->fill_template($template,$hashref);
|
||||||
|
}
|
||||||
|
$html;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# workaround for lack of quoting in dates
|
||||||
|
# Jochen says it will be fixed in later releases of DBD::mysql
|
||||||
|
sub _quote {
|
||||||
|
my($self,$value,$type) = @_;
|
||||||
|
$type = 12 if $type == 9;
|
||||||
|
return $self->dbh->quote($value,$type);
|
||||||
|
}
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<DbFramework::DefinitionObject>, L<DbFramework::Attribute>,
|
||||||
|
L<DbFramework::DataModelObject> and L<DbFramework::DataModel>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998,1999 Paul Sharpe. England. All rights
|
||||||
|
reserved. This program is free software; you can redistribute it
|
||||||
|
and/or modify it under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
1;
|
268
lib/DbFramework/Template.pm
Normal file
268
lib/DbFramework/Template.pm
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::Template - Fill template with database values
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::Template;
|
||||||
|
$t = new DbFramework::Template($template,\@tables);
|
||||||
|
print $t->fill;
|
||||||
|
$t->default($table);
|
||||||
|
$t->template->set_text($template);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
B<DbFramework::Template> is a class for filling templates with values
|
||||||
|
from a database.
|
||||||
|
|
||||||
|
=head2 Template placeholders
|
||||||
|
|
||||||
|
The following list describes the placeholders allowed in a template.
|
||||||
|
In each case I<%values> relates to the hash passed to the fill()
|
||||||
|
method.
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item I<(:&db_value(table.column):)>
|
||||||
|
|
||||||
|
Replaced with the value from I<%values> whose key is I<table.column>.
|
||||||
|
See L<DbFramework::Persistent/table_qualified_attribute_hashref()> for
|
||||||
|
a useful method for generating a hash to fill this type of
|
||||||
|
placeholder.
|
||||||
|
|
||||||
|
=item I<(:&db_html_form_field(table.column[ value=value][ type=type]):)>
|
||||||
|
|
||||||
|
Replaced with an HTML form field appropriate for the column I<column>
|
||||||
|
in the table I<table>. I<value> is the inital value which will be
|
||||||
|
applied to the field. The type of field generated is determined by
|
||||||
|
the data type of I<column>. This can be overridden by setting
|
||||||
|
I<type>. See L<DbFramework::Attribute/as_html_form_field()> for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
=item I<(:&db_fk_html_form_field(table.fk):)>
|
||||||
|
|
||||||
|
Replaced with an HTML form field appropriate for the foreign key I<fk>
|
||||||
|
in the table I<table>. See
|
||||||
|
L<DbFramework::ForeignKey/as_html_form_field()> for more details.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 SUPERCLASSES
|
||||||
|
|
||||||
|
B<DbFramework::Util>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::Template;
|
||||||
|
use strict;
|
||||||
|
use base 'DbFramework::Util';
|
||||||
|
use Text::FillIn;
|
||||||
|
use Alias;
|
||||||
|
use vars qw($_DEBUG $TEMPLATE %TABLE_H %VALUES);
|
||||||
|
|
||||||
|
# set delimiters
|
||||||
|
Text::FillIn->Ldelim('(:');
|
||||||
|
Text::FillIn->Rdelim(':)');
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
TEMPLATE => {},
|
||||||
|
TABLE_H => {},
|
||||||
|
VALUES => {},
|
||||||
|
);
|
||||||
|
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
## CLASS METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 CLASS METHODS
|
||||||
|
|
||||||
|
=head2 new($template,\@tables)
|
||||||
|
|
||||||
|
Create a new B<DbFramework::Template> object. I<$template> is the
|
||||||
|
template to be filled. I<@tables> are the B<DbFramework::Table>
|
||||||
|
objects required for filling the template.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
|
||||||
|
my $template = $self->template(new Text::FillIn());
|
||||||
|
$template->text(shift);
|
||||||
|
$template->object($self);
|
||||||
|
$template->hook('&','do_method');
|
||||||
|
|
||||||
|
my @tables;
|
||||||
|
for ( @{$_[0]} ) { push(@tables,($_->name,$_)) }
|
||||||
|
$self->table_h(\@tables);
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
##----------------------------------------------------------------------------
|
||||||
|
## OBJECT METHODS
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 OBJECT METHODS
|
||||||
|
|
||||||
|
=head2 template()
|
||||||
|
|
||||||
|
Returns the B<Text::FillIn> object associated with the template.
|
||||||
|
|
||||||
|
=head2 fill(\%values)
|
||||||
|
|
||||||
|
Returns a filled template. The values in I<%values> are used to fill
|
||||||
|
certain placeholders in the template (see L<"Template placeholders">.)
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub fill {
|
||||||
|
my $self = attr shift;
|
||||||
|
%VALUES = $_[0] ? %{$_[0]} : ();
|
||||||
|
$TEMPLATE->interpret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub do_method {
|
||||||
|
my $self = shift;
|
||||||
|
my($method,$arg) = $_[0] =~ /(\w+)\((.*)\)/ or die ("Bad slot: $_[0]");
|
||||||
|
#no strict('refs');
|
||||||
|
return $self->$method($arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub db_value {
|
||||||
|
my($self,$arg) = (attr shift,shift);
|
||||||
|
# (:&db_value(table.column):)
|
||||||
|
$arg =~ /(\w+\.\w+)/ && return $VALUES{$1};
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub db_html_form_field {
|
||||||
|
my($self,$arg) = (attr shift,shift);
|
||||||
|
my $html;
|
||||||
|
|
||||||
|
# (:&db_html_form_field(table.column[ value=value][ type=type]):)
|
||||||
|
if ( $arg =~ /^((\w+)\.(\w+))(,(.*?))?(,(.*?))?$/i ) {
|
||||||
|
my $table = $TABLE_H{$2} or die "Can't find table in $arg";
|
||||||
|
my $value = $5 ? $5 : $VALUES{$1};
|
||||||
|
my $type = $7 ? $7 : undef;
|
||||||
|
my $attribute_name = $3;
|
||||||
|
my($attribute) = $table->get_attributes($attribute_name);
|
||||||
|
|
||||||
|
print STDERR "\$arg = $arg, \$value = $value, \$type = $type, \$attribute_name = $attribute_name, \$attribute = $attribute" if $_DEBUG;
|
||||||
|
|
||||||
|
$html = $attribute->as_html_form_field($value,$type);
|
||||||
|
}
|
||||||
|
$html;
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub db_fk_html_form_field {
|
||||||
|
my($self,$arg) = (attr shift,shift);
|
||||||
|
|
||||||
|
# (:&db_fk_html_form_field(table.fk):)
|
||||||
|
if ( my($t_name,$fk_name) = $arg =~ /^(\w+)\.(\w+)$/ ) {
|
||||||
|
my $table = $TABLE_H{$t_name} or die "Can't find table in $arg";
|
||||||
|
my($fk) = $table->has_foreign_keys_h_byname($fk_name)
|
||||||
|
or die "Can't find foreign key $2 in table " . $table->name;
|
||||||
|
|
||||||
|
print STDERR "\$fk_name = $fk_name, \$fk = $fk\n" if $_DEBUG;
|
||||||
|
|
||||||
|
$fk->as_html_form_field(\%VALUES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sub db_pk_html_hidden {
|
||||||
|
my($self,$arg) = (attr shift,shift);
|
||||||
|
|
||||||
|
# (:&db_pk_html_hidden(table):)
|
||||||
|
if ( my($t_name) = $arg =~ /^(\w+)$/ ) {
|
||||||
|
my $table = $TABLE_H{$t_name} or die "Can't find table in $arg";
|
||||||
|
$table->is_identified_by->as_hidden_html(\%VALUES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 default($table)
|
||||||
|
|
||||||
|
I<$table> is a B<DbFramework::Table> object. Sets up a default
|
||||||
|
template consisting of all fields in I<$table>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub default {
|
||||||
|
my($self,$table) = (attr shift,shift);
|
||||||
|
|
||||||
|
$table = $TABLE_H{$table} or die "Can't find table '$table'";
|
||||||
|
my $t_name = $table->name;
|
||||||
|
my($l,$r) = ($TEMPLATE->Ldelim,$TEMPLATE->Rdelim);
|
||||||
|
my $t;
|
||||||
|
|
||||||
|
# primary key
|
||||||
|
for ( @{$table->is_identified_by->incorporates_l} ) {
|
||||||
|
unless ( $table->in_foreign_key($_) ) {
|
||||||
|
my $a_name = $_->name;
|
||||||
|
$t .= qq{<TD>${l}&db_html_form_field(${t_name}.${a_name})${r}</TD>};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ordinary attributes
|
||||||
|
for ( $table->non_key_attributes ) {
|
||||||
|
my $a_name = $_->name;
|
||||||
|
$t .= qq{<TD>${l}&db_html_form_field(${t_name}.${a_name})${r}</TD>};
|
||||||
|
}
|
||||||
|
|
||||||
|
# keys
|
||||||
|
my(%key_attributes,@fk_attributes,@key_attributes);
|
||||||
|
for ( @{$table->has_foreign_keys_l} ) {
|
||||||
|
push(@fk_attributes,$_->attribute_names)
|
||||||
|
}
|
||||||
|
@key_attributes = (@fk_attributes,$table->is_identified_by->attribute_names);
|
||||||
|
|
||||||
|
for my $key ( @{$table->is_accessed_using_l} ) {
|
||||||
|
# get unique hash of key attributes not in primary or foreign keys
|
||||||
|
for ( @{$key->incorporates_l} ) {
|
||||||
|
my $name = $_->name;
|
||||||
|
$key_attributes{$name} = $_ unless grep(/^$name$/,@key_attributes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for ( keys(%key_attributes) ) {
|
||||||
|
$t .= qq{<TD>${l}&db_html_form_field(${t_name}.$_)${r}</TD>};
|
||||||
|
}
|
||||||
|
|
||||||
|
# foreign keys
|
||||||
|
for ( @{$table->has_foreign_keys_l} ) {
|
||||||
|
my $fk_name = $_->name;
|
||||||
|
$t .= qq{<TD>${l}&db_fk_html_form_field(${t_name}.${fk_name})${r}</TD>};
|
||||||
|
}
|
||||||
|
|
||||||
|
$TEMPLATE->text($t);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 SEE ALSO
|
||||||
|
|
||||||
|
L<Text::FillIn> and L<DbFramework::Util>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1999 Paul Sharpe. England. All rights reserved. This
|
||||||
|
program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
219
lib/DbFramework/Util.pm
Normal file
219
lib/DbFramework/Util.pm
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
DbFramework::Util - DbFramework utility functions
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
use DbFramework::Util;
|
||||||
|
($user,$password) = DbFramework::Util::get_auth();
|
||||||
|
$dbh = DbFramework::Util::get_dbh($dsn,$user,$password);
|
||||||
|
$sth = DbFramework::Util::do_sql($dbh,$sql);
|
||||||
|
($user,$password) = DbFramework::Util::get_auth();
|
||||||
|
|
||||||
|
$object->debug($n);
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
I<DbFramework::Util> contains miscellaneous utility functions and acts
|
||||||
|
as a base class for many other B<DbFramework> classes.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
package DbFramework::Util;
|
||||||
|
use strict;
|
||||||
|
use vars qw($AUTOLOAD);
|
||||||
|
use IO::File;
|
||||||
|
use DBI 1.06;
|
||||||
|
use Carp;
|
||||||
|
use Term::ReadKey;
|
||||||
|
|
||||||
|
## CLASS DATA
|
||||||
|
|
||||||
|
my $Debugging = 0;
|
||||||
|
|
||||||
|
=head1 BASE CLASS METHODS
|
||||||
|
|
||||||
|
=head2 AUTOLOAD()
|
||||||
|
|
||||||
|
AUTOLOAD() provides default accessor methods (apart from DESTROY())
|
||||||
|
for any of its subclasses. For AUTOLOAD() to catch calls to these
|
||||||
|
methods objects must be implemented as an anonymous hash. Object
|
||||||
|
attributes must
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item *) have UPPER CASE names
|
||||||
|
|
||||||
|
=item *) have keys in attribute _PERMITTED (an anonymous hash)
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The name accessor method is the name of the attribute in B<lower
|
||||||
|
case>. The 'set' versions of these accessor methods require a single
|
||||||
|
scalar argument (which could of course be a reference.) Both 'set'
|
||||||
|
and 'get' versions return the attribute's value.
|
||||||
|
|
||||||
|
B<Special Attributes>
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item B</_L$/>
|
||||||
|
|
||||||
|
Attribute names matching the pattern B</_L$/> will be treated as
|
||||||
|
arrayrefs. These accessors require an arrayref as an argument. If
|
||||||
|
the attribute is defined they return the arrayref, otherwise they
|
||||||
|
return an empty arrayref.
|
||||||
|
|
||||||
|
A method B<*_l_add(@foo)> can be called on this type of attribute to
|
||||||
|
add the elements in I<@foo> to the array. If the attribute is defined
|
||||||
|
they return the arrayref, otherwise they return an empty arrayref.
|
||||||
|
|
||||||
|
=item B</_H$/>
|
||||||
|
|
||||||
|
Attribute names matching the pattern B</_H$/> will be treated as
|
||||||
|
hashrefs. These accessors require a reference to an array containing
|
||||||
|
key/value pairs. If the attribute is defined they return the hashref,
|
||||||
|
otherwise they return an empty hashref.
|
||||||
|
|
||||||
|
A method B<*_h_byname(@list)> can be called on this type of attribute.
|
||||||
|
These methods will return a list which is the hash slice of the B<_H>
|
||||||
|
attribute value over I<@list> or an empty list if the attribute is
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
A method B<*_h_add(\%foo)> can be called on this type of attribute to
|
||||||
|
add the elements in I<%foo> to the hash. If the attribute is defined
|
||||||
|
they return the hashref, otherwise they return an empty hashref.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub AUTOLOAD {
|
||||||
|
my $self = shift;
|
||||||
|
my $type = ref($self) or die "$self is not an object";
|
||||||
|
warn "AUTOLOAD($AUTOLOAD)" if $self->{_DEBUG} || $Debugging;
|
||||||
|
|
||||||
|
my $method = $AUTOLOAD;
|
||||||
|
$method =~ s/.*://; # strip fully-qualified portion
|
||||||
|
|
||||||
|
# accessor methods
|
||||||
|
$method = uc($method);
|
||||||
|
return if ( $method eq 'DESTROY' ); # don't catch 'DESTROY'
|
||||||
|
my $name = $method;
|
||||||
|
$name =~ s/_H_BYNAME|_H_ADD$/_H/;
|
||||||
|
$name =~ s/_L_ADD$/_L/;
|
||||||
|
unless ( exists $self->{_PERMITTED}->{$name} ) {
|
||||||
|
die "Can't access `$name' field in class $type";
|
||||||
|
}
|
||||||
|
|
||||||
|
print STDERR "\$_[0] = ",defined($_[0]) ? $_[0] : 'undef',"\n"
|
||||||
|
if $self->{_DEBUG};
|
||||||
|
|
||||||
|
if ( $method =~ /_L$/ ) { # set/get array
|
||||||
|
@{$self->{$name}} = @{$_[0]} if $_[0];
|
||||||
|
return defined($self->{$name}) ? $self->{$name} : [];
|
||||||
|
} elsif ( $method =~ /_L_ADD$/ ) { # add to array
|
||||||
|
print STDERR "\@_ = @_\n" if $self->{_DEBUG};
|
||||||
|
push(@{$self->{$name}},@_);
|
||||||
|
return defined($self->{$name}) ? $self->{$name} : [];
|
||||||
|
} elsif ( $method =~ /_H$/ ) { # set/get hash
|
||||||
|
%{$self->{$name}} = @{$_[0]} if $_[0];
|
||||||
|
return defined($self->{$name}) ? $self->{$name} : {};
|
||||||
|
} elsif ( $method =~ /_H_ADD$/ ) { # add to hash
|
||||||
|
while ( my($k,$v) = each(%{$_[0]}) ) { $self->{$name}->{$k} = $v }
|
||||||
|
return defined($self->{$name}) ? $self->{$name} : {};
|
||||||
|
} elsif ( $method =~ /_H_BYNAME$/ ) { # get hash values by name
|
||||||
|
print STDERR "$self $name byname: @_\n" if $self->{_DEBUG};
|
||||||
|
return defined($self->{$name}) ? @{$self->{$name}}{@_} : ();
|
||||||
|
}
|
||||||
|
else { # set/get scalar
|
||||||
|
return @_ ? $self->{$name} = shift : $self->{$name};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 debug($n)
|
||||||
|
|
||||||
|
As a class method sets the class attribute I<$Debugging> to I<$n>. As
|
||||||
|
an object method sets the object attribute I<$_DEBUG> to I<$n>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub debug {
|
||||||
|
my $self = shift;
|
||||||
|
confess "usage: thing->debug(level)" unless @_ == 1;
|
||||||
|
my $level = shift;
|
||||||
|
if (ref($self)) {
|
||||||
|
$self->{"_DEBUG"} = $level; # just myself
|
||||||
|
} else {
|
||||||
|
$Debugging = $level; # whole class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head1 UTILITY FUNCTIONS
|
||||||
|
|
||||||
|
=head2 get_auth()
|
||||||
|
|
||||||
|
Read (I<$user>,I<$password>) from standard input with no echo when
|
||||||
|
entering password.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_auth {
|
||||||
|
print "Username: ";
|
||||||
|
chop(my $user = <STDIN>);
|
||||||
|
print "Password: ";
|
||||||
|
ReadMode 2;
|
||||||
|
chop(my $password = <STDIN>);
|
||||||
|
print "\n";
|
||||||
|
ReadMode 0;
|
||||||
|
return($user,$password);
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 get_dbh($dsn,$user,$password)
|
||||||
|
|
||||||
|
Returns a database handle for the data source name I<$dsn> by
|
||||||
|
connecting using I<$user> and I<$password>.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get_dbh {
|
||||||
|
my($dsn,$user,$password) = @_;
|
||||||
|
return DBI->connect($dsn,$user,$password) || die("$dsn $DBI::errstr");
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
=head2 do_sql($dbh,$sql)
|
||||||
|
|
||||||
|
Executes I<$sql> on I<$dbh> and returns a statement handle. This
|
||||||
|
method will die with I<$h-E<gt>errstr> if prepare() or execute() fails.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub do_sql {
|
||||||
|
my($dbh,$sql) = @_;
|
||||||
|
#print "$sql\n";
|
||||||
|
my $sth = $dbh->prepare($sql) || die($dbh->errstr);
|
||||||
|
my $rv = $sth->execute || die($sth->errstr);
|
||||||
|
return $sth;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Paul Sharpe E<lt>paul@miraclefish.comE<gt>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 1997,1998 Paul Sharpe. England. All rights reserved.
|
||||||
|
This program is free software; you can redistribute it and/or modify it
|
||||||
|
under the same terms as Perl itself.
|
||||||
|
|
||||||
|
=cut
|
38
t/10base.t
Executable file
38
t/10base.t
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
# Before `make install' is performed this script should be runnable with
|
||||||
|
# `make test'. After `make install' it should work as `perl test.pl'
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use Test;
|
||||||
|
use DBI 1.06;
|
||||||
|
use DbFramework::Util;
|
||||||
|
use DbFramework::Catalog;
|
||||||
|
use t::Config;
|
||||||
|
require "t/util.pl";
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
plan tests => 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
package Foo;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Util);
|
||||||
|
|
||||||
|
my %fields = (
|
||||||
|
NAME => undef,
|
||||||
|
CONTAINS_H => undef,
|
||||||
|
);
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $proto = shift;
|
||||||
|
my $class = ref($proto) || $proto;
|
||||||
|
my $self = bless { _PERMITTED => \%fields, %fields, }, $class;
|
||||||
|
$self->name(shift);
|
||||||
|
$self->contains_h(shift);
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
package main;
|
||||||
|
my $foo = new Foo('foo',['foo','oof','bar','rab','baz','zab']);
|
||||||
|
my @names = $foo->contains_h_byname('foo','bar');
|
||||||
|
ok("@names",'oof rab');
|
||||||
|
|
156
t/15catalog.t
Executable file
156
t/15catalog.t
Executable file
@ -0,0 +1,156 @@
|
|||||||
|
# Before `make install' is performed this script should be runnable with
|
||||||
|
# `make test'. After `make install' it should work as `perl test.pl'
|
||||||
|
use strict;
|
||||||
|
use Test;
|
||||||
|
use t::Config;
|
||||||
|
|
||||||
|
BEGIN { plan tests => scalar(@t::Config::drivers) * 5 }
|
||||||
|
|
||||||
|
require 't/util.pl';
|
||||||
|
use DbFramework::Catalog;
|
||||||
|
use DbFramework::Util;
|
||||||
|
use DbFramework::DataModel;
|
||||||
|
use DbFramework::Table;
|
||||||
|
|
||||||
|
my($t1,$t2);
|
||||||
|
|
||||||
|
for my $driver ( @t::Config::drivers ) {
|
||||||
|
if ( $driver eq 'mSQL' ) {
|
||||||
|
$t1 = qq{CREATE TABLE foo (foo integer not null,
|
||||||
|
bar char(10) not null,
|
||||||
|
baz char(10),
|
||||||
|
quux integer,
|
||||||
|
foobar text(10)
|
||||||
|
)};
|
||||||
|
$t2 = qq{CREATE TABLE bar (foo integer not null,
|
||||||
|
# foreign key (foo)
|
||||||
|
foo_foo integer,
|
||||||
|
foo_bar char(10),
|
||||||
|
bar integer
|
||||||
|
)};
|
||||||
|
} elsif ( $driver eq 'mysql' ) {
|
||||||
|
$t1 = qq{CREATE TABLE foo (foo integer not null auto_increment,
|
||||||
|
bar varchar(10) not null,
|
||||||
|
baz varchar(10) not null,
|
||||||
|
quux integer not null,
|
||||||
|
foobar text,
|
||||||
|
KEY foo(bar,baz),
|
||||||
|
KEY bar(baz,quux),
|
||||||
|
PRIMARY KEY (foo,bar)
|
||||||
|
)};
|
||||||
|
$t2 = qq{CREATE TABLE bar (foo integer not null auto_increment,
|
||||||
|
# foreign key (foo)
|
||||||
|
foo_foo integer not null,
|
||||||
|
foo_bar varchar(10) not null,
|
||||||
|
bar integer,
|
||||||
|
KEY f_foo(foo_foo,foo_bar),
|
||||||
|
PRIMARY KEY (foo)
|
||||||
|
)};
|
||||||
|
} elsif ( $driver eq 'Pg' ) {
|
||||||
|
$t1 = qq{CREATE TABLE foo (foo integer not null,
|
||||||
|
bar varchar(10) not null,
|
||||||
|
baz varchar(10) not null,
|
||||||
|
quux integer not null,
|
||||||
|
foobar text,
|
||||||
|
UNIQUE(bar,baz),
|
||||||
|
UNIQUE(baz,quux),
|
||||||
|
PRIMARY KEY (foo,bar)
|
||||||
|
)};
|
||||||
|
$t2 = qq{CREATE TABLE bar (foo integer not null,
|
||||||
|
-- foreign key (foo)
|
||||||
|
foo_foo integer not null,
|
||||||
|
foo_bar varchar(10) not null,
|
||||||
|
bar integer,
|
||||||
|
UNIQUE(foo_foo,foo_bar),
|
||||||
|
PRIMARY KEY (foo)
|
||||||
|
)};
|
||||||
|
} elsif ( $driver eq 'Sybase' ) {
|
||||||
|
$t1 = qq{CREATE TABLE foo (foo numeric(10,0) identity not null,
|
||||||
|
bar varchar(10) not null,
|
||||||
|
baz varchar(10) not null,
|
||||||
|
quux integer not null,
|
||||||
|
foobar text,
|
||||||
|
UNIQUE (bar,baz),
|
||||||
|
UNIQUE (baz,quux),
|
||||||
|
PRIMARY KEY (foo,bar)
|
||||||
|
)};
|
||||||
|
$t2 = qq{CREATE TABLE bar (foo numeric(10,0) identity not null,
|
||||||
|
-- foreign key (foo)
|
||||||
|
foo_foo integer not null,
|
||||||
|
foo_bar varchar(10) not null,
|
||||||
|
bar integer,
|
||||||
|
UNIQUE (foo_foo,foo_bar),
|
||||||
|
PRIMARY KEY (foo)
|
||||||
|
)};
|
||||||
|
} elsif ( $driver eq 'CSV' ) {
|
||||||
|
$t1 = qq{CREATE TABLE foo (foo integer,
|
||||||
|
bar varchar(10),
|
||||||
|
baz varchar(10),
|
||||||
|
quux integer,
|
||||||
|
foobar varchar(255)
|
||||||
|
)};
|
||||||
|
$t2 = qq{CREATE TABLE bar (foo integer,
|
||||||
|
foo_foo integer,
|
||||||
|
foo_bar varchar(10),
|
||||||
|
bar integer
|
||||||
|
)};
|
||||||
|
} else { # ODBC syntax for auto increment is IDENTITY(seed,increment)
|
||||||
|
$t1 = qq{CREATE TABLE foo (foo integer not null identity(0,1),
|
||||||
|
bar varchar(10) not null,
|
||||||
|
baz varchar(10) not null,
|
||||||
|
quux integer not null,
|
||||||
|
foobar text,
|
||||||
|
KEY foo(bar,baz),
|
||||||
|
KEY bar(baz,quux),
|
||||||
|
PRIMARY KEY (foo,bar)
|
||||||
|
)};
|
||||||
|
$t2 = qq{CREATE TABLE bar (foo integer not null identity(0,1),
|
||||||
|
# foreign key (foo)
|
||||||
|
foo_foo integer not null,
|
||||||
|
foo_bar varchar(10) not null,
|
||||||
|
bar integer,
|
||||||
|
KEY f_foo(foo_foo,foo_bar),
|
||||||
|
PRIMARY KEY (foo)
|
||||||
|
)};
|
||||||
|
}
|
||||||
|
foo($driver,'foo',$t1,'bar',$t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub foo($$$$$) {
|
||||||
|
my($driver,$t1,$t1_sql,$t2,$t2_sql) = @_;
|
||||||
|
|
||||||
|
my($catalog_db,$c_dsn,$c_u,$c_p) = connect_args($driver,'catalog');
|
||||||
|
my($test_db,$dsn,$u,$p) = connect_args($driver,'test');
|
||||||
|
|
||||||
|
my $c = new DbFramework::Catalog($c_dsn,$c_u,$c_p);
|
||||||
|
ok(1);
|
||||||
|
|
||||||
|
my $dbh = DbFramework::Util::get_dbh($dsn,$u,$p);
|
||||||
|
$dbh->{PrintError} = 0; # don't warn about dropping non-existent tables
|
||||||
|
drop_create($test_db,$t1,undef,$t1_sql,$dbh);
|
||||||
|
drop_create($test_db,$t2,undef,$t2_sql,$dbh);
|
||||||
|
my $dm = new DbFramework::DataModel($test_db,$dsn,$u,$p);
|
||||||
|
$dm->init_db_metadata($c_dsn,$c_u,$c_p);
|
||||||
|
|
||||||
|
# test primary keys
|
||||||
|
my $foo_table = $dm->collects_table_h_byname('foo');
|
||||||
|
ok($foo_table->is_identified_by->as_sql,'PRIMARY KEY (foo,bar)');
|
||||||
|
|
||||||
|
# test keys
|
||||||
|
my @keys = @{$foo_table->is_accessed_using_l};
|
||||||
|
my($bar,$foo);
|
||||||
|
if ( $driver eq 'mSQL' ) {
|
||||||
|
($bar,$foo) = (1,0);
|
||||||
|
} else {
|
||||||
|
($bar,$foo) = (0,1);
|
||||||
|
}
|
||||||
|
ok($keys[$bar]->as_sql,'KEY bar (baz,quux)');
|
||||||
|
ok($keys[$foo]->as_sql,'KEY foo (bar,baz)');
|
||||||
|
|
||||||
|
# test foreign keys
|
||||||
|
my $bar_table = $dm->collects_table_h_byname('bar');
|
||||||
|
my $fk = $bar_table->has_foreign_keys_h_byname('f_foo');
|
||||||
|
ok($fk->as_sql,'KEY f_foo (foo_foo,foo_bar)');
|
||||||
|
|
||||||
|
$dbh->disconnect;
|
||||||
|
}
|
70
t/17datatype.t
Executable file
70
t/17datatype.t
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
# Before `make install' is performed this script should be runnable with
|
||||||
|
# `make test'. After `make install' it should work as `perl test.pl'
|
||||||
|
use strict;
|
||||||
|
use Test;
|
||||||
|
use t::Config;
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
my $tests;
|
||||||
|
my %tests = ( 'mSQL' => 1, 'mysql' => 3, 'Pg' => 0 );
|
||||||
|
for ( @t::Config::drivers ) { $tests += $tests{$_}; }
|
||||||
|
plan tests => $tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
require 't/util.pl';
|
||||||
|
use DbFramework::DataType::ANSII;
|
||||||
|
use DbFramework::DataType::Mysql;
|
||||||
|
use DbFramework::Util;
|
||||||
|
use DbFramework::DataModel;
|
||||||
|
|
||||||
|
for ( @t::Config::drivers ) { foo($_) }
|
||||||
|
|
||||||
|
sub foo($) {
|
||||||
|
my $driver = shift;
|
||||||
|
|
||||||
|
my($catalog_db,$c_dsn,$c_u,$c_p) = connect_args($driver,'catalog');
|
||||||
|
my($test_db,$dsn,$u,$p) = connect_args($driver,'test');
|
||||||
|
my $dm = new DbFramework::DataModel($test_db,$dsn,$u,$p);
|
||||||
|
$dm->init_db_metadata($c_dsn,$c_u,$c_p);
|
||||||
|
my $dbh = $dm->dbh; $dbh->{PrintError} = 0;
|
||||||
|
my $t = $dm->collects_table_h_byname('foo');
|
||||||
|
|
||||||
|
my $dt;
|
||||||
|
|
||||||
|
if ( $driver eq 'mSQL' ) {
|
||||||
|
# mapping of mSQL => ANSII types
|
||||||
|
ok($t->as_sql,'CREATE TABLE foo (
|
||||||
|
foo INT(4) NOT NULL,
|
||||||
|
bar CHAR(10) NOT NULL,
|
||||||
|
baz CHAR(10),
|
||||||
|
quux INT(4),
|
||||||
|
foobar TEXT(10),
|
||||||
|
PRIMARY KEY (foo,bar),
|
||||||
|
KEY foo (bar,baz),
|
||||||
|
KEY bar (baz,quux)
|
||||||
|
)');
|
||||||
|
} elsif ( $driver eq 'mysql' ) {
|
||||||
|
# mapping of Mysql => ANSII types
|
||||||
|
ok($t->as_sql,'CREATE TABLE foo (
|
||||||
|
foo INTEGER UNSIGNED(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
bar VARCHAR(10) NOT NULL,
|
||||||
|
baz VARCHAR(10) NOT NULL,
|
||||||
|
quux INTEGER UNSIGNED(11) NOT NULL,
|
||||||
|
foobar TEXT(65535),
|
||||||
|
PRIMARY KEY (foo,bar),
|
||||||
|
KEY bar (baz,quux),
|
||||||
|
KEY foo (bar,baz)
|
||||||
|
)');
|
||||||
|
|
||||||
|
# valid Mysql type
|
||||||
|
my $mdt = new DbFramework::DataType::Mysql($dm,253,12,50);
|
||||||
|
ok($mdt->name,'VARCHAR');
|
||||||
|
|
||||||
|
# invalid Mysql type
|
||||||
|
$mdt = eval { new DbFramework::DataType::Mysql($dm,69,12,undef) };
|
||||||
|
ok($@,'Invalid Mysql data type: 69
|
||||||
|
');
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbh->disconnect;
|
||||||
|
}
|
171
t/20table.t
Executable file
171
t/20table.t
Executable file
@ -0,0 +1,171 @@
|
|||||||
|
# Before `make install' is performed this script should be runnable with
|
||||||
|
# `make test'. After `make install' it should work as `perl test.pl'
|
||||||
|
use strict;
|
||||||
|
use Test;
|
||||||
|
use t::Config;
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
my $tests;
|
||||||
|
for ( @t::Config::drivers ) {
|
||||||
|
$tests += ($_ ne 'mysql') ? 27 : 28;
|
||||||
|
}
|
||||||
|
plan tests => $tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
require 't/util.pl';
|
||||||
|
use DbFramework::Attribute;
|
||||||
|
use DbFramework::Table;
|
||||||
|
use DbFramework::Util;
|
||||||
|
use DbFramework::DataModel;
|
||||||
|
use DbFramework::Catalog;
|
||||||
|
|
||||||
|
for ( @t::Config::drivers ) { foo($_) }
|
||||||
|
|
||||||
|
sub foo($) {
|
||||||
|
my $driver = shift;
|
||||||
|
|
||||||
|
my($catalog_db,$c_dsn,$c_u,$c_p) = connect_args($driver,'catalog');
|
||||||
|
my($test_db,$dsn,$u,$p) = connect_args($driver,'test');
|
||||||
|
my $dm = new DbFramework::DataModel($test_db,$dsn,$u,$p);
|
||||||
|
my $dbh = $dm->dbh; $dbh->{PrintError} = 0;
|
||||||
|
|
||||||
|
my $c = new DbFramework::Catalog($c_dsn,$c_u,$c_p);
|
||||||
|
$dm->init_db_metadata($c_dsn,$c_u,$c_p);
|
||||||
|
my $foo_table = $dm->collects_table_h_byname('foo');
|
||||||
|
|
||||||
|
# as_string()
|
||||||
|
my $ok_string;
|
||||||
|
if ( $driver eq 'mSQL' ) { # doesn't support auto_increment
|
||||||
|
$ok_string = <<EOF;
|
||||||
|
Table: foo
|
||||||
|
foo(INT (4) NOT NULL)
|
||||||
|
bar(CHAR (10) NOT NULL)
|
||||||
|
baz(CHAR (10))
|
||||||
|
quux(INT (4))
|
||||||
|
foobar(TEXT (10))
|
||||||
|
EOF
|
||||||
|
} elsif ( $driver eq 'mysql' ) {
|
||||||
|
$ok_string = <<EOF;
|
||||||
|
Table: foo
|
||||||
|
foo(INTEGER UNSIGNED (11) NOT NULL AUTO_INCREMENT)
|
||||||
|
bar(VARCHAR (10) NOT NULL)
|
||||||
|
baz(VARCHAR (10) NOT NULL)
|
||||||
|
quux(INTEGER UNSIGNED (11) NOT NULL)
|
||||||
|
foobar(TEXT (65535))
|
||||||
|
EOF
|
||||||
|
} elsif ( $driver eq 'Pg' ) {
|
||||||
|
$ok_string = <<EOF;
|
||||||
|
Table: foo
|
||||||
|
foo(INT4)
|
||||||
|
bar(VARCHAR)
|
||||||
|
baz(VARCHAR)
|
||||||
|
quux(INT4)
|
||||||
|
foobar(TEXT)
|
||||||
|
EOF
|
||||||
|
} else {
|
||||||
|
$ok_string = <<EOF;
|
||||||
|
Table: foo
|
||||||
|
foo(INTEGER UNSIGNED (11) NOT NULL IDENTITY(0,1))
|
||||||
|
bar(VARCHAR (10) NOT NULL)
|
||||||
|
baz(VARCHAR (10) NOT NULL)
|
||||||
|
quux(INTEGER UNSIGNED (11) NOT NULL)
|
||||||
|
foobar(TEXT (65535))
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
ok($foo_table->as_string,$ok_string);
|
||||||
|
|
||||||
|
# as_html_form()
|
||||||
|
$ok_string = <<EOF;
|
||||||
|
<tr><td><INPUT NAME="foo" VALUE="" SIZE=10 TYPE="text"></td></tr>
|
||||||
|
<tr><td><INPUT NAME="bar" VALUE='' SIZE=30 TYPE="text" MAXLENGTH=10></td></tr>
|
||||||
|
<tr><td><INPUT NAME="baz" VALUE='' SIZE=30 TYPE="text" MAXLENGTH=10></td></tr>
|
||||||
|
<tr><td><INPUT NAME="quux" VALUE="" SIZE=10 TYPE="text"></td></tr>
|
||||||
|
<tr><td><TEXTAREA COLS=60 NAME="foobar" ROWS=4></TEXTAREA></td></tr>
|
||||||
|
EOF
|
||||||
|
ok($foo_table->as_html_form,$ok_string);
|
||||||
|
|
||||||
|
# delete()
|
||||||
|
$foo_table->delete;
|
||||||
|
ok(1);
|
||||||
|
|
||||||
|
# insert()
|
||||||
|
my(@rows,$pk);
|
||||||
|
my $i = 0;
|
||||||
|
for ('foo','bar','baz','quux') {
|
||||||
|
push(@rows,{ foo => 0, bar => $_, baz => 'foo', quux => $i });
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
for ( @rows ) { $pk = $foo_table->insert($_) }
|
||||||
|
|
||||||
|
if ( $driver =~ /(mSQL|Pg)/ ) { # no auto_increment
|
||||||
|
ok(1);
|
||||||
|
} else {
|
||||||
|
ok($pk,$#rows + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
# select()
|
||||||
|
my @lol = $foo_table->select(['foo']);
|
||||||
|
ok(@lol,4);
|
||||||
|
|
||||||
|
if ( $driver eq 'mysql' ) {
|
||||||
|
# apply a function to a column in a 'SELECT...'
|
||||||
|
my @loh = $foo_table->select_loh([q[lpad(foo,2,'0')]]);
|
||||||
|
ok($loh[0]->{q[lpad(foo,2,'0')]},'01');
|
||||||
|
}
|
||||||
|
|
||||||
|
# mSQL doesn't return # rows modified
|
||||||
|
my $rows = ( $driver eq 'mSQL' ) ? -1 : 2;
|
||||||
|
ok($foo_table->delete(q{bar LIKE 'b%'}),$rows);
|
||||||
|
|
||||||
|
# update()
|
||||||
|
my $new_bar = 'bar';
|
||||||
|
$rows = ( $driver eq 'mSQL' ) ? -1 : 1;
|
||||||
|
ok($foo_table->update({bar => $new_bar },q{bar = 'foo'}),$rows);
|
||||||
|
@lol = $foo_table->select(['bar'],undef,'bar');
|
||||||
|
ok($lol[0]->[0],$new_bar);
|
||||||
|
my @loh = $foo_table->select_loh(['bar'],undef,'bar');
|
||||||
|
ok($loh[0]->{bar},$new_bar);
|
||||||
|
|
||||||
|
# data model
|
||||||
|
ok($dm->collects_table_h_byname('foo')->name,'foo');
|
||||||
|
ok($dm->collects_table_h_byname('foo')->is_identified_by->as_sql,"PRIMARY KEY (foo,bar)");
|
||||||
|
|
||||||
|
# foreign keys
|
||||||
|
$foo_table = $dm->collects_table_h_byname('foo');
|
||||||
|
ok($foo_table->is_identified_by->incorporates->name,'f_foo');
|
||||||
|
my $bar_table = $dm->collects_table_h_byname('bar');
|
||||||
|
ok($bar_table->has_foreign_keys_h_byname('f_foo')->name,'f_foo');
|
||||||
|
|
||||||
|
my @fk = @{$bar_table->has_foreign_keys_l};
|
||||||
|
my %fk = %{$bar_table->has_foreign_keys_h};
|
||||||
|
my @keys = keys(%fk);
|
||||||
|
ok(scalar(@fk),scalar(@keys));
|
||||||
|
|
||||||
|
@fk = $dm->collects_table_h_byname('foo')->is_identified_by->incorporates;
|
||||||
|
ok(scalar(@fk),scalar(@keys));
|
||||||
|
ok($fk[0],$fk{f_foo});
|
||||||
|
|
||||||
|
# keys
|
||||||
|
ok($foo_table->in_key($foo_table->get_attributes('bar')),1);
|
||||||
|
ok($foo_table->in_key($foo_table->get_attributes('foo')),0);
|
||||||
|
|
||||||
|
# primary keys
|
||||||
|
ok($foo_table->is_identified_by->belongs_to($foo_table),$foo_table);
|
||||||
|
ok($foo_table->in_primary_key($foo_table->get_attributes('foo')),1);
|
||||||
|
ok($foo_table->in_primary_key($foo_table->get_attributes('baz')),0);
|
||||||
|
|
||||||
|
# foreign key
|
||||||
|
my @fks = @{$bar_table->has_foreign_keys_l};
|
||||||
|
ok(@fks,1);
|
||||||
|
ok($bar_table->in_foreign_key($bar_table->get_attributes('foo_foo')),1);
|
||||||
|
ok($bar_table->in_foreign_key($bar_table->get_attributes('foo')),0);
|
||||||
|
|
||||||
|
# non-keys
|
||||||
|
ok($foo_table->in_any_key($foo_table->get_attributes('foo')),1);
|
||||||
|
ok($bar_table->in_any_key($bar_table->get_attributes('bar')),0);
|
||||||
|
my @nka = $bar_table->non_key_attributes;
|
||||||
|
ok($nka[0]->name,'bar');
|
||||||
|
|
||||||
|
$dm->dbh->disconnect;
|
||||||
|
$dbh->disconnect;
|
||||||
|
}
|
82
t/30persistent.t
Executable file
82
t/30persistent.t
Executable file
@ -0,0 +1,82 @@
|
|||||||
|
# Before `make install' is performed this script should be runnable with
|
||||||
|
# `make test'. After `make install' it should work as `perl test.pl'
|
||||||
|
use strict;
|
||||||
|
use Test;
|
||||||
|
use t::Config;
|
||||||
|
|
||||||
|
BEGIN { plan tests => scalar(@t::Config::drivers) * 12 }
|
||||||
|
|
||||||
|
require 't/util.pl';
|
||||||
|
use DbFramework::Persistent;
|
||||||
|
use DbFramework::DataModel;
|
||||||
|
use DbFramework::Table;
|
||||||
|
use DbFramework::Util;
|
||||||
|
|
||||||
|
package Foo;
|
||||||
|
use base qw(DbFramework::Persistent);
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
for ( @t::Config::drivers ) { foo($_) }
|
||||||
|
|
||||||
|
sub foo($) {
|
||||||
|
my($driver) = @_;
|
||||||
|
|
||||||
|
my($catalog_db,$c_dsn,$c_u,$c_p) = connect_args($driver,'catalog');
|
||||||
|
my($test_db,$dsn,$u,$p) = connect_args($driver,'test');
|
||||||
|
|
||||||
|
my $dm = new DbFramework::DataModel($test_db,$dsn,$u,$p);
|
||||||
|
$dm->init_db_metadata($c_dsn,$c_u,$c_p);
|
||||||
|
my $dbh = $dm->dbh; $dbh->{PrintError} = 0;
|
||||||
|
my $catalog = new DbFramework::Catalog($c_dsn);
|
||||||
|
my $foo = new Foo($dm->collects_table_h_byname('foo'),$dbh,$catalog);
|
||||||
|
ok(1);
|
||||||
|
|
||||||
|
# init
|
||||||
|
$foo->table->delete;
|
||||||
|
ok(1);
|
||||||
|
|
||||||
|
# insert
|
||||||
|
$foo->attributes_h(['foo',0,'bar','bar','baz','baz','quux',0]);
|
||||||
|
my $pk = ($driver =~ /(mSQL|Pg)/ ) ? -1 : 1;
|
||||||
|
ok($foo->insert,$pk);
|
||||||
|
my %foo = ( foo => 0, bar => 'baz', baz => 'baz', quux => 1 );
|
||||||
|
$foo->attributes_h([ %foo ]);
|
||||||
|
$pk = ($driver =~ /(mSQL|Pg)/ ) ? -1 : 2;
|
||||||
|
ok($foo->insert,$pk);
|
||||||
|
my @foo = $foo->attributes_h_byname('foo','bar');
|
||||||
|
ok($foo[1],$foo{bar});
|
||||||
|
|
||||||
|
# update
|
||||||
|
$pk = ($driver =~ /(mSQL|Pg)/ ) ? 0 : 2;
|
||||||
|
$foo->attributes_h(['foo',$pk,'bar','baz','baz','quux']);
|
||||||
|
my $rows = ($driver =~ /mSQL/ ) ? -1 : 1;
|
||||||
|
ok($foo->update,$rows);
|
||||||
|
|
||||||
|
# select
|
||||||
|
$foo->attributes_h([]);
|
||||||
|
@foo = $foo->select(undef,'bar');
|
||||||
|
my @a = $foo[0]->attributes_h_byname('foo','bar');
|
||||||
|
$pk = ($driver =~ /(mSQL|Pg)/ ) ? 0 : 1;
|
||||||
|
ok("@a","$pk bar");
|
||||||
|
@a = $foo[1]->attributes_h_byname('foo','bar');
|
||||||
|
$pk = ($driver =~ /(mSQL|Pg)/ ) ? 0 : 2;
|
||||||
|
ok("@a","$pk baz");
|
||||||
|
@foo = $foo->select(q{bar like 'b%'},'foo,bar');
|
||||||
|
ok(@foo,2);
|
||||||
|
|
||||||
|
# delete
|
||||||
|
$rows = ($driver =~ /mSQL/ ) ? -1 : 1;
|
||||||
|
ok($foo[0]->delete,$rows);
|
||||||
|
|
||||||
|
# make persistent (sub)class
|
||||||
|
my($class,$table) = ('Bar','bar');
|
||||||
|
my $ok = qq{package $class;
|
||||||
|
use strict;
|
||||||
|
use base qw(DbFramework::Persistent);
|
||||||
|
};
|
||||||
|
ok(DbFramework::Persistent->make_class($class),$ok);
|
||||||
|
eval $ok;
|
||||||
|
my $bar = $class->new($dm->collects_table_h_byname($table),$dbh,$catalog);
|
||||||
|
ok($bar->table->name,$table);
|
||||||
|
}
|
54
t/40template.t
Executable file
54
t/40template.t
Executable file
@ -0,0 +1,54 @@
|
|||||||
|
# Before `make install' is performed this script should be runnable with
|
||||||
|
# `make test'. After `make install' it should work as `perl test.pl'
|
||||||
|
use strict;
|
||||||
|
use Test;
|
||||||
|
use t::Config;
|
||||||
|
require 't/util.pl';
|
||||||
|
|
||||||
|
BEGIN { plan tests => scalar(@t::Config::drivers) * 4 + 1 }
|
||||||
|
|
||||||
|
use DbFramework::Template;
|
||||||
|
ok(1);
|
||||||
|
use DbFramework::DataModel;
|
||||||
|
|
||||||
|
for ( @t::Config::drivers ) { foo($_) }
|
||||||
|
|
||||||
|
sub foo($) {
|
||||||
|
my $driver = shift;
|
||||||
|
|
||||||
|
my($catalog_db,$c_dsn,$c_u,$c_p) = connect_args($driver,'catalog');
|
||||||
|
my($test_db,$dsn,$u,$p) = connect_args($driver,'test');
|
||||||
|
|
||||||
|
my $dm = new DbFramework::DataModel($test_db,$dsn,$u,$p);
|
||||||
|
$dm->init_db_metadata($c_dsn,$c_u,$c_p);
|
||||||
|
my $dbh = $dm->dbh; $dbh->{PrintError} = 0;
|
||||||
|
|
||||||
|
my $t = new DbFramework::Template("(:&db_value(foo.bar):)",
|
||||||
|
$dm->collects_table_l);
|
||||||
|
ok(1);
|
||||||
|
|
||||||
|
my $filling = 'bar';
|
||||||
|
ok($t->fill({'foo.bar' => $filling}),$filling);
|
||||||
|
|
||||||
|
$t->template->text("(:&db_html_form_field(foo.bar,,int):)");
|
||||||
|
my $ok = '<INPUT NAME="bar" VALUE="" SIZE=10 TYPE="text">';
|
||||||
|
ok($t->fill,$ok);
|
||||||
|
|
||||||
|
$t->template->text("(:&db_fk_html_form_field(bar.f_foo):)");
|
||||||
|
if ( $driver =~ /(mSQL|Pg)/ ) {
|
||||||
|
$ok = qq{<SELECT NAME="foo_foo,foo_bar">
|
||||||
|
<OPTION VALUE="">** Any Value **
|
||||||
|
<OPTION VALUE="NULL">NULL
|
||||||
|
<OPTION VALUE="0,baz">baz
|
||||||
|
</SELECT>
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
$ok = qq{<SELECT NAME="foo_foo,foo_bar">
|
||||||
|
<OPTION VALUE="">** Any Value **
|
||||||
|
<OPTION VALUE="NULL">NULL
|
||||||
|
<OPTION VALUE="2,baz">baz
|
||||||
|
</SELECT>
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ok($t->fill,$ok);
|
||||||
|
}
|
20
t/Config.pm
Normal file
20
t/Config.pm
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package t::Config;
|
||||||
|
|
||||||
|
$test_db = 'test';
|
||||||
|
@drivers = qw/mysql Pg/;
|
||||||
|
%driver = (mysql => {
|
||||||
|
test => {
|
||||||
|
p => '',u => '',dsn => 'DBI:mysql:database=test',},
|
||||||
|
dbframework_catalog => {
|
||||||
|
p => '',u => '',dsn => 'DBI:mysql:database=dbframework_catalog',},
|
||||||
|
},
|
||||||
|
Pg => {
|
||||||
|
test => {
|
||||||
|
p => '',u => '',dsn => 'DBI:Pg:dbname=test',},
|
||||||
|
dbframework_catalog => {
|
||||||
|
p => '',u => '',dsn => 'DBI:Pg:dbname=dbframework_catalog',},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
3
t/template
Normal file
3
t/template
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<DbField foo.foo>
|
||||||
|
<DbField foo.bar value=www.motorbase.com>
|
||||||
|
<DbField foo.baz value=1 type=INT>
|
6
t/test/foo/foo.form
Normal file
6
t/test/foo/foo.form
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
(
|
||||||
|
foo => qq{<DbField foo.foo>
|
||||||
|
<DbField foo.bar value=www.motorbase.com>
|
||||||
|
<DbField foo.baz value=1 type=INT>},
|
||||||
|
bar => q{bar},
|
||||||
|
)
|
54
t/util.pl
Normal file
54
t/util.pl
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
sub drop_create {
|
||||||
|
my($db,$table,$c,$sql,$dbh) = @_;
|
||||||
|
my $rv = $dbh->do("DROP TABLE $table");
|
||||||
|
|
||||||
|
## init catalog
|
||||||
|
if ( defined $c ) {
|
||||||
|
my $c_sql = qq{
|
||||||
|
DELETE FROM c_key
|
||||||
|
WHERE db_name = '$db'
|
||||||
|
AND ( table_name = '$table' )
|
||||||
|
};
|
||||||
|
my $sth = do_sql($c->dbh,$c_sql); $sth->finish;
|
||||||
|
$c_sql = qq{
|
||||||
|
DELETE FROM c_relationship
|
||||||
|
WHERE db_name = '$db'
|
||||||
|
AND ( fk_table = '$table' )
|
||||||
|
};
|
||||||
|
$sth = do_sql($c->dbh,$c_sql); $sth->finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $dbh->do($sql) || die $dbh->errstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub do_sql {
|
||||||
|
my($dbh,$sql) = @_;
|
||||||
|
#print STDERR "$sql\n";
|
||||||
|
my $sth = $dbh->prepare($sql) || die($dbh->errstr);
|
||||||
|
my $rv = $sth->execute || die($sth->errstr);
|
||||||
|
return $sth;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub connect_args($$) {
|
||||||
|
my %driver = %t::Config::driver;
|
||||||
|
my($driver,$db) = @_;
|
||||||
|
my $catalog_db = $DbFramework::Catalog::db;
|
||||||
|
my($db_name,$dsn,$u,$p);
|
||||||
|
|
||||||
|
SWITCH: {
|
||||||
|
($db eq 'catalog') && do {
|
||||||
|
$db_name = $catalog_db;
|
||||||
|
};
|
||||||
|
($db eq 'test') && do {
|
||||||
|
delete $driver{$driver}->{$catalog_db};
|
||||||
|
($db_name) = keys %{$driver{$driver}};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$dsn = $driver{$driver}->{$db_name}->{dsn};
|
||||||
|
$u = $driver{$driver}->{$db_name}->{u};
|
||||||
|
$p = $driver{$driver}->{$db_name}->{p};
|
||||||
|
return($db_name,$dsn,$u,$p);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
Loading…
Reference in New Issue
Block a user