Example #1
0
 /**
  * Executes the query that was previously passed to the constructor.
  *
  * @param mixed  $arg      Query arguments to escape and insert at ? placeholders in $query
  * @param mixed  ...       Additional arguments
  **/
 function execute()
 {
     // Load the function arguments, minus the query itself, which we already extracted
     $args = func_get_args();
     // Split out sub-arrays, etc..
     $args = Database::smart_args($args);
     // Were enough arguments passed in?
     if (count($args) != $this->num_args_needed) {
         trigger_error('Database_Query_mysqlicompat called with ' . count($args) . " arguments, but requires {$this->num_args_needed}.", E_USER_ERROR);
     }
     // Finish any previous statements
     $this->finish();
     // Replace in the arguments
     $this->last_query = '';
     foreach ($this->query as $part) {
         $this->last_query .= $part;
         if (count($args)) {
             $arg = array_shift($args);
             $this->last_query .= is_null($arg) ? 'NULL' : "'" . mysqli_real_escape_string($this->dbh, $arg) . "'";
         }
     }
     // Perform the query
     // If we don't have a valid connection, fataly error out.
     if ($this->dbh === false) {
         $this->db->error();
         trigger_error($this->db->error, E_USER_ERROR);
     }
     $this->sh = mysqli_query($this->dbh, $this->last_query);
     // Cache  these so the warning count below doesn't interfere
     if (is_bool($this->sh)) {
         $this->insert_id = mysqli_insert_id($this->dbh);
         $this->affected_rows = mysqli_affected_rows($this->dbh);
     } else {
         $this->num_rows = mysqli_num_rows($this->sh);
     }
     // On each execute, we clear the warnings of the statement handle, so it doesn't
     // store them up
     $this->warnings = array();
     // Check the warnings and store them
     if (mysqli_warning_count($this->dbh)) {
         if ($sh = mysqli_query($this->dbh, 'SHOW WARNINGS')) {
             while ($row = mysqli_fetch_row($sh)) {
                 $this->warnings[] = array('#' => $row[1], 'MSG' => $row[2]);
             }
             mysqli_free_result($sh);
             // This is used in errors.php to output in the backtrace
             global $_DEBUG;
             $_DEBUG['Database Warnings'][] = array('Query' => $this->last_query, 'Warnings' => $this->warnings);
         }
     }
     if ($this->sh === false) {
         if ($this->db->fatal_errors) {
             trigger_error('SQL Error: ' . mysqli_error($this->dbh) . ' [#' . mysqli_errno($this->dbh) . ']', E_USER_ERROR);
         } else {
             $this->db->error();
         }
     }
 }
Example #2
0
 /**
  * Executes the query that was previously passed to the constructor.
  *
  * @param mixed  $arg      Query arguments to escape and insert at ? placeholders in $query
  * @param mixed  ...       Additional arguments
  **/
 public function execute()
 {
     // We can't very well perform a query wtihout an active connection.
     if (!(is_object($this->dbh) && $this->dbh instanceof mysqli)) {
         $this->db->error('Lost connection to database.');
         return;
     }
     // Finish any previous statements
     $this->finish();
     // Replace in the arguments.  Yes, we're doing it manually.  While MySQLi
     // has prepared statements, the binding method requires that we know the
     // type of value being passed.  We can't guess it with certainty.
     $this->last_query = $this->replaceholders(Database::smart_args(func_get_args()));
     if (!$this->last_query) {
         return;
     }
     // Wrap the actual query execution in a bit of benchmarking.
     $before = microtime(true);
     $this->sh = mysqli_query($this->dbh, $this->last_query);
     $after = microtime(true);
     $this->db->mysql_time += $after - $before;
     // Non-select statements return a boolean, which means we call affected_rows.
     if (is_bool($this->sh)) {
         $this->insert_id = mysqli_insert_id($this->dbh);
         $this->affected_rows = mysqli_affected_rows($this->dbh);
     } else {
         $this->num_rows = mysqli_num_rows($this->sh);
     }
     // Can we pull down warnings from the server?
     $this->warnings = array();
     if ($this->db->enable_warning_logging && mysqli_warning_count($this->dbh)) {
         if ($sh = mysqli_query($this->dbh, 'SHOW WARNINGS')) {
             while ($row = mysqli_fetch_row($sh)) {
                 $this->warnings[] = array('#' => $row[1], 'MSG' => $row[2]);
             }
             mysqli_free_result($sh);
             // Push it upstream.
             $GLOBALS['_DEBUG']['Database Warnings'][] = array('Query' => $this->last_query, 'Warnings' => $this->warnings);
         }
     }
     // Did it even work?
     if ($this->sh === false) {
         $this->db->error('SQL Error:');
         return false;
     }
     return true;
 }
Example #3
0
 /**
  * Executes the query that was previously passed to the constructor.
  *
  * @param mixed  $arg      Query arguments to escape and insert at ? placeholders in $query
  * @param mixed  ...       Additional arguments
  **/
 public function execute()
 {
     // We can't very well perform a query wtihout an active connection.
     if ($this->dbh === false) {
         $this->db->error('Lost connection to database.');
         return;
     }
     // Finish any previous statements
     $this->finish();
     // Replace in the arguments
     $this->last_query = $this->replaceholders(Database::smart_args(func_get_args()));
     if (!$this->last_query) {
         return;
     }
     // Wrap the actual query execution in a bit of benchmarking.
     $before = microtime(true);
     $this->sh = mysql_query($this->last_query, $this->dbh);
     $after = microtime(true);
     $this->db->mysql_time += $after - $before;
     // Non-select statements return a boolean, which means we call affected_rows.
     if (is_bool($this->sh)) {
         $this->insert_id = mysql_insert_id($this->dbh);
         $this->affected_rows = mysql_affected_rows($this->dbh);
     } else {
         $this->num_rows = mysql_num_rows($this->sh);
     }
     // Can we pull down warnings from the server?  Because the mysql interface
     // doesn't give us the warning count, we have to check ourselves.
     $this->warnings = array();
     if ($this->db->enable_warning_logging) {
         if ($sh = mysql_query($this->dbh, 'SHOW WARNINGS')) {
             while ($row = mysql_fetch_row($sh)) {
                 $this->warnings[] = array('#' => $row[1], 'MSG' => $row[2]);
             }
             mysql_free_result($sh);
             // Push it upstream.
             $GLOBALS['_DEBUG']['Database Warnings'][] = array('Query' => $this->last_query, 'Warnings' => $this->warnings);
         }
     }
     // No matter what, a false statement handle means something horrid happened.
     if ($this->sh === false) {
         $this->db->error('SQL Error:');
         return false;
     }
     return true;
 }
Example #4
0
 /**
  * Executes the query that was previously passed to the constructor.
  *
  * @param mixed  $arg      Query arguments to escape and insert at ? placeholders in $query
  * @param mixed  ...       Additional arguments
 /**/
 function execute()
 {
     // Load the function arguments, minus the query itself, which we already extracted
     $args = func_get_args();
     // Split out sub-arrays, etc..
     $args = Database::smart_args($args);
     // Were enough arguments passed in?
     if (count($args) != $this->num_args_needed) {
         trigger_error('Database_Query called with ' . count($args) . " arguments, but requires {$this->num_args_needed}.", E_USER_ERROR);
     }
     // Finish any previous statements
     $this->finish();
     // Replace in the arguments
     $this->last_query = '';
     foreach ($this->query as $part) {
         $this->last_query .= $part;
         if (count($args)) {
             $arg = array_shift($args);
             $this->last_query .= is_null($arg) ? 'NULL' : "'" . mysql_real_escape_string($arg) . "'";
         }
     }
     // Perform the query
     // If we don't have a valid connection, fataly error out.
     if ($this->dbh === false) {
         $this->db->error();
         trigger_error($this->db->error, E_USER_ERROR);
     }
     $this->sh = mysql_query($this->last_query, $this->dbh);
     // Cache these
     if (is_bool($this->sh)) {
         $this->insert_id = mysql_insert_id($this->dbh);
         $this->affected_rows = mysql_affected_rows($this->dbh);
     } else {
         $this->num_rows = mysql_num_rows($this->sh);
     }
     if ($this->sh === false) {
         if ($this->db->fatal_errors) {
             trigger_error('SQL Error: ' . mysql_error($this->dbh) . ' [#' . mysql_errno($this->dbh) . ']', E_USER_ERROR);
         } else {
             $this->db->error();
         }
     }
 }
Example #5
0
 /**
  * I like how in perl you can pass variables into functions in lists or arrays,
  * and they all show up to the function as one giant list.  This takes an array
  * containing scalars and arrays of scalars, and returns one cleanarray of all
  * values.
  *
  * @param mixed $args Scalar or nested array to be "flattened" into a single array.
  *
  * @return array      Single array comprised of all scalars present in $args.
  **/
 static function smart_args($args)
 {
     $new_args = array();
     // Not an array
     if (!is_array($args)) {
         return array($args);
     }
     // Loop
     foreach ($args as $arg) {
         if (is_array($arg)) {
             $new_args += Database::smart_args($arg);
         } else {
             $new_args[] = $arg;
         }
     }
     // Return
     return $new_args;
 }
Example #6
0
 /**
  * Executes the query that was previously passed to the constructor.
  *
  * @param mixed  $arg      Query arguments to escape and insert at ? placeholders in $query
  * @param mixed  ...       Additional arguments
  **/
 public function execute()
 {
     // We can't very well perform a query wtihout an active connection.
     if ($this->dbh === false || pg_connection_status($this->dbh) !== PGSQL_CONNECTION_OK) {
         $this->db->error('Lost connection to database.');
         return;
     }
     // Finish any previous statements
     $this->finish();
     // We need to pre-process the arguments for literals and bytefields.  We
     // can't safely pass them into the naitve placeholder system, so we'll
     // have to stick them into the query at the right places before then.
     // Additionally, PG uses the string values 't' and 'f' for boolean true
     // and false, and will choke over PHP true/false when passed in this way.
     $args = Database::smart_args(func_get_args());
     foreach ($args as $i => $arg) {
         if (is_bool($arg)) {
             $args[$i] = $arg === true ? 't' : 'f';
         }
     }
     list($query, $nargs, $nargn) = $this->reprocess_query($args);
     if (!$query) {
         return;
     }
     // Prepare the placeholders.  PG uses $1, $2 .. $n as their placeholders.
     // Continuing the example from Database_Query::reprocess_query, we should
     // have the following $query array:
     //      'SELECT * FROM table WHERE a = ',
     //      ' AND b < NOW() AND c > '
     // which will get turned into the following string:
     //      SELECT * FROM table WHERE a = $1 AND b < NOW() AND c > $2
     $this->last_query = '';
     $placeholder_count = 1;
     foreach ($query as $i => $chunk) {
         $this->last_query .= $chunk;
         if ($placeholder_count <= $nargn) {
             $this->last_query .= '$' . $placeholder_count;
             $placeholder_count++;
         }
     }
     // Wrap the actual query execution in a bit of benchmarking.
     $before = microtime(true);
     // We're only using placeholders here, not prepared statements.  Why not?
     // The possibility of missing / pre-replaced placeholders means that the
     // actual query we get to execute changes each time.
     $this->sh = false;
     $this->errno = null;
     $state = pg_send_query_params($this->dbh, $this->last_query, $nargs);
     if ($state) {
         // So, here's some fun.  Like PDO, PG has error reporting bits at both
         // the top level and at the statement level.  However, the normal
         // query method returns false instead of a statement handle, and we
         // need the statement handle to pull errors back.  This means that
         // we need to use the async query sending method and check every single
         // time for an error message.  We can then nuke the statement handle
         // so things that try to look for it to detect errors can work.
         $this->sh = pg_get_result($this->dbh);
         $sqlstate = pg_result_error_field($this->sh, PGSQL_DIAG_SQLSTATE);
         if ($sqlstate && $sqlstate != '00000') {
             $this->errno = $sqlstate;
             $this->errstr = pg_result_error_field($this->sh, PGSQL_DIAG_MESSAGE_PRIMARY) . '  [detail=' . pg_result_error_field($this->sh, PGSQL_DIAG_MESSAGE_DETAIL) . '] (hint=' . pg_result_error_field($this->sh, PGSQL_DIAG_MESSAGE_HINT) . ') at character ' . pg_result_error_field($this->sh, PGSQL_DIAG_STATEMENT_POSITION);
             $this->sh = false;
         }
     } else {
         $this->db->error('Could not send query to server.');
         return;
     }
     $after = microtime(true);
     $this->db->mysql_time += $after - $before;
     // The other adapters also fetch the last insert id here, which we can't
     // effectively do, as it's not exposed by PG.  As an alternative, you can
     // use a RETURNING clause in your query to fetch generated values:
     // INSERT INTO table(foo, bar) VALUES(?, ?) RETURNING id;
     if (is_bool($this->sh) || is_null($this->sh)) {
         $this->affected_rows = 0;
         $this->num_rows = 0;
     } elseif (is_resource($this->sh)) {
         $this->affected_rows = pg_affected_rows($this->sh);
         $this->num_rows = pg_num_rows($this->sh);
     } else {
         assert('false; // statement handler was not a bool or resource, it was a: ' . gettype($this->sh));
     }
     // Upstream code may care about warnings.  PG can return multiple notices
     // for any given query.  It's unclear whether or not this will work, as
     // I haven't been able to actually find any query that does emit multiple
     // notices.  If your code suddenly launches into an infinite loop, now you
     // know why.  Fun times indeed.
     if ($this->db->enable_warning_logging) {
         $this->warnings = array();
         while (($last_notice = pg_last_notice($this->dbh)) !== false) {
             $this->warnings[] = $last_notice;
         }
         $GLOBALS['_DEBUG']['Database Warnings'][] = array('Query' => $this->last_query, 'Warnings' => $this->warnings);
     }
     // Finally, did it even work?
     if ($this->sh === false) {
         $this->db->error('SQL Error:');
         return false;
     }
     return true;
 }
Example #7
0
 /**
  * Executes the query that was previously passed to the constructor.
  *
  * @param mixed  $arg      Query arguments to escape and insert at ? placeholders in $query
  * @param mixed  ...       Additional arguments
  **/
 public function execute()
 {
     // We can't very well perform a query wtihout an active connection.
     if (!is_object($this->dbh)) {
         $this->db->error('Lost connection to database.');
         return;
     }
     // end if
     // Finish any previous statements
     $this->finish();
     // We need to pre-process the arguments for literals and bytefields.  We
     // can't safely pass them into the PDO placeholder system, so we'll
     // have to stick them into the query at the right places before then.
     $args = Database::smart_args(func_get_args());
     list($query, $nargs, $nargn) = $this->reprocess_query($args);
     if (!$query) {
         return;
     }
     $this->last_query = $this->unprocess_query($query, $nargn);
     // Wrap the actual query execution in a bit of benchmarking.
     $before = microtime(true);
     $this->last_exception = null;
     try {
         // We will always create a new statement.  The possibility of missing
         // & pre-replaced placeholders breaks our ability to work with actual
         // prepared statements.  Additionally, PDO tends to break easily when
         // working on prepared statements that return multiple result sets
         // combined with multiple concurrent statement handles.  See the
         // comments on the PDOStatement::closeCursor() manual page.
         // Skip preparing the query if there are no args in the query.
         if ($nargn == 0) {
             $this->sh = $this->dbh->query($this->last_query);
         } else {
             $this->sh = $this->dbh->prepare($this->last_query);
             $this->sh->execute($nargs);
         }
     } catch (PDOException $e) {
         $this->last_exception = $e;
         $this->db->error('SQL Error:');
         return false;
     }
     // end try
     $after = microtime(true);
     $this->db->mysql_time += $after - $before;
     // The other adapters handle these here.  Fun times.
     // Problem 1:
     //  PDO does not provide a clear way to get the last inserted primary key.
     //  There's PDO::lastInsertId(), but it's poorly supported and sometimes
     //  requires extra arguments be passed.  There's undoubtedly a better
     //  way to deal with this than to not deal with it.
     $this->insert_id = null;
     // Problem 2:
     //  PDOStatement::rowCount()'s manual page says:
     //   "If the last SQL statement executed by the associated PDOStatement
     //    was a SELECT statement, some databases may return the number of
     //    rows returned by that statement. However, this behaviour is not
     //    guaranteed for all databases and should not be relied on for
     //    portable applications."
     //  So, unlike the other adapters, there's no separate call for getting
     //  the number of returned rows vs the number of affected rows.
     //  We'll just use it here and hope it works and that calling code doesn't
     //  care too much.
     // Overall, PDO kind of sucks for this kind of thing.
     $this->affected_rows = $this->sh->rowCount();
     $this->num_rows = $this->sh->rowCount();
     // PDO has no way to check warnings.  We won't try.
     $this->warnings = array();
     // But we do have errors, though this *should* never be called thanks to the
     // exception catching above.
     $last_notice = $this->sh->errorCode();
     if ($last_notice == '00000' || empty($last_notice)) {
         $last_notice = null;
     }
     if ($last_notice) {
         $this->db->error('SQL Error:');
         return false;
     }
     return true;
 }