# The LearningOnline Network with CAPA
# MySQL utility functions
#
# $Id: lonmysql.pm,v 1.36 2007/03/12 17:10:02 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA 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 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA 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 LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# /home/httpd/html/adm/gpl.txt
#
# http://www.lon-capa.org/
#
######################################################################
package Apache::lonmysql;
use strict;
use DBI;
use POSIX qw(strftime mktime);
use Apache::lonnet;
my $mysqluser;
my $mysqlpassword;
my $mysqldatabase;
my %db_config;
sub set_mysql_user_and_password {
# If we are running under Apache and LONCAPA, use the LON-CAPA
# user and password. Otherwise...? ? ? ?
my ($input_mysqluser,$input_mysqlpassword,$input_mysqldatabase) = @_;
if (! defined($mysqldatabase)) {
$mysqldatabase = 'loncapa';
}
if (defined($input_mysqldatabase)) {
$mysqldatabase = $input_mysqldatabase;
}
if (! defined($mysqluser) || ! defined($mysqlpassword)) {
if (! eval 'require Apache::lonnet();') {
$mysqluser = 'www';
$mysqlpassword = $Apache::lonnet::perlvar{'lonSqlAccess'};
} else {
$mysqluser = '';
$mysqlpassword = '';
}
}
if (defined($input_mysqluser)) {
$mysqluser = $input_mysqluser;
}
if (defined($input_mysqlpassword)) {
$mysqlpassword = $input_mysqlpassword;
}
}
######################################################################
######################################################################
=pod
=head1 Name
lonmysql - LONCAPA MySQL utility functions
=head1 Synopsis
lonmysql contains utility functions to make accessing the mysql loncapa
database easier.
=head1 Description
lonmysql does its best to encapsulate all the database/table functions
and provide a common interface. The goal, however, is not to provide
a complete reimplementation of the DBI interface. Instead we try to
make using mysql as painless as possible.
Each table has a numeric ID that is a parameter to most lonmysql
functions. The table id is returned by &create_table. If you lose
the table id, it is lost forever. The table names in MySQL correspond
to $env{'user.name'}.'_'.$env{'user.domain'}.'_'.$table_id. (With all
non-word characters removed form user.name and user.domain) If the
table id is non-numeric, it is assumed to be the full name of a table.
If you pass the table id in a form, you MUST ensure that what you send
to lonmysql is numeric, otherwise you are opening up all the tables in
the MySQL database.
=over 4
=item Creating a table
To create a table, you need a description of its structure. See the entry
for &create_table for a description of what is needed.
$table_id = &create_table({
id => 'tableid', # usually you will use the returned id
columns => (
{ name => 'id',
type => 'INT',
restrictions => 'NOT NULL',
primary_key => 'yes',
auto_inc => 'yes'
},
{ name => 'verbage',
type => 'TEXT' },
),
fulltext => [qw/verbage/],
});
The above command will create a table with two columns, 'id' and 'verbage'.
'id' will be an integer which is autoincremented and non-null.
'verbage' will be of type 'TEXT', which (conceivably) allows any length
text string to be stored. Depending on your intentions for this database,
setting restrictions => 'NOT NULL' may help you avoid storing empty data.
the fulltext element sets up the 'verbage' column for 'FULLTEXT' searching.
=item Storing rows
Storing a row in a table requires calling &store_row($table_id,$data)
$data is either a hash reference or an array reference. If it is an array
reference, the data is passed as is (after being escaped) to the
"INSERT INTO <table> VALUES( ... )" SQL command. If $data is a hash reference,
the data will be placed into an array in the proper column order for the table
and then passed to the database.
An example of inserting into the table created above is:
&store_row($table_id,[undef,'I am not a crackpot!']);
or equivalently,
&store_row($table_id,{ verbage => 'I am not a crackpot!'});
Since the above table was created with the first column ('id') as
autoincrement, providing a value is unnecessary even though the column was
marked as 'NOT NULL'.
=item Retrieving rows
Retrieving rows requires calling get_rows:
@row = &Apache::lonmysql::get_rows($table_id,$condition)
This results in the query "SELECT * FROM <table> HAVING $condition".
@row = &Apache::lonmysql::get_rows($table_id,'id>20');
returns all rows with column 'id' greater than 20.
=back
=cut
######################################################################
######################################################################
=pod
=head1 Package Variables
=over 4
=cut
##################################################
##################################################
=pod
=item %Tables
Holds information regarding the currently open connections. Each key
in the %Tables hash will be a unique table key. The value associated
with a key is a hash reference. Most values are initialized when the
table is created.
The following entries are allowed in the hash reference:
=over 4
=item Name
Table name.
=item Type
The type of table, typically MyISAM.
=item Row_format
Describes how rows should be stored in the table. DYNAMIC or STATIC.
=item Create_time
The date of the tables creation.
=item Update_time
The date of the last modification of the table.
=item Check_time
Usually NULL.
=item Avg_row_length
The average length of the rows.
=item Data_length
The length of the data stored in the table (bytes)
=item Max_data_length
The maximum possible size of the table (bytes).
=item Index_length
The length of the index for the table (bytes)
=item Data_free
I have no idea what this is.
=item Comment
The comment associated with the table.
=item Rows
The number of rows in the table.
=item Auto_increment
The value of the next auto_increment field.
=item Create_options
I have no idea.
=item Col_order
an array reference which holds the order of columns in the table.
=item row_insert_sth
The statement handler for row inserts.
=item row_replace_sth
The statement handler for row inserts.
=back
Col_order and row_insert_sth are kept internally by lonmysql and are not
part of the usual MySQL table information.
=cut
##################################################
##################################################
my %Tables;
##################################################
##################################################
=pod
=item $errorstring
Holds the last error.
=cut
##################################################
##################################################
my $errorstring;
##################################################
##################################################
=pod
=item $debugstring
Describes current events within the package.
=cut
##################################################
##################################################
my $debugstring;
##################################################
##################################################
=pod
=item $dbh
The database handler; The actual connection to MySQL via the perl DBI.
=cut
##################################################
##################################################
my $dbh;
##################################################
##################################################
# End of global variable declarations
=pod
=back
=cut
######################################################################
######################################################################
=pod
=head1 Internals
=over 4
=cut
######################################################################
######################################################################
=pod
=item &connect_to_db()
Inputs: none.
Returns: undef on error, 1 on success.
Checks to make sure the database has been connected to. If not, the
connection is established.
=cut
###############################
sub connect_to_db {
return 1 if ($dbh);
if (! defined($mysqluser) || ! defined($mysqlpassword)) {
&set_mysql_user_and_password();
}
if (! ($dbh = DBI->connect("DBI:mysql:$mysqldatabase",$mysqluser,$mysqlpassword,
{ RaiseError=>0,PrintError=>0}))) {
$debugstring = "Unable to connect to loncapa database.";
if (! defined($dbh)) {
$debugstring = "Unable to connect to loncapa database.";
$errorstring = "dbh was undefined.";
} elsif ($dbh->err) {
$errorstring = "Connection error: ".$dbh->errstr;
}
return undef;
}
$debugstring = "Successfully connected to loncapa database.";
# Determine DB configuration
undef(%db_config);
my $sth = $dbh->prepare("SHOW VARIABLES");
$sth->execute();
if ($sth->err()) {
$debugstring = "Unable to retrieve db config variables";
return undef;
}
foreach my $row (@{$sth->fetchall_arrayref}) {
$db_config{$row->[0]} = $row->[1];
}
#&Apache::lonnet::logthis("MySQL configuration variables");
#while (my ($k,$v) = each(%db_config)) {
# &Apache::lonnet::logthis(" '$k' => '$v'");
#}
#
return 1;
}
###############################
=pod
=item &verify_sql_connection()
Inputs: none.
Returns: 0 (failure) or 1 (success)
Checks to make sure the database can be connected to. It does not
initialize anything in the lonmysql package.
=cut
###############################
sub verify_sql_connection {
if (! defined($mysqluser) || ! defined($mysqlpassword)) {
&set_mysql_user_and_password();
}
my $connection;
if (! ($connection = DBI->connect("DBI:mysql:loncapa",
$mysqluser,$mysqlpassword,
{ RaiseError=>0,PrintError=>0}))) {
return 0;
}
undef($connection);
return 1;
}
###############################
=pod
=item &disconnect_from_db()
Inputs: none.
Returns: Always returns 1.
Severs the connection to the mysql database.
=cut
###############################
sub disconnect_from_db {
foreach (keys(%Tables)) {
# Supposedly, having statement handlers running around after the
# database connection has been lost will cause trouble. So we
# kill them off just to be sure.
if (exists($Tables{$_}->{'row_insert_sth'})) {
delete($Tables{$_}->{'row_insert_sth'});
}
if (exists($Tables{$_}->{'row_replace_sth'})) {
delete($Tables{$_}->{'row_replace_sth'});
}
}
$dbh->disconnect if ($dbh);
$debugstring = "Disconnected from database.";
$dbh = undef;
return 1;
}
###############################
=pod
=item &number_of_rows()
Input: table identifier
Returns: the number of rows in the given table, undef on error.
=cut
###############################
sub number_of_rows {
my ($table_id) = @_;
return undef if (! defined(&connect_to_db()));
return undef if (! defined(&update_table_info($table_id)));
return $Tables{&translate_id($table_id)}->{'Rows'};
}
###############################
=pod
=item &get_dbh()
Input: nothing
Returns: the database handler, or undef on error.
This routine allows the programmer to gain access to the database handler.
Be careful.
=cut
###############################
sub get_dbh {
return undef if (! defined(&connect_to_db()));
return $dbh;
}
###############################
=pod
=item &get_error()
Inputs: none.
Returns: The last error reported.
=cut
###############################
sub get_error {
return $errorstring;
}
###############################
=pod
=item &get_debug()
Inputs: none.
Returns: A string describing the internal state of the lonmysql package.
=cut
###############################
sub get_debug {
return $debugstring;
}
###############################
=pod
=item &update_table_info()
Inputs: table id
Returns: undef on error, 1 on success.
&update_table_info updates the %Tables hash with current information about
the given table.
The default MySQL table status fields are:
Name Type Row_format
Max_data_length Index_length Data_free
Create_time Update_time Check_time
Avg_row_length Data_length Comment
Rows Auto_increment Create_options
Additionally, "Col_order" is updated as well.
=cut
###############################
sub update_table_info {
my ($table_id) = @_;
return undef if (! defined(&connect_to_db()));
my $table_status = &check_table($table_id);
return undef if (! defined($table_status));
if (! $table_status) {
$errorstring = "table $table_id does not exist.";
return undef;
}
my $tablename = &translate_id($table_id);
#
# Get MySQLs table status information.
#
my $db_command = "SHOW TABLE STATUS FROM loncapa LIKE '$tablename'";
my $sth = $dbh->prepare($db_command);
$sth->execute();
if ($sth->err) {
$errorstring = "$dbh ATTEMPTED:\n".$db_command."\nRESULTING ERROR:\n".
$sth->errstr;
&disconnect_from_db();
return undef;
}
my @column_name = @{$sth->{NAME}};
#
my @info=$sth->fetchrow_array;
for (my $i=0;$i<= $#info ; $i++) {
if ($column_name[$i] =~ /^(Create_|Update_|Check_)time$/) {
$Tables{$tablename}->{$column_name[$i]}=
&unsqltime($info[$i]);
} else {
$Tables{$tablename}->{$column_name[$i]}= $info[$i];
}
}
#
# Determine the column order
#
$db_command = "DESCRIBE $tablename";
$sth = $dbh->prepare($db_command);
$sth->execute();
if ($sth->err) {
$errorstring = "$dbh ATTEMPTED:\n".$db_command."\nRESULTING ERROR:\n".
$sth->errstr;
&disconnect_from_db();
return undef;
}
my $aref=$sth->fetchall_arrayref;
$Tables{$tablename}->{'Col_order'}=[]; # Clear values.
# The values we want are the 'Field' entries, the first column.
for (my $i=0;$i< @$aref ; $i++) {
push @{$Tables{$tablename}->{'Col_order'}},$aref->[$i]->[0];
}
#
$debugstring = "Retrieved table info for $tablename";
return 1;
}
###############################
=pod
=item &table_information()
Inputs: table id
Returns: hash with the table status
=cut
###############################
sub table_information {
my $table_id=shift;
if (&update_table_info($table_id)) {
return %{$Tables{$table_id}};
} else {
return ();
}
}
###############################
=pod
=item &col_order()
Inputs: table id
Returns: array with column order
=cut
###############################
sub col_order {
my $table_id=shift;
if (&update_table_info($table_id)) {
return @{$Tables{$table_id}->{'Col_order'}};
} else {
return ();
}
}
###############################
=pod
=item &create_table()
Inputs:
table description, see &build_table_creation_request
Returns:
undef on error, table id on success.
=cut
###############################
sub create_table {
return undef if (!defined(&connect_to_db($dbh)));
my ($table_des)=@_;
my ($request,$table_id) = &build_table_creation_request($table_des);
#
# Execute the request to create the table
#############################################
my $count = $dbh->do($request);
if (! defined($count)) {
$errorstring = "$dbh ATTEMPTED:\n".$request."\nRESULTING ERROR:\n".
$dbh->errstr();
return undef;
}
my $tablename = &translate_id($table_id);
delete($Tables{$tablename}) if (exists($Tables{$tablename}));
return undef if (! defined(&update_table_info($table_id)));
$debugstring = "Created table $tablename at time ".time.
" with request\n$request";
return $table_id;
}
###############################
=pod
=item build_table_creation_request
Input: table description
table description = {
permanent => 'yes' or 'no',
columns => [
{ name => 'colA',
type => mysql type,
restrictions => 'NOT NULL' or empty,
primary_key => 'yes' or empty,
auto_inc => 'yes' or empty,
},
{ name => 'colB',
...
},
{ name => 'colC',
...
},
],
'PRIMARY KEY' => (index_col_name,...),
KEY => [{ name => 'idx_name',
columns => (col1,col2,..),},],
INDEX => [{ name => 'idx_name',
columns => (col1,col2,..),},],
UNIQUE => [{ index => 'yes',
name => 'idx_name',
columns => (col1,col2,..),},],
FULLTEXT => [{ index => 'yes',
name => 'idx_name',
columns => (col1,col2,..),},],
}
Returns: scalar string containing mysql commands to create the table
=cut
###############################
sub build_table_creation_request {
my ($table_des)=@_;
#
# Build request to create table
##################################
my @Columns;
my $col_des;
my $table_id;
if (exists($table_des->{'id'})) {
$table_id = $table_des->{'id'};
} else {
$table_id = &get_new_table_id();
}
my $tablename = &translate_id($table_id);
&Apache::lonnet::logthis(" hmm $tablename, $table_id, ". $table_des->{'id'});
my $request = "CREATE TABLE IF NOT EXISTS ".$tablename." ";
foreach my $coldata (@{$table_des->{'columns'}}) {
my $column = $coldata->{'name'};
next if (! defined($column));
$col_des = '';
if (lc($coldata->{'type'}) =~ /(enum|set)/) { # 'enum' or 'set'
$col_des.=$column." ".$coldata->{'type'}."('".
join("', '",@{$coldata->{'values'}})."')";
} else {
$col_des.=$column." ".$coldata->{'type'};
if (exists($coldata->{'size'})) {
$col_des.="(".$coldata->{'size'}.")";
}
}
# Modifiers
if (exists($coldata->{'restrictions'})){
$col_des.=" ".$coldata->{'restrictions'};
}
if (exists($coldata->{'default'})) {
$col_des.=" DEFAULT '".$coldata->{'default'}."'";
}
$col_des.=' AUTO_INCREMENT' if (exists($coldata->{'auto_inc'}) &&
($coldata->{'auto_inc'} eq 'yes'));
$col_des.=' PRIMARY KEY' if (exists($coldata->{'primary_key'}) &&
($coldata->{'primary_key'} eq 'yes'));
} continue {
# skip blank items.
push (@Columns,$col_des) if ($col_des ne '');
}
if (exists($table_des->{'PRIMARY KEY'})) {
push (@Columns,'PRIMARY KEY ('.join(',',@{$table_des->{'PRIMARY KEY'}})
.')');
}
#
foreach my $indextype ('KEY','INDEX') {
next if (!exists($table_des->{$indextype}));
foreach my $indexdescription (@{$table_des->{$indextype}}) {
my $text = $indextype.' ';
if (exists($indexdescription->{'name'})) {
$text .=$indexdescription->{'name'};
}
$text .= ' ('.join(',',@{$indexdescription->{'columns'}}).')';
push (@Columns,$text);
}
}
#
foreach my $indextype ('UNIQUE','FULLTEXT') {
next if (! exists($table_des->{$indextype}));
foreach my $indexdescription (@{$table_des->{$indextype}}) {
my $text = $indextype.' ';
if (exists($indexdescription->{'index'}) &&
$indexdescription->{'index'} eq 'yes') {
$text .= 'INDEX ';
}
if (exists($indexdescription->{'name'})) {
$text .=$indexdescription->{'name'};
}
$text .= ' ('.join(',',@{$indexdescription->{'columns'}}).')';
push (@Columns,$text);
}
}
#
$request .= "(".join(", ",@Columns).") ";
unless($table_des->{'permanent'} eq 'yes') {
$request.="COMMENT = 'temporary' ";
}
$request .= "TYPE=MYISAM";
return $request,$table_id;
}
###############################
=pod
=item &get_table_prefix()
returns the cleaned version of user.name and user.domain for us in table names
=cut
###############################
sub get_table_prefix {
my $clean_name = $env{'user.name'};
my $clean_domain = $env{'user.domain'};
$clean_name =~ s/\W//g;
$clean_domain =~ s/\W//g;
return $clean_name.'_'.$clean_domain.'_';
}
###############################
=pod
=item &get_new_table_id()
Used internally to prevent table name collisions.
=cut
###############################
sub get_new_table_id {
my $newid = 0;
my @tables = &tables_in_db();
my $prefix = &get_table_prefix();
foreach (@tables) {
if (/^\Q$prefix\E(\d+)$/) {
$newid = $1 if ($1 > $newid);
}
}
return ++$newid;
}
###############################
=pod
=item &get_rows()
Inputs: $table_id,$condition
Returns: undef on error, an array ref to (array of) results on success.
Internally, this function does a 'SELECT * FROM table WHERE $condition'.
$condition = 'id>0' will result in all rows where column 'id' has a value
greater than 0 being returned.
=cut
###############################
sub get_rows {
my ($table_id,$condition) = @_;
return undef if (! defined(&connect_to_db()));
my $table_status = &check_table($table_id);
return undef if (! defined($table_status));
if (! $table_status) {
$errorstring = "table $table_id does not exist.";
return undef;
}
my $tablename = &translate_id($table_id);
my $request;
if (defined($condition) && $condition ne '') {
$request = 'SELECT * FROM '.$tablename.' WHERE '.$condition;
} else {
$request = 'SELECT * FROM '.$tablename;
$condition = 'no condition';
}
my $sth=$dbh->prepare($request);
$sth->execute();
if ($sth->err) {
$errorstring = "$dbh ATTEMPTED:\n".$request."\nRESULTING ERROR:\n".
$sth->errstr;
$debugstring = "Failed to get rows matching $condition";
return undef;
}
$debugstring = "Got rows matching $condition";
my @Results = @{$sth->fetchall_arrayref};
return @Results;
}
###############################
=pod
=item &store_row()
Inputs: table id, row data
returns undef on error, 1 on success.
=cut
###############################
sub store_row {
my ($table_id,$rowdata) = @_;
#
return undef if (! defined(&connect_to_db()));
my $table_status = &check_table($table_id);
return undef if (! defined($table_status));
if (! $table_status) {
$errorstring = "table $table_id does not exist.";
return undef;
}
#
my $tablename = &translate_id($table_id);
#
my $sth;
if (exists($Tables{$tablename}->{'row_insert_sth'})) {
$sth = $Tables{$tablename}->{'row_insert_sth'};
} else {
# Build the insert statement handler
return undef if (! defined(&update_table_info($table_id)));
my $insert_request = 'INSERT INTO '.$tablename.' VALUES(';
foreach (@{$Tables{$tablename}->{'Col_order'}}) {
$insert_request.="?,";
}
chop $insert_request;
$insert_request.=")";
$sth=$dbh->prepare($insert_request);
$Tables{$tablename}->{'row_insert_sth'}=$sth;
}
my @Parameters;
if (ref($rowdata) eq 'ARRAY') {
@Parameters = @$rowdata;
} elsif (ref($rowdata) eq 'HASH') {
foreach (@{$Tables{$tablename}->{'Col_order'}}) {
push(@Parameters,$rowdata->{$_});
}
}
$sth->execute(@Parameters);
if ($sth->err) {
$errorstring = "$dbh ATTEMPTED insert @Parameters RESULTING ERROR:\n".
$sth->errstr;
return undef;
}
$debugstring = "Stored row.";
return 1;
}
###############################
=pod
=item &bulk_store_rows()
Inputs: table id, [columns],[[row data1].[row data2],...]
returns undef on error, 1 on success.
=cut
###############################
sub bulk_store_rows {
my ($table_id,$columns,$rows) = @_;
#
return undef if (! defined(&connect_to_db()));
my $dbh = &get_dbh();
return undef if (! defined($dbh));
my $table_status = &check_table($table_id);
return undef if (! defined($table_status));
if (! $table_status) {
$errorstring = "table $table_id does not exist.";
return undef;
}
#
my $tablename = &translate_id($table_id);
#
my $request = 'INSERT IGNORE INTO '.$tablename.' ';
if (defined($columns) && ref($columns) eq 'ARRAY') {
$request .= join(',',@$columns).' ';
}
if (! defined($rows) || ref($rows) ne 'ARRAY') {
$errorstring = "no input rows given.";
return undef;
}
$request .= 'VALUES ';
foreach my $row (@$rows) {
# avoid doing row stuff here...
$request .= '('.join(',',@$row).'),';
}
$request =~ s/,$//;
# $debugstring = "Executed ".$/.$request; # commented out - this is big
$dbh->do($request);
if ($dbh->err) {
$errorstring = 'Attempted '.$/.$request.$/.'Got error '.$dbh->errstr();
return undef;
}
return 1;
}
###############################
=pod
=item &replace_row()
Inputs: table id, row data
returns undef on error, 1 on success.
Acts like &store_row() but uses the 'REPLACE' command instead of 'INSERT'.
=cut
###############################
sub replace_row {
my ($table_id,$rowdata) = @_;
#
return undef if (! defined(&connect_to_db()));
my $table_status = &check_table($table_id);
return undef if (! defined($table_status));
if (! $table_status) {
$errorstring = "table $table_id does not exist.";
return undef;
}
#
my $tablename = &translate_id($table_id);
#
my $sth;
if (exists($Tables{$tablename}->{'row_replace_sth'})) {
$sth = $Tables{$tablename}->{'row_replace_sth'};
} else {
# Build the insert statement handler
return undef if (! defined(&update_table_info($table_id)));
my $replace_request = 'REPLACE INTO '.$tablename.' VALUES(';
foreach (@{$Tables{$tablename}->{'Col_order'}}) {
$replace_request.="?,";
}
chop $replace_request;
$replace_request.=")";
$sth=$dbh->prepare($replace_request);
$Tables{$tablename}->{'row_replace_sth'}=$sth;
}
my @Parameters;
if (ref($rowdata) eq 'ARRAY') {
@Parameters = @$rowdata;
} elsif (ref($rowdata) eq 'HASH') {
foreach (@{$Tables{$tablename}->{'Col_order'}}) {
push(@Parameters,$rowdata->{$_});
}
}
$sth->execute(@Parameters);
if ($sth->err) {
$errorstring = "$dbh ATTEMPTED replace @Parameters RESULTING ERROR:\n".
$sth->errstr;
return undef;
}
$debugstring = "Stored row.";
return 1;
}
###########################################
=pod
=item &tables_in_db()
Returns a list containing the names of all the tables in the database.
Returns undef on error.
=cut
###########################################
sub tables_in_db {
return undef if (!defined(&connect_to_db()));
my $sth=$dbh->prepare('SHOW TABLES');
$sth->execute();
$sth->execute();
my $aref = $sth->fetchall_arrayref;
if ($sth->err()) {
$errorstring =
"$dbh ATTEMPTED:\n".'fetchall_arrayref after SHOW TABLES'.
"\nRESULTING ERROR:\n".$sth->errstr;
return undef;
}
my @table_list;
foreach (@$aref) {
push(@table_list,$_->[0]);
}
$debugstring = "Got list of tables in DB: ".join(',',@table_list);
return(@table_list);
}
###########################################
=pod
=item &translate_id()
Used internally to translate a numeric table id into a MySQL table name.
If the input $id contains non-numeric characters it is assumed to have
already been translated.
Checks are NOT performed to see if the table actually exists.
=cut
###########################################
sub translate_id {
my $id = shift;
# id should be a digit. If it is not a digit we assume the given id
# is complete and does not need to be translated.
return $id if ($id =~ /\D/);
return &get_table_prefix().$id;
}
###########################################
=pod
=item &check_table()
Input: table id
Checks to see if the requested table exists. Returns 0 (no), 1 (yes), or
undef (error).
=cut
###########################################
sub check_table {
my $table_id = shift;
return undef if (!defined(&connect_to_db()));
#
$table_id = &translate_id($table_id);
my @Table_list = &tables_in_db();
my $result = 0;
foreach (@Table_list) {
if ($_ eq $table_id) {
$result = 1;
last;
}
}
# If it does not exist, make sure we do not have it listed in %Tables
delete($Tables{$table_id}) if ((! $result) && exists($Tables{$table_id}));
$debugstring = "check_table returned $result for $table_id";
return $result;
}
###########################################
=pod
=item &remove_from_table()
Input: $table_id, $column, $value
Returns: the number of rows deleted. undef on error.
Executes a "delete from $tableid where $column like binary '$value'".
=cut
###########################################
sub remove_from_table {
my ($table_id,$column,$value) = @_;
return undef if (!defined(&connect_to_db()));
#
$table_id = &translate_id($table_id);
my $command = 'DELETE FROM '.$table_id.' WHERE '.$column.
" LIKE BINARY ".$dbh->quote($value);
my $sth = $dbh->prepare($command);
unless ($sth->execute()) {
$errorstring = "ERROR on execution of ".$command."\n".$sth->errstr;
return undef;
}
$debugstring = $command;
my $rows = $sth->rows;
return $rows;
}
###########################################
=pod
=item drop_table($table_id)
Issues a 'drop table if exists' command
=cut
###########################################
sub drop_table {
my ($table_id) = @_;
return undef if (!defined(&connect_to_db()));
#
$table_id = &translate_id($table_id);
my $command = 'DROP TABLE IF EXISTS '.$table_id;
my $sth = $dbh->prepare($command);
$sth->execute();
if ($sth->err) {
$errorstring = "ERROR on execution of ".$command."\n".$sth->errstr;
return undef;
}
$debugstring = $command;
delete($Tables{$table_id}); # remove any knowledge of the table
return 1; # if we got here there was no error, so return a 'true' value
}
##########################################
=pod
=item fix_table_name
Fixes a table name so that it will work with MySQL.
=cut
##########################################
sub fix_table_name {
my ($name) = @_;
$name =~ s/^(\d+[eE]\d+)/_$1/;
return $name;
}
# ---------------------------- convert 'time' format into a datetime sql format
sub sqltime {
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(&unsqltime($_[0]));
$mon++; $year+=1900;
return "$year-$mon-$mday $hour:$min:$sec";
}
sub maketime {
my %th=@_;
return POSIX::mktime(($th{'seconds'},$th{'minutes'},$th{'hours'},
$th{'day'},$th{'month'}-1,
$th{'year'}-1900,0,0,$th{'dlsav'}));
}
#########################################
#
# Retro-fixing of un-backward-compatible time format
sub unsqltime {
my $timestamp=shift;
if ($timestamp=~/^(\d+)\-(\d+)\-(\d+)\s+(\d+)\:(\d+)\:(\d+)$/) {
$timestamp=&maketime('year'=>$1,'month'=>$2,'day'=>$3,
'hours'=>$4,'minutes'=>$5,'seconds'=>$6);
}
return $timestamp;
}
1;
__END__;
=pod
=back
=cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>