Diff for /loncom/interface/lonmysql.pm between versions 1.9 and 1.30

version 1.9, 2003/03/13 19:08:52 version 1.30, 2005/04/11 15:33:46
Line 31  package Apache::lonmysql; Line 31  package Apache::lonmysql;
   
 use strict;  use strict;
 use DBI;  use DBI;
 use Apache::lonnet();  use POSIX qw(strftime mktime);
   use Apache::lonnet;
   
   my $mysqluser;
   my $mysqlpassword;
   my $mysqldatabase;
   
   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;
       }
   }
   
 ######################################################################  ######################################################################
 ######################################################################  ######################################################################
Line 58  Each table has a numeric ID that is a pa Line 90  Each table has a numeric ID that is a pa
 The table id is returned by &create_table.    The table id is returned by &create_table.  
 If you lose the table id, it is lost forever.  If you lose the table id, it is lost forever.
 The table names in MySQL correspond to   The table names in MySQL correspond to 
 $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.$table_id.  If the table id   $env{'user.name'}.'_'.$env{'user.domain'}.'_'.$table_id.  If the table id 
 is non-numeric, it is assumed to be the full name of a table.  If you pass  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  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.  numeric, otherwise you are opening up all the tables in the MySQL database.
Line 328  connection is established. Line 360  connection is established.
 ###############################  ###############################
 sub connect_to_db {   sub connect_to_db { 
     return 1 if ($dbh);      return 1 if ($dbh);
     if (! ($dbh = DBI->connect("DBI:mysql:loncapa","www",      if (! defined($mysqluser) || ! defined($mysqlpassword)) {
                                $Apache::lonnet::perlvar{'lonSqlAccess'},          &set_mysql_user_and_password();
       }
       if (! ($dbh = DBI->connect("DBI:mysql:$mysqldatabase",$mysqluser,$mysqlpassword,
                                { RaiseError=>0,PrintError=>0}))) {                                 { RaiseError=>0,PrintError=>0}))) {
         $debugstring = "Unable to connect to loncapa database.";              $debugstring = "Unable to connect to loncapa database.";    
         if (! defined($dbh)) {          if (! defined($dbh)) {
Line 348  sub connect_to_db { Line 382  sub connect_to_db {
   
 =pod  =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()  =item &disconnect_from_db()
   
 Inputs: none.  Inputs: none.
Line 396  sub number_of_rows { Line 460  sub number_of_rows {
     return undef if (! defined(&update_table_info($table_id)));      return undef if (! defined(&update_table_info($table_id)));
     return $Tables{&translate_id($table_id)}->{'Rows'};      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()));
       &Apache::lonnet::logthis("reconnect set to ".$dbh->{mysql_auto_reconnect});
       return $dbh;
   }
   
 ###############################  ###############################
   
Line 486  sub update_table_info { Line 571  sub update_table_info {
     #      #
     my @info=$sth->fetchrow_array;      my @info=$sth->fetchrow_array;
     for (my $i=0;$i<= $#info ; $i++) {      for (my $i=0;$i<= $#info ; $i++) {
         $Tables{$tablename}->{$tabledesc[$i]}= $info[$i];          if ($tabledesc[$i] !~ /^(Create_|Update_|Check_)time$/) {
               $Tables{$tablename}->{$tabledesc[$i]}= 
                   &unsqltime($info[$i]);
           } else {
               $Tables{$tablename}->{$tabledesc[$i]}= $info[$i];
           }
     }      }
     #      #
     # Determine the column order      # Determine the column order
Line 515  sub update_table_info { Line 605  sub update_table_info {
   
 =pod  =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()  =item &create_table()
   
 Inputs:   Inputs: 
     table description      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 formats:  Input: table description
   
     table description = {      table description = {
         permanent  => 'yes' or 'no',          permanent  => 'yes' or 'no',
Line 539  Input formats: Line 705  Input formats:
                   },                    },
         ],          ],
         'PRIMARY KEY' => (index_col_name,...),          'PRIMARY KEY' => (index_col_name,...),
          KEY => { name => 'idx_name',            KEY => [{ name => 'idx_name', 
                   columns => (col1,col2,..),},                    columns => (col1,col2,..),},],
          INDEX => { name => 'idx_name',            INDEX => [{ name => 'idx_name', 
                     columns => (col1,col2,..),},                      columns => (col1,col2,..),},],
          UNIQUE => { index => 'yes',           UNIQUE => [{ index => 'yes',
                      name => 'idx_name',                       name => 'idx_name',
                      columns => (col1,col2,..),},                       columns => (col1,col2,..),},],
          FULLTEXT => { index => 'yes',           FULLTEXT => [{ index => 'yes',
                        name => 'idx_name',                         name => 'idx_name',
                        columns => (col1,col2,..),},                         columns => (col1,col2,..),},],
   
     }      }
   
 Returns:  Returns: scalar string containing mysql commands to create the table
     undef on error, table id on success.  
   
 =cut  =cut
   
 ###############################  ###############################
 sub create_table {  sub build_table_creation_request {
     return undef if (!defined(&connect_to_db($dbh)));  
     my ($table_des)=@_;      my ($table_des)=@_;
     #      #
     # Build request to create table      # Build request to create table
Line 606  sub create_table { Line 770  sub create_table {
         push (@Columns,'PRIMARY KEY ('.join(',',@{$table_des->{'PRIMARY KEY'}})          push (@Columns,'PRIMARY KEY ('.join(',',@{$table_des->{'PRIMARY KEY'}})
               .')');                .')');
     }      }
     foreach ('KEY','INDEX') {      #
         if (exists($table_des->{$_})) {      foreach my $indextype ('KEY','INDEX') {
             my $text = $_.' ';          next if (!exists($table_des->{$indextype}));
             if (exists($table_des->{$_}->{'name'})) {          foreach my $indexdescription (@{$table_des->{$indextype}}) {
                 $text .=$table_des->{$_}->{'name'};              my $text = $indextype.' ';
               if (exists($indexdescription->{'name'})) {
                   $text .=$indexdescription->{'name'};
             }              }
             $text .= ' ('.join(',',@{$table_des->{$_}->{'columns'}}).')';              $text .= ' ('.join(',',@{$indexdescription->{'columns'}}).')';
             push (@Columns,$text);              push (@Columns,$text);
         }          }
     }      }
     foreach ('UNIQUE','FULLTEXT') {      #
         if (exists($table_des->{$_})) {      foreach my $indextype ('UNIQUE','FULLTEXT') {
             my $text = $_.' ';          next if (! exists($table_des->{$indextype}));
             if (exists($table_des->{$_}->{'index'}) &&          foreach my $indexdescription (@{$table_des->{$indextype}}) {
                 $table_des->{$_}->{'index'} eq 'yes') {              my $text = $indextype.' ';
               if (exists($indexdescription->{'index'}) &&
                   $indexdescription->{'index'} eq 'yes') {
                 $text .= 'INDEX ';                  $text .= 'INDEX ';
             }              }
             if (exists($table_des->{$_}->{'name'})) {              if (exists($indexdescription->{'name'})) {
                 $text .=$table_des->{$_}->{'name'};                  $text .=$indexdescription->{'name'};
             }              }
             $text .= ' ('.join(',',@{$table_des->{$_}->{'columns'}}).')';              $text .= ' ('.join(',',@{$indexdescription->{'columns'}}).')';
             push (@Columns,$text);              push (@Columns,$text);
         }          }
     }      }
       #
     $request .= "(".join(", ",@Columns).") ";      $request .= "(".join(", ",@Columns).") ";
     unless($table_des->{'permanent'} eq 'yes') {      unless($table_des->{'permanent'} eq 'yes') {
         $request.="COMMENT = 'temporary' ";          $request.="COMMENT = 'temporary' ";
     }       } 
     $request .= "TYPE=MYISAM";      $request .= "TYPE=MYISAM";
     #      return $request,$table_id;
     # Execute the request to create the table  
     #############################################  
     my $count = $dbh->do($request);  
     if (! defined($count)) {  
         $errorstring = "$dbh ATTEMPTED:\n".$request."\nRESULTING ERROR:\n";  
         return undef;  
     }  
     #  
     # Set up the internal bookkeeping  
     #############################################  
     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;  
 }  }
   
 ###############################  ###############################
Line 668  sub get_new_table_id { Line 822  sub get_new_table_id {
     my $newid = 0;      my $newid = 0;
     my @tables = &tables_in_db();      my @tables = &tables_in_db();
     foreach (@tables) {      foreach (@tables) {
         if (/^$ENV{'user.name'}_$ENV{'user.domain'}_(\d+)$/) {          if (/^$env{'user.name'}_$env{'user.domain'}_(\d+)$/) {
             $newid = $1 if ($1 > $newid);              $newid = $1 if ($1 > $newid);
         }          }
     }      }
Line 781  sub store_row { Line 935  sub store_row {
     return 1;      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/,$//;
       $dbh->do($request);
       if ($dbh->err) {
           $errorstring = 'Attempted '.$/.$request.$/.'Got error '.$dbh->errstr();
           return undef;
       }
       return 1;
   }
   
   
 ###############################  ###############################
   
 =pod  =pod
Line 858  sub tables_in_db { Line 1064  sub tables_in_db {
     return undef if (!defined(&connect_to_db()));      return undef if (!defined(&connect_to_db()));
     my $sth=$dbh->prepare('SHOW TABLES');      my $sth=$dbh->prepare('SHOW TABLES');
     $sth->execute();      $sth->execute();
     if ($sth->err) {      $sth->execute();
         $errorstring = "$dbh ATTEMPTED:\n".'SHOW TABLES'.      my $aref = $sth->fetchall_arrayref;
       if ($sth->err()) {
           $errorstring = 
               "$dbh ATTEMPTED:\n".'fetchall_arrayref after SHOW TABLES'.
             "\nRESULTING ERROR:\n".$sth->errstr;              "\nRESULTING ERROR:\n".$sth->errstr;
         return undef;          return undef;
     }      }
     my $aref = $sth->fetchall_arrayref;      my @table_list;
     my @table_list=();  
     foreach (@$aref) {      foreach (@$aref) {
         push @table_list,$_->[0];          push(@table_list,$_->[0]);
     }      }
     $debugstring = "Got list of tables in DB: @table_list";      $debugstring = "Got list of tables in DB: ".join(',',@table_list);
     return @table_list;      return(@table_list);
 }  }
   
 ###########################################  ###########################################
Line 892  sub translate_id { Line 1100  sub translate_id {
     # id should be a digit.  If it is not a digit we assume the given id      # 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.      # is complete and does not need to be translated.
     return $id if ($id =~ /\D/);        return $id if ($id =~ /\D/);  
     return $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.$id;      return $env{'user.name'}.'_'.$env{'user.domain'}.'_'.$id;
 }  }
   
 ###########################################  ###########################################
Line 948  sub remove_from_table { Line 1156  sub remove_from_table {
     return undef if (!defined(&connect_to_db()));      return undef if (!defined(&connect_to_db()));
     #      #
     $table_id = &translate_id($table_id);      $table_id = &translate_id($table_id);
     my $command = 'DELETE FROM '.$table_id.' WHERE '.$dbh->quote($column).      my $command = 'DELETE FROM '.$table_id.' WHERE '.$column.
         " LIKE BINARY ".$dbh->quote($value);          " LIKE BINARY ".$dbh->quote($value);
     my $sth = $dbh->prepare($command);       my $sth = $dbh->prepare($command); 
     $sth->execute();      unless ($sth->execute()) {
     if ($sth->err) {  
         $errorstring = "ERROR on execution of ".$command."\n".$sth->errstr;          $errorstring = "ERROR on execution of ".$command."\n".$sth->errstr;
         return undef;          return undef;
     }      }
       $debugstring = $command;
     my $rows = $sth->rows;      my $rows = $sth->rows;
     return $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;  1;
   

Removed from v.1.9  
changed lines
  Added in v.1.30


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>