[system] / branches / rel-2-2-dev / webwork2 / lib / WeBWorK / DB / Schema / SQL.pm Repository:
ViewVC logotype

View of /branches/rel-2-2-dev/webwork2/lib/WeBWorK/DB/Schema/SQL.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4123 - (download) (as text) (annotate)
Wed Jun 7 19:32:38 2006 UTC (6 years, 11 months ago) by sh002i
File size: 11180 byte(s)
backport (sh002i): replace "SELECT *" with explicit list of fields to
avoid problems when columns are ordered differently in the database.
fixes bug #1033.

    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader$
    5 #
    6 # This program is free software; you can redistribute it and/or modify it under
    7 # the terms of either: (a) the GNU General Public License as published by the
    8 # Free Software Foundation; either version 2, or (at your option) any later
    9 # version, or (b) the "Artistic License" which comes with this package.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::DB::Schema::SQL;
   18 use base qw(WeBWorK::DB::Schema);
   19 
   20 =head1 NAME
   21 
   22 WeBWorK::DB::Schema::SQL - support SQL access to all tables.
   23 
   24 =cut
   25 
   26 use strict;
   27 use warnings;
   28 use Carp qw(croak);
   29 use WeBWorK::Debug;
   30 
   31 use constant TABLES => qw(*);
   32 use constant STYLE  => "dbi";
   33 
   34 {
   35   no warnings 'redefine';
   36 
   37   sub debug {
   38     my ($self, @string) = @_;
   39     WeBWorK::Debug::debug(@string) if $self->{params}->{debug};
   40   }
   41 }
   42 
   43 =head1 SUPPORTED PARAMS
   44 
   45 This schema pays attention to the following items in the C<params> entry.
   46 
   47 =over
   48 
   49 =item tableOverride
   50 
   51 Alternate name for this table, to satisfy SQL naming requirements.
   52 
   53 =item fieldOverride
   54 
   55 A reference to a hash mapping field names to alternate names, to satisfy SQL
   56 naming requirements.
   57 
   58 =back
   59 
   60 =cut
   61 
   62 ################################################################################
   63 # constructor for SQL-specific behavior
   64 ################################################################################
   65 
   66 sub new {
   67   my ($proto, $db, $driver, $table, $record, $params) = @_;
   68   my $self = $proto->SUPER::new($db, $driver, $table, $record, $params);
   69 
   70   ## override table name if tableOverride param is given
   71   #$self->{table} = $params->{tableOverride} if $params->{tableOverride};
   72 
   73   # add sqlTable field
   74   $self->{sqlTable} = $params->{tableOverride} || $self->{table};
   75 
   76   return $self;
   77 }
   78 
   79 ################################################################################
   80 # table access functions
   81 ################################################################################
   82 
   83 sub count {
   84   my ($self, @keyparts) = @_;
   85 
   86   my $table = $self->{table};
   87   my $sqlTable = $self->{sqlTable};
   88   my @keynames = $self->sqlKeynames();
   89 
   90   croak "too many keyparts for table $table (need at most: @keynames)"
   91     if @keyparts > @keynames;
   92 
   93   my ($where, @where_args) = $self->makeWhereClause(@keyparts);
   94 
   95   my $stmt = "SELECT COUNT(*) FROM `$sqlTable` $where";
   96   $self->debug("SQL-count: $stmt\n");
   97 
   98   $self->{driver}->connect("ro");
   99 
  100   my $sth = $self->{driver}->dbi()->prepare($stmt);
  101   $sth->execute(@where_args);
  102   my ($result) = $sth->fetchrow_array;
  103 
  104   $self->{driver}->disconnect();
  105 
  106   return $result;
  107 }
  108 
  109 sub list($@) {
  110   my ($self, @keyparts) = @_;
  111 
  112   my $table = $self->{table};
  113   my $sqlTable = $self->{sqlTable};
  114   my @keynames = $self->sqlKeynames();
  115   my $keynames = join(", ", @keynames);
  116 
  117   croak "too many keyparts for table $table (need at most: @keynames)"
  118     if @keyparts > @keynames;
  119 
  120   my ($where, @where_args) = $self->makeWhereClause(@keyparts);
  121 
  122   my $stmt = "SELECT $keynames FROM `$sqlTable` $where";
  123   $self->debug("SQL-list: $stmt\n");
  124 
  125   $self->{driver}->connect("ro");
  126 
  127   my $sth = $self->{driver}->dbi()->prepare($stmt);
  128   $sth->execute(@where_args);
  129   my $result = $sth->fetchall_arrayref;
  130 
  131   $self->{driver}->disconnect();
  132 
  133   croak "failed to SELECT: $DBI::errstr" unless defined $result;
  134   return @$result;
  135 }
  136 
  137 sub exists($@) {
  138   my ($self, @keyparts) = @_;
  139 
  140   my $table = $self->{table};
  141   my $sqlTable = $self->{sqlTable};
  142   my @keynames = $self->sqlKeynames();
  143 
  144   croak "wrong number of keyparts for table $table (needs: @keynames)"
  145     unless @keyparts == @keynames;
  146 
  147   my ($where, @where_args) = $self->makeWhereClause(@keyparts);
  148 
  149   my $stmt = "SELECT COUNT(*) FROM `$sqlTable` $where";
  150   $self->debug("SQL-exists: $stmt\n");
  151 
  152   $self->{driver}->connect("ro");
  153 
  154   my $sth = $self->{driver}->dbi()->prepare($stmt);
  155   $sth->execute(@where_args);
  156   my ($result) = $sth->fetchrow_array;
  157 
  158   $self->{driver}->disconnect();
  159 
  160   croak "failed to SELECT: $DBI::errstr" unless defined $result;
  161   return $result > 0;
  162 }
  163 
  164 sub add($$) {
  165   my ($self, $Record) = @_;
  166 
  167   my @realKeynames = $self->{record}->KEYFIELDS();
  168   my @keyparts = map { $Record->$_() } @realKeynames;
  169   croak "(" . join(", ", @keyparts) . "): exists (use put)"
  170     if $self->exists(@keyparts);
  171 
  172   my $table = $self->{table};
  173   my $sqlTable = $self->{sqlTable};
  174   my @fieldnames = $self->sqlFieldnames();
  175   my $fieldnames = join(", ", @fieldnames);
  176   my $marks = join(", ", map { "?" } @fieldnames);
  177 
  178   my @realFieldnames = $self->{record}->FIELDS();
  179   my @fieldvalues = map { $Record->$_() } @realFieldnames;
  180   @fieldvalues = map { (defined($_) and $_ eq "") ? undef : $_ } @fieldvalues;
  181 
  182   my $stmt = "INSERT INTO `$sqlTable` ($fieldnames) VALUES ($marks)";
  183   $self->debug("SQL-add: $stmt\n");
  184 
  185   $self->{driver}->connect("rw");
  186   my $sth = $self->{driver}->dbi()->prepare($stmt);
  187   my $result = $sth->execute(@fieldvalues);
  188   $self->{driver}->disconnect();
  189 
  190   unless (defined $result) {
  191     my @realKeynames = $self->{record}->KEYFIELDS();
  192     my @keyvalues = map { $Record->$_() } @realKeynames;
  193     croak "(" . join(", ", @keyvalues) . "): failed to INSERT: $DBI::errstr";
  194   }
  195 
  196   return 1;
  197 }
  198 
  199 sub get($@) {
  200   my ($self, @keyparts) = @_;
  201 
  202   return ($self->gets(\@keyparts))[0];
  203 }
  204 
  205 sub gets($@) {
  206   my ($self, @keypartsRefList) = @_;
  207 
  208   my $table = $self->{table};
  209   my $sqlTable = $self->{sqlTable};
  210   my @keynames = $self->sqlKeynames();
  211 
  212   my @records;
  213   $self->{driver}->connect("ro");
  214   foreach my $keypartsRef (@keypartsRefList) {
  215     my @keyparts = @$keypartsRef;
  216 
  217     croak "wrong number of keyparts for table $table (needs: @keynames)"
  218       unless @keyparts == @keynames;
  219 
  220     my ($where, @where_args) = $self->makeWhereClause(@keyparts);
  221 
  222     my $fieldnames = join(", ", $self->sqlFieldnames);
  223     my $stmt = "SELECT $fieldnames FROM `$sqlTable` $where";
  224     $self->debug("SQL-gets: $stmt\n");
  225 
  226     my $sth = $self->{driver}->dbi()->prepare($stmt);
  227     $sth->execute(@where_args);
  228     my $result = $sth->fetchrow_arrayref;
  229 
  230     if (defined $result) {
  231       my @record = @$result;
  232       my $Record = $self->{record}->new();
  233       my @realFieldnames = $self->{record}->FIELDS();
  234       foreach (@realFieldnames) {
  235         my $value = shift @record;
  236         $value = "" unless defined $value; # promote undef to ""
  237         $Record->$_($value);
  238       }
  239       push @records, $Record;
  240     } else {
  241       push @records, undef;
  242     }
  243   }
  244   $self->{driver}->disconnect();
  245 
  246   return @records;
  247 }
  248 
  249 # getAll($userID, $setID)
  250 #
  251 # Returns all problems in a given set. Only supported for the problem and
  252 # problem_user tables.
  253 
  254 sub getAll {
  255   my ($self, @keyparts) = @_;
  256   my $table = $self->{table};
  257   my $sqlTable = $self->{sqlTable};
  258 
  259   croak "getAll: only supported for the problem_user table"
  260     unless $table eq "problem" or $table eq "problem_user";
  261 
  262   my @keynames = $self->sqlKeynames();
  263   pop @keynames; # get rid of problem_id
  264 
  265   my ($where, @where_args) = $self->makeWhereClause(@keyparts);
  266 
  267   my $fieldnames = join(", ", $self->sqlFieldnames);
  268   my $stmt = "SELECT $fieldnames FROM `$sqlTable` $where";
  269   $self->debug("SQL-getAll: $stmt\n");
  270 
  271   my @records;
  272 
  273   $self->{driver}->connect("ro");
  274 
  275   my $sth = $self->{driver}->dbi()->prepare($stmt);
  276   $sth->execute(@where_args);
  277   my $results = $sth->fetchall_arrayref;
  278 
  279   foreach my $result (@$results) {
  280     if (defined $result) {
  281       my @record = @$result;
  282       my $Record = $self->{record}->new();
  283       my @realFieldnames = $self->{record}->FIELDS();
  284       foreach (@realFieldnames) {
  285         my $value = shift @record;
  286         $value = "" unless defined $value; # promote undef to ""
  287         $Record->$_($value);
  288       }
  289       push @records, $Record;
  290     }
  291   }
  292   $self->{driver}->disconnect();
  293 
  294   return @records;
  295 }
  296 
  297 sub put($$) {
  298   my ($self, $Record) = @_;
  299 
  300   my @realKeynames = $self->{record}->KEYFIELDS();
  301   my @keyparts = map { $Record->$_() } @realKeynames;
  302   croak "(" . join(", ", @keyparts) . "): not found (use add)"
  303     unless $self->exists(@keyparts);
  304 
  305   my $table = $self->{table};
  306   my $sqlTable = $self->{sqlTable};
  307   my @fieldnames = $self->sqlFieldnames();
  308   my $fieldnames = join(", ", @fieldnames);
  309   my $marks = join(", ", map { "?" } @fieldnames);
  310 
  311   my @realFieldnames = $self->{record}->FIELDS();
  312   my @fieldvalues = map { $Record->$_() } @realFieldnames;
  313   @fieldvalues = map { (defined($_) and $_ eq "") ? undef : $_ } @fieldvalues;
  314 
  315   my ($where, @where_args) = $self->makeWhereClause(map { $Record->$_() } @realKeynames);
  316 
  317   my $stmt = "UPDATE `$sqlTable` SET";
  318   while (@fieldnames) {
  319     $stmt .= " " . (shift @fieldnames) . "=?";
  320     $stmt .= "," if @fieldnames;
  321   }
  322   $stmt .= " $where";
  323   $self->debug("SQL-put: $stmt\n");
  324 
  325   $self->{driver}->connect("rw");
  326   my $sth = $self->{driver}->dbi()->prepare($stmt);
  327   my $result = $sth->execute(@fieldvalues, @where_args);
  328   $self->{driver}->disconnect();
  329 
  330   unless (defined $result) {
  331     croak "(" . join(", ", @keyparts) . "): failed to UPDATE: $DBI::errstr";
  332   }
  333 
  334   return 1;
  335 }
  336 
  337 sub delete($@) {
  338   my ($self, @keyparts) = @_;
  339 
  340   return 0 unless $self->exists(@keyparts);
  341 
  342   my $table = $self->{table};
  343   my $sqlTable = $self->{sqlTable};
  344   my @keynames = $self->sqlKeynames();
  345 
  346   croak "wrong number of keyparts for table $table (needs: @keynames)"
  347     unless @keyparts == @keynames;
  348 
  349   my ($where, @where_args) = $self->makeWhereClause(@keyparts);
  350 
  351   my $stmt = "DELETE FROM `$sqlTable` $where";
  352   $self->debug("SQL-delete: $stmt\n");
  353 
  354   $self->{driver}->connect("rw");
  355 
  356   my $sth = $self->{driver}->dbi()->prepare($stmt);
  357   my $result = $sth->execute(@where_args);
  358 
  359   $self->{driver}->disconnect();
  360   croak "failed to DELETE: $DBI::errstr" unless defined $result;
  361 
  362   return $result;
  363 }
  364 
  365 ################################################################################
  366 # utility functions
  367 ################################################################################
  368 
  369 sub makeWhereClause($@) {
  370   my ($self, @keyparts) = @_;
  371 
  372   my @keynames = $self->sqlKeynames();
  373 
  374   my $where = "";
  375   my @used_keyparts;
  376 
  377   my $first = 1;
  378   while (@keyparts) {
  379     my $name = shift @keynames;
  380     my $part = shift @keyparts;
  381 
  382     next unless defined $part;
  383 
  384     $where .= " AND" unless $first;
  385 #   $where .= " BINARY $name=?";
  386     $where .= " $name=?";   ## Make lookups case insensitive.  Otherwise
  387                 ## indices seem not to be used which slows things
  388                 ## down drastically.  See
  389                 ## openwebwork-devel@lists.sourceforge.net discussion
  390     push @used_keyparts, $part;
  391 
  392     $first = 0;
  393   }
  394 
  395   my $clause = $where ? "WHERE$where" : "";
  396 
  397   return ($clause, @used_keyparts);
  398 }
  399 
  400 sub sqlKeynames($) {
  401   my ($self) = @_;
  402   my @keynames = $self->{record}->KEYFIELDS();
  403   return map { "`$_`" } map { $self->{params}->{fieldOverride}->{$_} || $_ } @keynames;
  404 }
  405 
  406 sub sqlFieldnames($) {
  407   my ($self) = @_;
  408   my @keynames = $self->{record}->FIELDS();
  409   return map { "`$_`" } map { $self->{params}->{fieldOverride}->{$_} || $_ } @keynames;
  410 }
  411 
  412 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9