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

version 1.1, 2002/07/26 16:22:24 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 71  To create a table, you need a descriptio Line 103  To create a table, you need a descriptio
 for &create_table for a description of what is needed.  for &create_table for a description of what is needed.
   
  $table_id = &create_table({    $table_id = &create_table({ 
             columns => {       id      => 'tableid',      # usually you will use the returned id
                 id => {       columns => (
                     type => 'INT',                   { name => 'id',
                     restrictions => 'NOT NULL',                     type => 'INT',
                     primary_key => 'yes',                     restrictions => 'NOT NULL',
                     auto_inc    => 'yes'                     primary_key => 'yes',
                     }                     auto_inc    => 'yes'
                 verbage => { type => 'TEXT' },                     },
                 idx_verbage => { type => 'FULLTEXT',                   { name => 'verbage',
                                  target => 'verbage'                     type => 'TEXT' },
                                  }                   ),
             },                         fulltext => [qw/verbage/],
             column_order => [qw/id verbage idx_verbage/]          });
             });  
   
 The above command will create a table with two columns, 'id' and 'verbage'.  The above command will create a table with two columns, 'id' and 'verbage'.
   
Line 94  The above command will create a table wi Line 125  The above command will create a table wi
 text string to be stored.  Depending on your intentions for this database,  text string to be stored.  Depending on your intentions for this database,
 setting restrictions => 'NOT NULL' may help you avoid storing empty data.  setting restrictions => 'NOT NULL' may help you avoid storing empty data.
   
 'idx_verbage' sets up the 'verbage' column for 'FULLTEXT' searching.  the fulltext element sets up the 'verbage' column for 'FULLTEXT' searching.
   
   
   
Line 120  Since the above table was created with t Line 151  Since the above table was created with t
 autoincrement, providing a value is unnecessary even though the column was  autoincrement, providing a value is unnecessary even though the column was
 marked as 'NOT NULL'.  marked as 'NOT NULL'.
   
 In the future an array of arrays or hashes may be supported, but currently  
 the system only performs one insert at a time.  Given the nature of this   
 interface, transactions (locking of the table) are not supported.  
   
   
   
 =item Retrieving rows  =item Retrieving rows
Line 168  The following entries are allowed in the Line 195  The following entries are allowed in the
   
 =over 4  =over 4
   
 =item columns   =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. 
   
 The columns information required by &create_table.  =item Avg_row_length
   
 =item column_order  The average length of the rows.
   
 Reference to an array containing the order of columns in the table.  =item Data_length
   
 =item table_info  The length of the data stored in the table (bytes)
   
 Set to the results of &get_table_info.  =item Max_data_length
   
 =item row_insert_sth  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.  The statement handler for row inserts.
   
 =back  =back
   
   Col_order and row_insert_sth are kept internally by lonmysql and are not
   part of the usual MySQL table information.
   
 =cut  =cut
   
 ##################################################  ##################################################
Line 274  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 ($dbh->err) {          if (! defined($dbh)) {
               $debugstring = "Unable to connect to loncapa database.";
               $errorstring = "dbh was undefined.";
           } elsif ($dbh->err) {
             $errorstring = "Connection error: ".$dbh->errstr;              $errorstring = "Connection error: ".$dbh->errstr;
         }          }
         return undef;          return undef;
     }      }
     # The code below will let us switch to a different database.  
     # my $db_command = "USE $db;";  
     # my $sth = $dbh->prepare($db_command);  
     # $sth->execute();  
     # if ($sth->err) {  
     #     # Unable to use the database.  Interesting...  
     #     $dbh->disconnect;  
     #     return undef;  
     # }  
     $debugstring = "Successfully connected to loncapa database.";          $debugstring = "Successfully connected to loncapa database.";    
     return 1;      return 1;
 }  }
Line 300  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 319  sub disconnect_from_db { Line 431  sub disconnect_from_db {
         if (exists($Tables{$_}->{'row_insert_sth'})) {          if (exists($Tables{$_}->{'row_insert_sth'})) {
             delete($Tables{$_}->{'row_insert_sth'});              delete($Tables{$_}->{'row_insert_sth'});
         }          }
           if (exists($Tables{$_}->{'row_replace_sth'})) {
               delete($Tables{$_}->{'row_replace_sth'});
           }
     }      }
     $dbh->disconnect if ($dbh);      $dbh->disconnect if ($dbh);
     $debugstring = "Disconnected from database.";      $debugstring = "Disconnected from database.";
Line 330  sub disconnect_from_db { Line 445  sub disconnect_from_db {
   
 =pod  =pod
   
 =item &query_table()  =item &number_of_rows()
   
   Input: table identifier
   
 Currently unimplemented.  Returns: the number of rows in the given table, undef on error.
   
 =cut  =cut
   
 ###############################  ###############################
 sub query_table {   sub number_of_rows { 
     # someday this will work.      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()));
       &Apache::lonnet::logthis("reconnect set to ".$dbh->{mysql_auto_reconnect});
       return $dbh;
   }
   
 ###############################  ###############################
   
Line 381  sub get_debug { Line 520  sub get_debug {
   
 =pod  =pod
   
 =item &get_table_info($table_id)  =item &update_table_info()
   
 Inputs: table id  Inputs: table id
   
 Returns: undef or a pointer to a hash of data about a table.  Returns: undef on error, 1 on success.
   
   &update_table_info updates the %Tables hash with current information about
   the given table.  
   
 &get_table_info returns all of the information it can about a table in the  The default MySQL table status fields are:
 form of a hash.  Currently the fields in the hash are:  
   
    Name             Type            Row_format     Name             Type            Row_format
    Max_data_length  Index_length    Data_free     Max_data_length  Index_length    Data_free
Line 396  form of a hash.  Currently the fields in Line 537  form of a hash.  Currently the fields in
    Avg_row_length   Data_length     Comment      Avg_row_length   Data_length     Comment 
    Rows             Auto_increment  Create_options     Rows             Auto_increment  Create_options
   
   Additionally, "Col_order" is updated as well.
   
 =cut  =cut
   
 ###############################  ###############################
 sub get_table_info {   sub update_table_info { 
     my ($table_id) = @_;      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);      my $tablename = &translate_id($table_id);
     return undef if (! &check_table($table_id));      #
     my %tableinfo;      # Get MySQLs table status information.
       #
     my @tabledesc = qw/      my @tabledesc = qw/
         Name Type Row_format Rows Avg_row_length Data_length          Name Type Row_format Rows Avg_row_length Data_length
             Max_data_length Index_length Data_free Auto_increment               Max_data_length Index_length Data_free Auto_increment 
Line 412  sub get_table_info { Line 563  sub get_table_info {
     my $sth = $dbh->prepare($db_command);      my $sth = $dbh->prepare($db_command);
     $sth->execute();      $sth->execute();
     if ($sth->err) {      if ($sth->err) {
         # Unable to use the database.  Interesting...  
         $errorstring = "$dbh ATTEMPTED:\n".$db_command."\nRESULTING ERROR:\n".          $errorstring = "$dbh ATTEMPTED:\n".$db_command."\nRESULTING ERROR:\n".
             $sth->errstr;              $sth->errstr;
         $dbh->disconnect;          &disconnect_from_db();
         return undef;          return undef;
     }      }
     #      #
     my @info=$sth->fetchrow_array;      my @info=$sth->fetchrow_array;
     for (my $i=0;$i<= $#info ; $i++) {      for (my $i=0;$i<= $#info ; $i++) {
         $tableinfo{$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
       #
       $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";      $debugstring = "Retrieved table info for $tablename";
     return \%tableinfo;      return 1;
 }  }
   
 ###############################  ###############################
   
 =pod  =pod
   
 =item &create_table  =item &table_information()
   
 Inputs:   Inputs: table id
     table description  
   
 Input formats:  Returns: hash with the table status
   
     table description = {  =cut
         permanent  => 'yes' or 'no',  
         columns => {  ###############################
             colA => {  sub table_information {
                 type         => mysql type,      my $table_id=shift;
                 restrictions => 'NOT NULL' or empty,      if (&update_table_info($table_id)) {
                 primary_key  => 'yes' or empty,   return %{$Tables{$table_id}};
                 auto_inc     => 'yes' or empty,      } else {
                 target       => 'colB' (only if type eq 'FULLTEXT'),   return ();
             }  
             colB  => { .. }  
             colZ  => { .. }  
         },  
         column_order => [ colA, colB, ..., colZ],  
     }      }
   }
   
   ###############################
   
   =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:  Returns:
     undef on error, table id on success.      undef on error, table id on success.
   
Line 462  Returns: Line 660  Returns:
   
 ###############################  ###############################
 sub create_table {  sub create_table {
     return undef if (!&connect_to_db($dbh));      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)=@_;      my ($table_des)=@_;
     #      #
     # Build request to create table      # Build request to create table
     ##################################      ##################################
     my @Columns;      my @Columns;
     my $col_des;      my $col_des;
     my $tableid = &get_new_table_id();      my $table_id;
     my $tablename = &translate_id($tableid);      if (exists($table_des->{'id'})) {
           $table_id = $table_des->{'id'};
       } else {
           $table_id = &get_new_table_id();
       }
       my $tablename = &translate_id($table_id);
     my $request = "CREATE TABLE IF NOT EXISTS ".$tablename." ";      my $request = "CREATE TABLE IF NOT EXISTS ".$tablename." ";
     foreach my $column (@{$table_des->{'column_order'}}) {      foreach my $coldata (@{$table_des->{'columns'}}) {
           my $column = $coldata->{'name'};
           next if (! defined($column));
         $col_des = '';          $col_des = '';
         my $coldata = $table_des->{'columns'}->{$column};          if (lc($coldata->{'type'}) =~ /(enum|set)/) { # 'enum' or 'set'
         if (lc($coldata->{'type'}) eq 'fulltext') {  
             $col_des.='FULLTEXT '.$column." (".$coldata->{'target'}.")";  
             next; # Skip to the continue block and store the column data  
         } elsif (lc($coldata->{'type'}) =~ /(enum|set)/) { # 'enum' or 'set'  
             $col_des.=$column." ".$coldata->{'type'}."('".              $col_des.=$column." ".$coldata->{'type'}."('".
                 join("', '",@{$coldata->{'values'}})."')";                  join("', '",@{$coldata->{'values'}})."')";
         } else {          } else {
Line 494  sub create_table { Line 758  sub create_table {
         if (exists($coldata->{'default'})) {          if (exists($coldata->{'default'})) {
             $col_des.=" DEFAULT '".$coldata->{'default'}."'";              $col_des.=" DEFAULT '".$coldata->{'default'}."'";
         }          }
         $col_des.=' AUTO_INCREMENT' if (exists($coldata->{'auto_inc'}));          $col_des.=' AUTO_INCREMENT' if (exists($coldata->{'auto_inc'}) &&
         $col_des.=' PRIMARY KEY'    if (exists($coldata->{'primary_key'}));                                          ($coldata->{'auto_inc'} eq 'yes'));
           $col_des.=' PRIMARY KEY'    if (exists($coldata->{'primary_key'}) &&
                                           ($coldata->{'primary_key'} eq 'yes'));
     } continue {      } continue {
         # skip blank items.          # skip blank items.
         push (@Columns,$col_des) if ($col_des ne '');          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).") ";      $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}));  
     my @column_order_copy = @{$table_des->{'column_order'}};  
     $Tables{$tablename} = {   
         columns      => $table_des->{'columns'},  
         column_order => $table_des->{'column_order'},  
         table_info   => &get_table_info($tableid),  
     };  
     $debugstring = "$dbh Created table $tablename at time ".time.  
         " with request\n$request";  
     return $tableid;  
 }  }
   
 ###############################  ###############################
   
 =pod  =pod
   
 =item &get_new_table_id  =item &get_new_table_id()
   
 Used internally to prevent table name collisions.  Used internally to prevent table name collisions.
   
Line 541  Used internally to prevent table name co Line 820  Used internally to prevent table name co
 ###############################  ###############################
 sub get_new_table_id {  sub get_new_table_id {
     my $newid = 0;      my $newid = 0;
     my $name_regex = '^'.$ENV{'user.name'}.'_'.$ENV{'user.domain'}."_(\d+)\$";  
     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 555  sub get_new_table_id { Line 833  sub get_new_table_id {
   
 =pod  =pod
   
 =item &execute_db_command  =item &get_rows()
   
 Currently unimplemented  
   
 =cut  
   
 ###############################  
 sub execute_db_command {  
     my ($tablename,$command) = @_;  
     return 1;  
 }  
   
 ###############################  
   
 =pod  
   
 =item &get_rows  
   
 Inputs: $table_id,$condition  Inputs: $table_id,$condition
   
 Returns: undef on error, an array ref to (array of) results on success.  Returns: undef on error, an array ref to (array of) results on success.
   
 Internally, this function does a 'SELECT * FROM table HAVING $condition'.  Internally, this function does a 'SELECT * FROM table WHERE $condition'.
 $condition = 'id>0' will result in all rows where column 'id' has a value  $condition = 'id>0' will result in all rows where column 'id' has a value
 greater than 0 being returned.  greater than 0 being returned.
   
Line 586  greater than 0 being returned. Line 848  greater than 0 being returned.
 ###############################  ###############################
 sub get_rows {  sub get_rows {
     my ($table_id,$condition) = @_;      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 $tablename = &translate_id($table_id);
     my $request = 'SELECT * FROM '.$tablename.' HAVING '.$condition;      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);      my $sth=$dbh->prepare($request);
     $sth->execute();      $sth->execute();
     if ($sth->err) {      if ($sth->err) {
Line 598  sub get_rows { Line 873  sub get_rows {
     }      }
     $debugstring = "Got rows matching $condition";      $debugstring = "Got rows matching $condition";
     my @Results = @{$sth->fetchall_arrayref};      my @Results = @{$sth->fetchall_arrayref};
     foreach my $row (@Results) {  
         for(my $i=0;$i<@$row;$i++) {  
             $row->[$i]=&Apache::lonnet::unescape($row->[$i]);  
         }  
     }  
     return @Results;      return @Results;
 }  }
   
Line 610  sub get_rows { Line 880  sub get_rows {
   
 =pod  =pod
   
 =item &store_row  =item &store_row()
   
 Inputs: table id, row data  Inputs: table id, row data
   
Line 621  returns undef on error, 1 on success. Line 891  returns undef on error, 1 on success.
 ###############################  ###############################
 sub store_row {  sub store_row {
     my ($table_id,$rowdata) = @_;      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 $tablename = &translate_id($table_id);
     my $table = $Tables{$tablename};      #
     my $sth;      my $sth;
     if (exists($table->{'row_insert_sth'})) {      if (exists($Tables{$tablename}->{'row_insert_sth'})) {
         $sth = $table->{'row_insert_sth'};          $sth = $Tables{$tablename}->{'row_insert_sth'};
     } else {      } else {
         # We need to build a statement handler          # Build the insert statement handler
           return undef if (! defined(&update_table_info($table_id)));
         my $insert_request = 'INSERT INTO '.$tablename.' VALUES(';          my $insert_request = 'INSERT INTO '.$tablename.' VALUES(';
         foreach (@{$table->{'column_order'}}) {          foreach (@{$Tables{$tablename}->{'Col_order'}}) {
             # Skip the 'fulltext' columns.  
             next if (lc($table->{'columns'}->{$_}->{'type'}) eq 'fulltext');  
             $insert_request.="?,";              $insert_request.="?,";
         }          }
         chop $insert_request;          chop $insert_request;
         $insert_request.=")";          $insert_request.=")";
         $sth=$dbh->prepare($insert_request);          $sth=$dbh->prepare($insert_request);
           $Tables{$tablename}->{'row_insert_sth'}=$sth;
     }      }
     my @Parameters;       my @Parameters; 
     if (ref($rowdata) eq 'ARRAY') {      if (ref($rowdata) eq 'ARRAY') {
         @Parameters = @$rowdata;          @Parameters = @$rowdata;
     } elsif (ref($rowdata) eq 'HASH') {      } elsif (ref($rowdata) eq 'HASH') {
         foreach (@{$table->{'column_order'}}) {          foreach (@{$Tables{$tablename}->{'Col_order'}}) {
             # Is this appropriate?  Am I being presumptious? ACK!!!!!              push(@Parameters,$rowdata->{$_});
             next if (lc($table->{'columns'}->{$_}->{'type'}) eq 'fulltext');  
             push(@Parameters,&Apache::lonnet::escape($rowdata->{$_}));  
         }          }
     }       } 
     $sth->execute(@Parameters);      $sth->execute(@Parameters);
Line 658  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
   
   =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  =pod
   
 =item tables_in_db  =item &tables_in_db()
   
 Returns a list containing the names of all the tables in the database.  Returns a list containing the names of all the tables in the database.
 Returns undef on error.  Returns undef on error.
Line 671  Returns undef on error. Line 1061  Returns undef on error.
   
 ###########################################  ###########################################
 sub tables_in_db {  sub tables_in_db {
     return undef if (! &connect_to_db()); # bail out if we cannot connect      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'."\nRESULTING ERROR:\n".      my $aref = $sth->fetchall_arrayref;
             $sth->errstr;      if ($sth->err()) {
           $errorstring = 
               "$dbh ATTEMPTED:\n".'fetchall_arrayref after SHOW TABLES'.
               "\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);
 }  }
   
 ###########################################  ###########################################
   
 =pod  =pod
   
 =item &translate_id  =item &translate_id()
   
 Used internally to translate a numeric table id into a MySQL table name.  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   If the input $id contains non-numeric characters it is assumed to have 
Line 708  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;
 }  }
   
 ###########################################  ###########################################
   
 =pod  =pod
   
 =item &check_table($id)  =item &check_table()
   
   Input: table id
   
 Checks to see if the requested table exists.  Returns 0 (no), 1 (yes), or   Checks to see if the requested table exists.  Returns 0 (no), 1 (yes), or 
 undef (error).  undef (error).
Line 725  undef (error). Line 1119  undef (error).
 ###########################################  ###########################################
 sub check_table {  sub check_table {
     my $table_id = shift;      my $table_id = shift;
       return undef if (!defined(&connect_to_db()));
       #
     $table_id = &translate_id($table_id);      $table_id = &translate_id($table_id);
     return undef if (! &connect_to_db());  
     my @Table_list = &tables_in_db();      my @Table_list = &tables_in_db();
     my $result = 0;      my $result = 0;
     foreach (@Table_list) {      foreach (@Table_list) {
         if (/^$table_id$/) {          if ($_ eq $table_id) {
             $result = 1;              $result = 1;
             last;              last;
         }          }
Line 741  sub check_table { Line 1136  sub check_table {
     return $result;      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;  1;
   
 __END__;  __END__;
   
   =pod
   
   =back
   
   =cut

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


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