1215 lines
40 KiB
Perl
Executable file
1215 lines
40 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
# $Id$
|
|
|
|
# Database Updater
|
|
# This script checks for required updates to the database.
|
|
|
|
# Part of the Koha Library Software www.koha.org
|
|
# Licensed under the GPL.
|
|
|
|
# Bugs/ToDo:
|
|
# - Would also be a good idea to offer to do a backup at this time...
|
|
|
|
# NOTE: If you do something more than once in here, make it table driven.
|
|
|
|
use strict;
|
|
|
|
# CPAN modules
|
|
use DBI;
|
|
|
|
# Koha modules
|
|
use C4::Context;
|
|
|
|
# FIXME - The user might be installing a new database, so can't rely
|
|
# on /etc/koha.conf anyway.
|
|
|
|
my $debug = 0;
|
|
|
|
my (
|
|
$sth, $sti,
|
|
$query,
|
|
%existingtables, # tables already in database
|
|
%types,
|
|
$table,
|
|
$column,
|
|
$type, $null, $key, $default, $extra,
|
|
$prefitem, # preference item in systempreferences table
|
|
);
|
|
|
|
my $dbh = C4::Context->dbh;
|
|
print "connected to your DB. Checking & modifying it\n";
|
|
|
|
#-------------------
|
|
# Defines
|
|
|
|
# Tables to add if they don't exist
|
|
my %requiretables = (
|
|
shelfcontents => "( shelfnumber int not null,
|
|
itemnumber int not null,
|
|
flags int)",
|
|
bookshelf => "( shelfnumber int auto_increment primary key,
|
|
shelfname char(255))",
|
|
z3950queue => "( id int auto_increment primary key,
|
|
term text,
|
|
type char(10),
|
|
startdate int,
|
|
enddate int,
|
|
done smallint,
|
|
results longblob,
|
|
numrecords int,
|
|
servers text,
|
|
identifier char(30))",
|
|
z3950results => "( id int auto_increment primary key,
|
|
queryid int,
|
|
server char(255),
|
|
startdate int,
|
|
enddate int,
|
|
results longblob,
|
|
numrecords int,
|
|
numdownloaded int,
|
|
highestseen int,
|
|
active smallint)",
|
|
branchrelations => "( branchcode varchar(4),
|
|
categorycode varchar(4))",
|
|
websites => "( websitenumber int(11) NOT NULL auto_increment,
|
|
biblionumber int(11) NOT NULL default '0',
|
|
title text,
|
|
description text,
|
|
url varchar(255),
|
|
PRIMARY KEY (websitenumber) )",
|
|
marcrecorddone => "( isbn char(40),
|
|
issn char(40),
|
|
lccn char(40),
|
|
controlnumber char(40))",
|
|
uploadedmarc => "( id int(11) NOT NULL auto_increment PRIMARY KEY,
|
|
marc longblob,
|
|
hidden smallint(6) default NULL,
|
|
name varchar(255) default NULL)",
|
|
ethnicity => "( code varchar(10) NOT NULL default '',
|
|
name varchar(255) default NULL,
|
|
PRIMARY KEY (code) )",
|
|
sessions => "( sessionID varchar(255) NOT NULL default '',
|
|
userid varchar(255) default NULL,
|
|
ip varchar(16) default NULL,
|
|
lasttime int,
|
|
PRIMARY KEY (sessionID) )",
|
|
sessionqueries => "( sessionID varchar(255) NOT NULL default '',
|
|
userid char(100) NOT NULL default '',
|
|
ip char(18) NOT NULL default '',
|
|
url text NOT NULL default '' )",
|
|
bibliothesaurus => "( id bigint(20) NOT NULL auto_increment,
|
|
freelib char(255) NOT NULL default '',
|
|
stdlib char(255) NOT NULL default '',
|
|
category char(10) NOT NULL default '',
|
|
level tinyint(4) NOT NULL default '1',
|
|
hierarchy char(80) NOT NULL default '',
|
|
father char(80) NOT NULL default '',
|
|
PRIMARY KEY (id),
|
|
KEY freelib (freelib),
|
|
KEY stdlib (stdlib),
|
|
KEY category (category),
|
|
KEY hierarchy (hierarchy)
|
|
)",
|
|
marc_biblio => "(
|
|
bibid bigint(20) unsigned NOT NULL auto_increment,
|
|
biblionumber int(11) NOT NULL default '0',
|
|
datecreated date NOT NULL default '0000-00-00',
|
|
datemodified date default NULL,
|
|
origincode char(20) default NULL,
|
|
PRIMARY KEY (bibid),
|
|
KEY origincode (origincode),
|
|
KEY biblionumber (biblionumber)
|
|
) ",
|
|
marc_blob_subfield => "(
|
|
blobidlink bigint(20) NOT NULL auto_increment,
|
|
subfieldvalue longtext NOT NULL,
|
|
PRIMARY KEY (blobidlink)
|
|
) ",
|
|
marc_subfield_structure => "(
|
|
tagfield char(3) NOT NULL default '',
|
|
tagsubfield char(1) NOT NULL default '',
|
|
liblibrarian char(255) NOT NULL default '',
|
|
libopac char(255) NOT NULL default '',
|
|
repeatable tinyint(4) NOT NULL default '0',
|
|
mandatory tinyint(4) NOT NULL default '0',
|
|
kohafield char(40) default NULL,
|
|
tab tinyint(1) default NULL,
|
|
authorised_value char(10) default NULL,
|
|
thesaurus_category char(10) default NULL,
|
|
value_builder char(80) default NULL,
|
|
PRIMARY KEY (tagfield,tagsubfield),
|
|
KEY kohafield (kohafield),
|
|
KEY tab (tab)
|
|
)",
|
|
marc_subfield_table => "(
|
|
subfieldid bigint(20) unsigned NOT NULL auto_increment,
|
|
bibid bigint(20) unsigned NOT NULL default '0',
|
|
tag char(3) NOT NULL default '',
|
|
tagorder tinyint(4) NOT NULL default '1',
|
|
tag_indicator char(2) NOT NULL default '',
|
|
subfieldcode char(1) NOT NULL default '',
|
|
subfieldorder tinyint(4) NOT NULL default '1',
|
|
subfieldvalue varchar(255) default NULL,
|
|
valuebloblink bigint(20) default NULL,
|
|
PRIMARY KEY (subfieldid),
|
|
KEY bibid (bibid),
|
|
KEY tag (tag),
|
|
KEY tag_indicator (tag_indicator),
|
|
KEY subfieldorder (subfieldorder),
|
|
KEY subfieldcode (subfieldcode),
|
|
KEY subfieldvalue (subfieldvalue),
|
|
KEY tagorder (tagorder)
|
|
)",
|
|
marc_tag_structure => "(
|
|
tagfield char(3) NOT NULL default '',
|
|
liblibrarian char(255) NOT NULL default '',
|
|
libopac char(255) NOT NULL default '',
|
|
repeatable tinyint(4) NOT NULL default '0',
|
|
mandatory tinyint(4) NOT NULL default '0',
|
|
authorised_value char(10) default NULL,
|
|
PRIMARY KEY (tagfield)
|
|
)",
|
|
marc_word => "(
|
|
bibid bigint(20) NOT NULL default '0',
|
|
tag char(3) NOT NULL default '',
|
|
tagorder tinyint(4) NOT NULL default '1',
|
|
subfieldid char(1) NOT NULL default '',
|
|
subfieldorder tinyint(4) NOT NULL default '1',
|
|
word varchar(255) NOT NULL default '',
|
|
sndx_word varchar(255) NOT NULL default '',
|
|
KEY bibid (bibid),
|
|
KEY tag (tag),
|
|
KEY tagorder (tagorder),
|
|
KEY subfieldid (subfieldid),
|
|
KEY subfieldorder (subfieldorder),
|
|
KEY word (word),
|
|
KEY sndx_word (sndx_word)
|
|
)",
|
|
marc_breeding => "( id bigint(20) NOT NULL auto_increment,
|
|
file varchar(80) NOT NULL default '',
|
|
isbn varchar(10) NOT NULL default '',
|
|
title varchar(128) default NULL,
|
|
author varchar(80) default NULL,
|
|
marc text NOT NULL,
|
|
encoding varchar(40) default NULL,
|
|
PRIMARY KEY (id),
|
|
KEY title (title),
|
|
KEY isbn (isbn)
|
|
)",
|
|
authorised_values => "(id int(11) NOT NULL auto_increment,
|
|
category char(10) NOT NULL default '',
|
|
authorised_value char(80) NOT NULL default '',
|
|
lib char(80) NULL,
|
|
PRIMARY KEY (id),
|
|
KEY name (category)
|
|
)",
|
|
userflags => "( bit int(11) NOT NULL default '0',
|
|
flag char(30), flagdesc char(255),
|
|
defaulton int(11)
|
|
)",
|
|
);
|
|
|
|
my %requirefields = (
|
|
biblio => { 'abstract' => 'text' },
|
|
deletedbiblio => { 'abstract' => 'text', 'marc' => 'blob' },
|
|
deleteditems => { 'marc' => 'blob', 'paidfor' => 'text' },
|
|
biblioitems => {
|
|
'lccn' => 'char(25)',
|
|
'url' => 'varchar(255)',
|
|
'marc' => 'text'
|
|
},
|
|
deletedbiblioitems => {
|
|
'lccn' => 'char(25)',
|
|
'url' => 'varchar(255)',
|
|
'marc' => 'text'
|
|
},
|
|
branchtransfers => { 'datearrived' => 'datetime' },
|
|
statistics => { 'borrowernumber' => 'int(11)' },
|
|
aqbooksellers => {
|
|
'invoicedisc' => 'float(6,4)',
|
|
'nocalc' => 'int(11)'
|
|
},
|
|
borrowers => {
|
|
'userid' => 'char(30)',
|
|
'password' => 'char(30)',
|
|
'flags' => 'int(11)',
|
|
'textmessaging' => 'varchar(30)',
|
|
'zipcode' => 'varchar(25)',
|
|
'homezipcode' => 'varchar(25)',
|
|
},
|
|
aqorders => { 'budgetdate' => 'date' },
|
|
aqbudget => {'aqbudgetid' => 'tinyint(4) auto_increment primary key'},
|
|
items => {'paidfor' => 'text'},
|
|
|
|
#added so that reference items are not available for reserves...
|
|
itemtypes => { 'notforloan' => 'smallint(6)' },
|
|
systempreferences => { 'explanation' => 'char(80)',
|
|
'type' => 'char(20)',
|
|
'options' => 'text' },
|
|
z3950servers => { 'syntax' => 'char(80)' },
|
|
);
|
|
|
|
my %dropable_table = (
|
|
classification => 'classification',
|
|
multipart => 'multipart',
|
|
multivolume => 'multivolume',
|
|
newitems => 'newitems',
|
|
procedures => 'procedures',
|
|
publisher => 'publisher',
|
|
searchstats => 'searchstats',
|
|
serialissues => 'serialissues',
|
|
);
|
|
|
|
# the other hash contains other actions that can't be done elsewhere. they are done
|
|
# either BEFORE of AFTER everything else, depending on "when" entry (default => AFTER)
|
|
|
|
# The tabledata hash contains data that should be in the tables.
|
|
# The uniquefieldrequired hash entry is used to determine which (if any) fields
|
|
# must not exist in the table for this row to be inserted. If the
|
|
# uniquefieldrequired entry is already in the table, the existing data is not
|
|
# modified, unless the forceupdate hash entry is also set. Fields in the
|
|
# anonymous "forceupdate" hash will be forced to be updated to the default
|
|
# values given in the %tabledata hash.
|
|
|
|
my %tabledata = (
|
|
userflags => [
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 0,
|
|
flag => 'superlibrarian',
|
|
flagdesc => 'Access to all librarian functions',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 1,
|
|
flag => 'circulate',
|
|
flagdesc => 'Circulate books',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 2,
|
|
flag => 'catalogue',
|
|
flagdesc => 'View Catalogue (Librarian Interface)',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 3,
|
|
flag => 'parameters',
|
|
flagdesc => 'Set Koha system paramters',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 4,
|
|
flag => 'borrowers',
|
|
flagdesc => 'Add or modify borrowers',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 5,
|
|
flag => 'permissions',
|
|
flagdesc => 'Set user permissions',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 6,
|
|
flag => 'reserveforothers',
|
|
flagdesc => 'Reserve books for patrons',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 7,
|
|
flag => 'borrow',
|
|
flagdesc => 'Borrow books',
|
|
defaulton => 1
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 8,
|
|
flag => 'reserveforself',
|
|
flagdesc => 'Reserve books for self',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 9,
|
|
flag => 'editcatalogue',
|
|
flagdesc => 'Edit Catalogue (Modify bibliographic/holdings data)',
|
|
defaulton => 0
|
|
},
|
|
{
|
|
uniquefieldrequired => 'bit',
|
|
bit => 10,
|
|
flag => 'updatecharges',
|
|
flagdesc => 'Update borrower charges',
|
|
defaulton => 0
|
|
},
|
|
],
|
|
systempreferences => [
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
variable => 'LibraryName',
|
|
value => '<i><b>Koha<br/>Free Software ILS<br/><br/></b>Koha : a gift, a contribution<br/> in Maori</i>',
|
|
explanation => 'Library name as shown on main opac page',
|
|
type => ''
|
|
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
variable => 'autoMemberNum',
|
|
value => '1',
|
|
explanation => 'Member number is auto-calculated',
|
|
type => 'YesNo'
|
|
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1,
|
|
'options' => 1 },
|
|
variable => 'acquisitions',
|
|
value => 'normal',
|
|
explanation =>
|
|
'Normal, budget-based acquisitions, or Simple bibliographic-data acquisitions',
|
|
type => 'Choice',
|
|
options => 'simple|normal'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1,
|
|
'options' => 1 },
|
|
variable => 'dateformat',
|
|
value => 'metric',
|
|
explanation =>
|
|
'date format (us mm/dd/yyyy, metric dd/mm/yyy, ISO yyyy/mm/dd)',
|
|
type => 'Choice',
|
|
options => 'metric|us|iso'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'template',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => 'default',
|
|
explanation => 'Preference order for intranet interface templates',
|
|
type => 'Themes'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'autoBarcode',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => 'yes',
|
|
explanation => 'Barcode is auto-calculated',
|
|
type => 'YesNo'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'insecure',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => 'no',
|
|
explanation =>
|
|
'If YES, no auth at all is needed. Be careful if you set this to yes!',
|
|
type => 'YesNo'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'authoritysep',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1,
|
|
'options' => 1 },
|
|
value => '--',
|
|
explanation =>
|
|
'the separator used in authority/thesaurus. Usually --',
|
|
type => 'free',
|
|
options => '10'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'opaclanguages',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => 'en',
|
|
explanation => 'Set the preferred order for translations. The top language will be tried first.',
|
|
type => 'Languages'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'opacthemes',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => 'default',
|
|
explanation => 'Set the preferred order for themes. The top theme will be tried first.',
|
|
type => 'Themes'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'timeout',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => '1200',
|
|
explanation => 'Inactivity timeout for cookies authentication (in seconds)',
|
|
type => 'Integer'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'marc',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => 'yes',
|
|
explanation => 'Turn on MARC support',
|
|
type => 'YesNo'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'marcflavour',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1,
|
|
'options' => 1},
|
|
value => 'MARC21',
|
|
explanation =>
|
|
'your MARC flavor (MARC21 or UNIMARC) used for character encoding',
|
|
type => 'Choice',
|
|
options => 'MARC21|UNIMARC'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'checkdigit',
|
|
value => 'none',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1,
|
|
'options' => 1},
|
|
explanation => 'Validity checks on membership number: none or "Katipo" style checks',
|
|
type => 'Choice',
|
|
options => 'none|katipo'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'maxoutstanding',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => '5',
|
|
explanation =>
|
|
'maximum amount withstanding to be able make reserves ',
|
|
type => 'Integer'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'maxreserves',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => '5',
|
|
explanation =>
|
|
'maximum number of reserves a member can make',
|
|
type => 'Integer'
|
|
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'noissuescharge',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => '5',
|
|
explanation =>
|
|
'maximum amount withstanding to be able to check out an item',
|
|
type => 'Integer'
|
|
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'KohaAdminEmailAddress',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => 'your.mail@here',
|
|
explanation => 'the email address where borrowers modifs are sent',
|
|
type => 'free'
|
|
},
|
|
{
|
|
uniquefieldrequired => 'variable',
|
|
variable => 'gist',
|
|
forceupdate => { 'explanation' => 1,
|
|
'type' => 1 },
|
|
value => '0.125',
|
|
explanation => 'the gist rate. NOT in %, but in numeric form (0.12 for 12%)',
|
|
type => 'free'
|
|
},
|
|
],
|
|
|
|
);
|
|
|
|
my %fielddefinitions = (
|
|
printers => [
|
|
{
|
|
field => 'printername',
|
|
type => 'char(40)',
|
|
null => '',
|
|
key => 'PRI',
|
|
default => ''
|
|
},
|
|
],
|
|
aqbookfund => [
|
|
{
|
|
field => 'bookfundid',
|
|
type => 'char(5)',
|
|
null => '',
|
|
key => 'PRI',
|
|
default => ''
|
|
},
|
|
],
|
|
aqbudget => [
|
|
{
|
|
field => 'aqbudgetid',
|
|
type => 'tinyint(4)',
|
|
null => '',
|
|
key => 'PRI',
|
|
default =>'',
|
|
extra => 'auto_increment'
|
|
},
|
|
],
|
|
z3950servers => [
|
|
{
|
|
field => 'id',
|
|
type => 'int',
|
|
null => '',
|
|
key => 'PRI',
|
|
default => '',
|
|
extra => 'auto_increment'
|
|
},
|
|
],
|
|
marc_breeding => [
|
|
{
|
|
field => 'z3950random',
|
|
type => 'varchar(40)',
|
|
null => 'NULL',
|
|
key => '',
|
|
default => '',
|
|
extra => ''
|
|
},
|
|
{
|
|
field => 'encoding',
|
|
type => 'varchar(40)',
|
|
null => '',
|
|
key => '',
|
|
default => '',
|
|
extra => ''
|
|
},
|
|
],
|
|
);
|
|
|
|
#-------------------
|
|
# Initialize
|
|
|
|
# Start checking
|
|
|
|
# Get version of MySQL database engine.
|
|
my $mysqlversion = `mysqld --version`;
|
|
$mysqlversion =~ /Ver (\S*) /;
|
|
$mysqlversion = $1;
|
|
if ( $mysqlversion ge '3.23' ) {
|
|
print "Could convert to MyISAM database tables...\n";
|
|
}
|
|
|
|
#---------------------------------
|
|
# Tables
|
|
|
|
# Collect all tables into a list
|
|
$sth = $dbh->prepare("show tables");
|
|
$sth->execute;
|
|
while ( my ($table) = $sth->fetchrow ) {
|
|
$existingtables{$table} = 1;
|
|
}
|
|
|
|
|
|
# Now add any missing tables
|
|
foreach $table ( keys %requiretables ) {
|
|
unless ( $existingtables{$table} ) {
|
|
print "Adding $table table...\n";
|
|
my $sth = $dbh->prepare("create table $table $requiretables{$table}");
|
|
$sth->execute;
|
|
if ( $sth->err ) {
|
|
print "Error : $sth->errstr \n";
|
|
$sth->finish;
|
|
} # if error
|
|
} # unless exists
|
|
} # foreach
|
|
|
|
# now drop useless tables
|
|
foreach $table ( keys %dropable_table ) {
|
|
if ( $existingtables{$table} ) {
|
|
print "Dropping unused table $table\n" if $debug;
|
|
$dbh->do("drop table $table");
|
|
if ( $dbh->err ) {
|
|
print "Error : $dbh->errstr \n";
|
|
}
|
|
}
|
|
}
|
|
unless ( $existingtables{'z3950servers'} ) {
|
|
#MJR: added syntax entries to close bug 624
|
|
print "Adding z3950servers table...\n";
|
|
my $sti = $dbh->prepare( "create table z3950servers (
|
|
host char(255),
|
|
port int,
|
|
db char(255),
|
|
userid char(255),
|
|
password char(255),
|
|
name text,
|
|
id int,
|
|
checked smallint,
|
|
rank int,
|
|
syntax char(80))"
|
|
);
|
|
$sti->execute;
|
|
$sti = $dbh->prepare( "insert into z3950servers
|
|
values ('z3950.loc.gov',
|
|
7090,
|
|
'voyager',
|
|
'', '',
|
|
'Library of Congress',
|
|
1, 1, 1, 'USMARC')"
|
|
);
|
|
$sti->execute;
|
|
}
|
|
|
|
#---------------------------------
|
|
# Columns
|
|
|
|
foreach $table ( keys %requirefields ) {
|
|
print "Check table $table\n" if $debug;
|
|
$sth = $dbh->prepare("show columns from $table");
|
|
$sth->execute();
|
|
undef %types;
|
|
while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
|
{
|
|
$types{$column} = $type;
|
|
} # while
|
|
foreach $column ( keys %{ $requirefields{$table} } ) {
|
|
print " Check column $column [$types{$column}]\n" if $debug;
|
|
if ( !$types{$column} ) {
|
|
|
|
# column doesn't exist
|
|
print "Adding $column field to $table table...\n";
|
|
$query = "alter table $table
|
|
add column $column " . $requirefields{$table}->{$column};
|
|
print "Execute: $query\n" if $debug;
|
|
my $sti = $dbh->prepare($query);
|
|
$sti->execute;
|
|
if ( $sti->err ) {
|
|
print "**Error : $sti->errstr \n";
|
|
$sti->finish;
|
|
} # if error
|
|
} # if column
|
|
} # foreach column
|
|
} # foreach table
|
|
|
|
foreach $table ( keys %fielddefinitions ) {
|
|
print "Check table $table\n" if $debug;
|
|
$sth = $dbh->prepare("show columns from $table");
|
|
$sth->execute();
|
|
my $definitions;
|
|
while ( ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
|
{
|
|
$definitions->{$column}->{type} = $type;
|
|
$definitions->{$column}->{null} = $null;
|
|
$definitions->{$column}->{key} = $key;
|
|
$definitions->{$column}->{default} = $default;
|
|
$definitions->{$column}->{extra} = $extra;
|
|
} # while
|
|
my $fieldrow = $fielddefinitions{$table};
|
|
foreach my $row (@$fieldrow) {
|
|
my $field = $row->{field};
|
|
my $type = $row->{type};
|
|
my $null = $row->{null};
|
|
my $key = $row->{key};
|
|
my $default = $row->{default};
|
|
$default="''" unless $default;
|
|
my $extra = $row->{extra};
|
|
my $def = $definitions->{$field};
|
|
unless ( $type eq $def->{type}
|
|
&& $null eq $def->{null}
|
|
&& $key eq $def->{key}
|
|
&& $default eq $def->{default}
|
|
&& $extra eq $def->{extra} )
|
|
{
|
|
|
|
if ( $null eq '' ) {
|
|
$null = 'NOT NULL';
|
|
}
|
|
if ( $key eq 'PRI' ) {
|
|
$key = 'PRIMARY KEY';
|
|
}
|
|
unless ( $extra eq 'auto_increment' ) {
|
|
$extra = '';
|
|
}
|
|
# if it's a new column use "add", if it's an old one, use "change".
|
|
my $action;
|
|
if ($definitions->{$field}->{type}) {
|
|
$action="change $field"
|
|
} else {
|
|
$action="add";
|
|
}
|
|
# if it's a primary key, drop the previous pk, before altering the table
|
|
my $sth;
|
|
if ($key ne 'PRIMARY KEY') {
|
|
$sth =$dbh->prepare("alter table $table $action $field $type $null $key $extra default ?");
|
|
} else {
|
|
$sth =$dbh->prepare("alter table $table drop primary key, $action $field $type $null $key $extra default ?");
|
|
}
|
|
$sth->execute($default);
|
|
print " Alter $field in $table\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
# Get list of columns from borrowers table
|
|
my %itemtypes;
|
|
my %nullenabled;
|
|
$sth = $dbh->prepare("show columns from borrowers");
|
|
$sth->execute;
|
|
while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
|
{
|
|
$itemtypes{$column} = $type;
|
|
$nullenabled{$column} = $null;
|
|
}
|
|
|
|
unless ( $itemtypes{'cardnumber'} eq 'varchar(20)' ) {
|
|
$itemtypes{'cardnumber'} =~ /varchar\((\d+)\)/;
|
|
my $oldlength = $1;
|
|
if ( $oldlength < 16 ) {
|
|
print "Setting maximum cardnumber length to 16 (was $oldlength) and marking unique.\n";
|
|
my $sti =
|
|
$dbh->prepare(
|
|
"alter table borrowers change cardnumber cardnumber varchar(16)");
|
|
$sti->execute;
|
|
$sti->finish;
|
|
$sti =
|
|
$dbh->prepare(
|
|
"alter table borrowers drop index cardnumber");
|
|
$sti->execute;
|
|
$sti->finish;
|
|
$sti =
|
|
$dbh->prepare(
|
|
"alter table borrowers add unique(cardnumber)");
|
|
$sti->execute;
|
|
$sti->finish;
|
|
}
|
|
}
|
|
#
|
|
# Get list of columns from items table
|
|
$sth = $dbh->prepare("show columns from items");
|
|
$sth->execute;
|
|
while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
|
{
|
|
$itemtypes{$column} = $type;
|
|
$nullenabled{$column} = $null;
|
|
}
|
|
|
|
unless ( $itemtypes{'barcode'} eq 'varchar(20)' ) {
|
|
$itemtypes{'barcode'} =~ /varchar\((\d+)\)/;
|
|
my $oldlength = $1;
|
|
if ( $oldlength < 20 ) {
|
|
print "Setting maximum barcode length to 20 (was $oldlength).\n";
|
|
my $sti =
|
|
$dbh->prepare(
|
|
"alter table items change barcode barcode varchar(20)");
|
|
$sti->execute;
|
|
}
|
|
}
|
|
#
|
|
# dropping unique barcode index & setting barcode to null allowed.
|
|
#
|
|
$sth = $dbh->prepare("show index from items");
|
|
$sth->execute;
|
|
while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
|
|
{
|
|
if ($key_name eq 'barcode' && $non_unique eq 0) {
|
|
print "dropping BARCODE index to enable empty barcodes\n";
|
|
$dbh->do("ALTER TABLE `items` DROP INDEX `barcode`");
|
|
}
|
|
}
|
|
$dbh->do("ALTER TABLE `items` CHANGE `barcode` `barcode` VARCHAR( 20 )") unless ($nullenabled{barcode} eq 'YES');
|
|
|
|
#
|
|
# creating fulltext index in bibliothesaurus if needed
|
|
#
|
|
$sth = $dbh->prepare("show index from bibliothesaurus");
|
|
$sth->execute;
|
|
my $exists=0;
|
|
while ( my ( $table, $non_unique, $key_name, $Seq_in_index, $Column_name, $Collation, $cardinality, $sub_part, $Packed, $comment ) = $sth->fetchrow )
|
|
{
|
|
if ($key_name eq 'category_2') {
|
|
$exists=1;
|
|
}
|
|
}
|
|
print "Creating fulltext index on bibliothesaurus\n" unless $exists;
|
|
$dbh->do('create fulltext index category_2 on bibliothesaurus (category,freelib)') unless $exists;
|
|
|
|
# changing z3950daemon field to NULL in marc_breeding
|
|
$dbh->do("ALTER TABLE `marc_breeding` CHANGE `z3950random` `z3950random` VARCHAR( 40 )");
|
|
|
|
# making borrowernumber an auto_increment field
|
|
$dbh->do("ALTER TABLE `borrowers` CHANGE `borrowernumber` `borrowernumber` INTEGER auto_increment");
|
|
|
|
# extending the timestamp in branchtransfers...
|
|
my %branchtransfers;
|
|
|
|
$sth = $dbh->prepare("show columns from branchtransfers");
|
|
$sth->execute;
|
|
while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
|
{
|
|
$branchtransfers{$column} = $type;
|
|
}
|
|
|
|
unless ( $branchtransfers{'datesent'} eq 'datetime' ) {
|
|
print "Setting type of datesent in branchtransfers to datetime.\n";
|
|
my $sti =
|
|
$dbh->prepare(
|
|
"alter table branchtransfers change datesent datesent datetime");
|
|
$sti->execute;
|
|
}
|
|
|
|
unless ( $branchtransfers{'datearrived'} eq 'datetime' ) {
|
|
print "Setting type of datearrived in branchtransfers to datetime.\n";
|
|
my $sti =
|
|
$dbh->prepare(
|
|
"alter table branchtransfers change datearrived datearrived datetime");
|
|
$sti->execute;
|
|
}
|
|
|
|
# changing the branchcategories table around...
|
|
my %branchcategories;
|
|
|
|
$sth = $dbh->prepare("show columns from branchcategories");
|
|
$sth->execute;
|
|
while ( my ( $column, $type, $null, $key, $default, $extra ) = $sth->fetchrow )
|
|
{
|
|
$branchcategories{$column} = $type;
|
|
}
|
|
|
|
unless ( $branchcategories{'categorycode'} eq 'varchar(4)' ) {
|
|
print
|
|
"Setting type of categorycode in branchcategories to varchar(4),\n and making the primary key.\n";
|
|
my $sti =
|
|
$dbh->prepare(
|
|
"alter table branchcategories change categorycode categorycode varchar(4) not null"
|
|
);
|
|
$sti->execute;
|
|
$sti =
|
|
$dbh->prepare(
|
|
"alter table branchcategories add primary key (categorycode)");
|
|
$sti->execute;
|
|
}
|
|
|
|
unless ( $branchcategories{'categoryname'} eq 'text' ) {
|
|
print "Changing branchcode in branchcategories to categoryname text.\n";
|
|
my $sth =
|
|
$dbh->prepare(
|
|
"alter table branchcategories change branchcode categoryname text");
|
|
$sth->execute;
|
|
}
|
|
|
|
unless ( $branchcategories{'codedescription'} eq 'text' ) {
|
|
print
|
|
"Replacing branchholding in branchcategories with codedescription text.\n";
|
|
my $sth =
|
|
$dbh->prepare(
|
|
"alter table branchcategories change branchholding codedescription text"
|
|
);
|
|
$sth->execute;
|
|
}
|
|
|
|
# Populate tables with required data
|
|
|
|
foreach my $table ( keys %tabledata ) {
|
|
print "Checking for data required in table $table...\n";
|
|
my $tablerows = $tabledata{$table};
|
|
foreach my $row (@$tablerows) {
|
|
my $uniquefieldrequired = $row->{uniquefieldrequired};
|
|
my $uniquevalue = $row->{$uniquefieldrequired};
|
|
my $forceupdate = $row->{forceupdate};
|
|
my $sth =
|
|
$dbh->prepare(
|
|
"select $uniquefieldrequired from $table where $uniquefieldrequired=?"
|
|
);
|
|
$sth->execute($uniquevalue);
|
|
if ($sth->rows) {
|
|
foreach my $field (keys %$forceupdate) {
|
|
if ($forceupdate->{$field}) {
|
|
my $sth=$dbh->prepare("update systempreferences set $field=? where $uniquefieldrequired=?");
|
|
$sth->execute($row->{$field}, $uniquevalue);
|
|
}
|
|
}
|
|
} else {
|
|
print "Adding row to $table: ";
|
|
my @values;
|
|
my $fieldlist;
|
|
my $placeholders;
|
|
foreach my $field ( keys %$row ) {
|
|
next if $field eq 'uniquefieldrequired';
|
|
next if $field eq 'forceupdate';
|
|
my $value = $row->{$field};
|
|
push @values, $value;
|
|
print " $field => $value";
|
|
$fieldlist .= "$field,";
|
|
$placeholders .= "?,";
|
|
}
|
|
print "\n";
|
|
$fieldlist =~ s/,$//;
|
|
$placeholders =~ s/,$//;
|
|
my $sth =
|
|
$dbh->prepare(
|
|
"insert into $table ($fieldlist) values ($placeholders)");
|
|
$sth->execute(@values);
|
|
}
|
|
}
|
|
}
|
|
|
|
$sth->finish;
|
|
|
|
exit;
|
|
|
|
# $Log$
|
|
# Revision 1.71 2003/12/18 17:23:22 tipaul
|
|
# fix for 625
|
|
#
|
|
# Revision 1.70 2003/12/15 14:40:09 tipaul
|
|
# OPAC now show a systempref variable in main/home screen => easier to change for a library.
|
|
#
|
|
# Revision 1.69 2003/12/04 12:51:41 tipaul
|
|
# setting default acquisition to NORMAL, which is better, imho (it's always possible to add a biblio directly through catalogue menu entry)
|
|
#
|
|
# Revision 1.68 2003/12/03 17:47:14 tipaul
|
|
# bugfixes for biblio deletion
|
|
#
|
|
# Revision 1.67 2003/11/28 10:08:33 tipaul
|
|
# * removing too verbose messages.
|
|
# * creating a fulltext index on bibliothesaurus
|
|
#
|
|
# Revision 1.66 2003/11/12 16:14:42 slef
|
|
# lengthen cardnumber to 16 and make it unique
|
|
#
|
|
# Revision 1.65 2003/11/06 15:07:11 tipaul
|
|
# adding marc fields in deletedbiblio & deleteditems
|
|
#
|
|
# Revision 1.64 2003/10/23 20:33:53 rangi
|
|
# Making the borrowenumber an auto_increment field
|
|
#
|
|
# Revision 1.63 2003/10/20 16:13:01 slef
|
|
# Omitted annotation added. Closes: 624
|
|
#
|
|
# Revision 1.62 2003/10/20 16:10:19 slef
|
|
# Adding USMARC to LOC z3950 entry
|
|
#
|
|
# Revision 1.61 2003/10/01 15:03:45 tipaul
|
|
# oups... typo fix in z3950random field definition
|
|
#
|
|
# Revision 1.59 2003/09/30 16:22:05 tipaul
|
|
# adding barcode NOT mandatory feature. Just run updatedatabase to get it.
|
|
# Note it's impossible to issue an item without barcode, as issue/returns is based on barcode...
|
|
#
|
|
# Revision 1.58 2003/07/16 04:08:29 acli
|
|
# Minor spelling correction
|
|
#
|
|
# Revision 1.57 2003/07/11 11:50:29 tipaul
|
|
# fixing a bug that occured when adding a field into a table.
|
|
#
|
|
# Revision 1.56 2003/07/07 15:37:20 tipaul
|
|
# *** empty log message ***
|
|
#
|
|
# Revision 1.53 2003/07/07 14:11:16 tipaul
|
|
# fixing bug #526 : gst rate is now calculated through systempref gist entry.
|
|
# Before this fix :
|
|
# * was harcoded to 12,5%
|
|
# * some bugs in template parameters prevented the javascript to work.
|
|
# * some bugs prevented some calculations to be done properly.
|
|
#
|
|
# Revision 1.52 2003/06/23 15:54:32 tipaul
|
|
# *** empty log message ***
|
|
#
|
|
# Revision 1.51 2003/06/23 11:27:29 tipaul
|
|
# *** empty log message ***
|
|
#
|
|
# Revision 1.50 2003/06/11 21:28:22 tonnesen
|
|
# Added modifications required to the systempreferences table by the new
|
|
# systempreferences.pl script. The systempreferences.pl script will not work
|
|
# properly until this table is updated.
|
|
#
|
|
# Revision 1.49 2003/05/26 10:41:53 tipaul
|
|
# bugfix : borrowers modifs overwritten by stupid hash entry existing twice.
|
|
#
|
|
# Revision 1.48 2003/05/20 19:50:45 slef
|
|
# Initial fix to bug 456: hardwired paths
|
|
#
|
|
# Revision 1.47 2003/05/15 12:23:33 tipaul
|
|
# adding zipcode and homezipcode into borrowers table (bug #246
|
|
#
|
|
# Revision 1.46 2003/05/08 12:48:24 wolfpac444
|
|
# Added "noissuescharge" parameter
|
|
#
|
|
# Revision 1.45 2003/05/08 12:26:16 wolfpac444
|
|
# Bug fixes
|
|
#
|
|
# Revision 1.44 2003/05/03 05:39:57 rangi
|
|
# Fixing bug 429
|
|
# (Wording changes in the explanation fields in system preferences)
|
|
#
|
|
# Revision 1.43 2003/05/02 23:01:09 rangi
|
|
# Adding the textmessaging column to the borrowers table.
|
|
# insertdata.pl is expecting this to exist, and hence modifying/adding
|
|
# borrowers was broken.
|
|
#
|
|
# Also ran they script thru perltidy
|
|
#
|
|
# Revision 1.42 2003/04/29 16:53:25 tipaul
|
|
# really proud of this commit :-)
|
|
# z3950 search and import seems to works fine.
|
|
# Let me explain how :
|
|
# * a "search z3950" button is added in the addbiblio template.
|
|
# * when clicked, a popup appears and z3950/search.pl is called
|
|
# * z3950/search.pl calls addz3950search in the DB
|
|
# * the z3950 daemon retrieve the records and stores them in z3950results AND in marc_breeding table.
|
|
# * as long as there as searches pending, the popup auto refresh every 2 seconds, and says how many searches are pending.
|
|
# * when the user clicks on a z3950 result => the parent popup is called with the requested biblio, and auto-filled
|
|
#
|
|
# Note :
|
|
# * character encoding support : (It's a nightmare...) In the z3950servers table, a "encoding" column has been added. You can put "UNIMARC" or "USMARC" in this column. Depending on this, the char_decode in C4::Biblio.pm replaces marc-char-encode by an iso 8859-1 encoding. Note that in the breeding import this value has been added too, for a better support.
|
|
# * the marc_breeding and z3950* tables have been modified : they have an encoding column and the random z3950 number is stored too for convenience => it's the key I use to list only requested biblios in the popup.
|
|
#
|
|
# Revision 1.41 2003/04/29 08:09:44 tipaul
|
|
# z3950 support is coming...
|
|
# * adding a syntax column in z3950 table = this column will say wether the z3950 must be called with PerferedRecordsyntax => USMARC or PerferedRecordsyntax => UNIMARC. I tried some french UNIMARC z3950 servers, and some only send USMARC, some only UNIMARC, some can answer with both.
|
|
# Note this is a 1st draft. More to follow (today ? I hope).
|
|
#
|
|
# Revision 1.40 2003/04/22 10:48:27 wolfpac444
|
|
# Added "father" column to bibliothesaurus table
|
|
#
|
|
# Revision 1.39 2003/04/04 08:45:00 tipaul
|
|
# last commits before 1.9.1
|
|
#
|
|
# Revision 1.38 2003/03/18 10:58:19 tipaul
|
|
# adding checkdigit parameter that choose how to check the members cardnumber.
|
|
# At the moment :
|
|
# * none = no checking
|
|
# * katipo = checked as before
|
|
#
|
|
# Revision 1.37 2003/01/30 01:47:48 acli
|
|
# Corrected syntax error reported by Benedict
|
|
#
|
|
# Made the indentation somewhat easier to read; the messiness probably caused
|
|
# the original syntax error.
|
|
#
|
|
# Revision 1.36 2003/01/28 15:13:30 tipaul
|
|
# userflag table now created in upgrade script (bugfix #171)
|
|
#
|
|
# Revision 1.35 2003/01/27 03:12:49 acli
|
|
# Reworded the description for "acquisitions" to make it fit on the screen
|
|
#
|
|
# Added "iso" to dateformat, since dateformat is not yet being used anyway
|
|
#
|
|
# Revision 1.34 2003/01/23 12:30:02 tipaul
|
|
# introducint marcflavour in systempref file : used for character decoding
|
|
#
|
|
# Revision 1.33 2003/01/21 09:03:27 tipaul
|
|
# bugfix (NOTE : this bugs makes installation of the 1.3.3 a little fuzzy. Please fix your DB if you installed 1.3.3)
|
|
#
|
|
# Revision 1.32 2003/01/16 10:29:45 tipaul
|
|
# adding a MARC parameter in systempref ( which is ON or OFF)
|
|
# the search will be a marc search if MARC=ON
|
|
# and a standard (v1.2) search if MARC=OFF
|
|
#
|
|
# Revision 1.31 2003/01/06 13:32:43 tipaul
|
|
# *** empty log message ***
|
|
#
|
|
# Revision 1.29 2003/01/06 11:14:11 tipaul
|
|
# last bugfixes before 1.3.3 : systempref table correctly filled
|
|
#
|
|
# Revision 1.28 2002/12/10 13:27:47 tipaul
|
|
# bugfixes (davide mails in koha-dev)
|
|
#
|
|
# Revision 1.27 2002/11/26 15:04:54 tipaul
|
|
# road to 1.3.2. Updating db structure during installation
|
|
#
|
|
# Revision 1.26 2002/11/12 17:42:40 tonnesen
|
|
# Merged some features over from rel-1-2, including primary key checking.
|
|
#
|
|
# Revision 1.25 2002/11/12 16:44:38 tipaul
|
|
# road to 1.3.2 :
|
|
# * many bugfixes
|
|
# * adding value_builder : you can map a subfield in the marc_subfield_structure to a sub stored in "value_builder" directory. In this directory you can create screen used to build values with any method. In this commit is a 1st draft of the builder for 100$a unimarc french subfield, which is composed of 35 digits, with 12 differents values (only the 4th first are provided for instance)
|
|
#
|
|
# Revision 1.24 2002/10/30 14:00:23 arensb
|
|
# (bug fix): Fixed typo.
|
|
#
|
|
# Revision 1.23 2002/10/25 10:55:46 tipaul
|
|
# Road to 1.3.2
|
|
# * bugfixes and improvements
|
|
# * manage mandatory MARC subfields
|
|
# * new table : authorised_values. this table contains categories and authorised values for the category. On MARC management, you can map a subfield to a authorised_values category. If you do this, the subfield can only be filled with a authorised_value of the selected category.
|
|
# this submit contains everything needed :
|
|
# * updatedatabase
|
|
# * admin screens
|
|
# * "links" management
|
|
# * creation of a html-list if a subfield is mapped to an authorised value.
|
|
#
|
|
# Note this is different from authorities support, which will come soon.
|
|
# The authorised_values is supposed to contains a "small" number of authorised values for a category (less than 50-100). If you enter more authorised values than this, it should be hard to find what you want in a BIG list...
|
|
#
|
|
# Revision 1.22 2002/10/15 10:08:19 tipaul
|
|
# fixme corrected, re-indent and adding the marc_breeding table (see commit of marcimport.pl for more explanations about breeding)
|
|
#
|
|
# Revision 1.21 2002/10/14 11:48:59 tipaul
|
|
# bugfix
|
|
#
|
|
# Revision 1.20 2002/10/10 04:49:41 arensb
|
|
# Added some FIXME comments.
|
|
#
|
|
# Revision 1.19 2002/10/05 10:17:17 arensb
|
|
# Merged with arensb-context branch: use C4::Context->dbh instead of
|
|
# &C4Connect, and generally prefer C4::Context over C4::Database.
|
|
#
|
|
# Revision 1.18.2.2 2002/10/05 06:18:43 arensb
|
|
# Added a whole mess of FIXME comments.
|
|
#
|
|
# Revision 1.18.2.1 2002/10/04 02:46:00 arensb
|
|
# Use C4::Connect instead of C4::Database, C4::Connect->dbh instead
|
|
# C4Connect.
|
|
#
|
|
# Revision 1.18 2002/09/24 13:50:55 tipaul
|
|
# long WAS the road to 1.3.0...
|
|
# coming VERY SOON NOW...
|
|
# modifying installer and buildrelease to update the DB
|
|
#
|
|
# Revision 1.17 2002/09/24 12:57:35 tipaul
|
|
# long WAS the road to 1.3.0...
|
|
# coming VERY SOON NOW...
|
|
# modifying installer and buildrelease to update the DB
|
|
#
|
|
# Revision 1.16 2002/07/31 02:34:27 finlayt
|
|
#
|
|
# added "notforloan" field to the itemtypes table.
|
|
#
|
|
# Revision 1.15 2002/07/20 22:30:06 rangi
|
|
# Making sure fix makes it into the main branch as well
|
|
# Fix for bug 69
|
|
#
|
|
# Revision 1.14 2002/07/08 16:20:26 tonnesen
|
|
# Added sessionqueries table and password/userid fields to borrowers table
|
|
#
|
|
# Revision 1.13 2002/07/04 18:05:36 tonnesen
|
|
# bug fix
|
|
#
|
|
# Revision 1.12 2002/07/04 16:41:06 tonnesen
|
|
# Merged changes from rel-1-2. Abstracted table structure changes by alan.
|
|
#
|