MDB2
[ class tree: MDB2 ] [ index: MDB2 ] [ all elements ]

Source for file ibase.php

Documentation is available at ibase.php

  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5                                                 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
  7. // | Stig. S. Bakken, Lukas Smith                                         |
  8. // | All rights reserved.                                                 |
  9. // +----------------------------------------------------------------------+
  10. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
  11. // | API as well as database abstraction for PHP applications.            |
  12. // | This LICENSE is in the BSD license style.                            |
  13. // |                                                                      |
  14. // | Redistribution and use in source and binary forms, with or without   |
  15. // | modification, are permitted provided that the following conditions   |
  16. // | are met:                                                             |
  17. // |                                                                      |
  18. // | Redistributions of source code must retain the above copyright       |
  19. // | notice, this list of conditions and the following disclaimer.        |
  20. // |                                                                      |
  21. // | Redistributions in binary form must reproduce the above copyright    |
  22. // | notice, this list of conditions and the following disclaimer in the  |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // |                                                                      |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission.                                                  |
  29. // |                                                                      |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  41. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Lorenzo Alberton <l.alberton@quipo.it>                       |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: ibase.php,v 1.88 2005/04/25 13:09:03 lsmith Exp $
  47.  
  48. /**
  49.  * MDB2 FireBird/InterBase driver
  50.  *
  51.  * @package MDB2
  52.  * @category Database
  53.  * @author  Lorenzo Alberton <l.alberton@quipo.it>
  54.  */
  55. {
  56.     // {{{ properties
  57.     var $escape_quotes = "'";
  58.  
  59.     var $transaction_id = 0;
  60.  
  61.     var $database_path = '';
  62.     var $database_extension = '';
  63.  
  64.     var $query_parameters = array();
  65.     var $query_parameter_values = array();
  66.  
  67.     // }}}
  68.     // {{{ constructor
  69.  
  70.     /**
  71.      * Constructor
  72.      */
  73.     function __construct()
  74.     {
  75.         parent::__construct();
  76.  
  77.         $this->phptype  = 'ibase';
  78.         $this->dbsyntax = 'ibase';
  79.  
  80.         $this->supported['sequences'= true;
  81.         $this->supported['indexes'= true;
  82.         $this->supported['affected_rows'function_exists('ibase_affected_rows');
  83.         $this->supported['summary_functions'= true;
  84.         $this->supported['order_by_text'= true;
  85.         $this->supported['transactions'= true;
  86.         $this->supported['current_id'= true;
  87.         // maybe this needs different handling for ibase and firebird?
  88.         $this->supported['limit_queries'= true;
  89.         $this->supported['LOBs'= true;
  90.         $this->supported['replace'= false;
  91.         $this->supported['sub_selects'= true;
  92.         $this->supported['auto_increment'= false;
  93.  
  94.         $this->options['database_path''';
  95.         $this->options['database_extension''.gdb';
  96.         $this->options['default_text_field_length'= 4096;
  97.     }
  98.  
  99.     // }}}
  100.     // {{{ errorInfo()
  101.  
  102.     /**
  103.      * This method is used to collect information about an error
  104.      *
  105.      * @param integer $error 
  106.      * @return array 
  107.      * @access public
  108.      */
  109.     function errorInfo($error = null)
  110.     {
  111.         $native_msg @ibase_errmsg();
  112.  
  113.         if (function_exists('ibase_errcode')) {
  114.             $native_code @ibase_errcode();
  115.         else {
  116.             // memo for the interbase php module hackers: we need something similar
  117.             // to mysql_errno() to retrieve error codes instead of this ugly hack
  118.             if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/'$native_msg$m)) {
  119.                 $native_code = (int)$m[2];
  120.             else {
  121.                 $native_code = null;
  122.             }
  123.         }
  124.         if (is_null($error)) {
  125.             $error MDB2_ERROR;
  126.             if ($native_code{
  127.                 // try to interpret Interbase error code (that's why we need ibase_errno()
  128.                 // in the interbase module to return the real error code)
  129.                 switch ($native_code{
  130.                 case -204:
  131.                     if (isset($m[3]&& is_int(strpos($m[3]'Table unknown'))) {
  132.                         $errno MDB2_ERROR_NOSUCHTABLE;
  133.                     }
  134.                 break;
  135.                 default:
  136.                     static $ecode_map;
  137.                     if (empty($ecode_map)) {
  138.                         $ecode_map = array(
  139.                             -104 => MDB2_ERROR_SYNTAX,
  140.                             -150 => MDB2_ERROR_ACCESS_VIOLATION,
  141.                             -151 => MDB2_ERROR_ACCESS_VIOLATION,
  142.                             -155 => MDB2_ERROR_NOSUCHTABLE,
  143.                             -157 => MDB2_ERROR_NOSUCHFIELD,
  144.                             -158 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  145.                             -170 => MDB2_ERROR_MISMATCH,
  146.                             -171 => MDB2_ERROR_MISMATCH,
  147.                             -172 => MDB2_ERROR_INVALID,
  148.                             // -204 =>  // Covers too many errors, need to use regex on msg
  149.                             -205 => MDB2_ERROR_NOSUCHFIELD,
  150.                             -206 => MDB2_ERROR_NOSUCHFIELD,
  151.                             -208 => MDB2_ERROR_INVALID,
  152.                             -219 => MDB2_ERROR_NOSUCHTABLE,
  153.                             -297 => MDB2_ERROR_CONSTRAINT,
  154.                             -303 => MDB2_ERROR_INVALID,
  155.                             -413 => MDB2_ERROR_INVALID_NUMBER,
  156.                             -530 => MDB2_ERROR_CONSTRAINT,
  157.                             -551 => MDB2_ERROR_ACCESS_VIOLATION,
  158.                             -552 => MDB2_ERROR_ACCESS_VIOLATION,
  159.                             // -607 =>  // Covers too many errors, need to use regex on msg
  160.                             -625 => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  161.                             -803 => MDB2_ERROR_CONSTRAINT,
  162.                             -804 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  163.                             -904 => MDB2_ERROR_CONNECT_FAILED,
  164.                             -922 => MDB2_ERROR_NOSUCHDB,
  165.                             -923 => MDB2_ERROR_CONNECT_FAILED,
  166.                             -924 => MDB2_ERROR_CONNECT_FAILED
  167.                         );
  168.                     }
  169.                     if (isset($ecode_map[$native_code])) {
  170.                         $error $ecode_map[$native_code];
  171.                     }
  172.                     break;
  173.                 }
  174.             else {
  175.                 static $error_regexps;
  176.                 if (!isset($error_regexps)) {
  177.                     $error_regexps = array(
  178.                         '/generator .* is not defined/'
  179.                             => MDB2_ERROR_SYNTAX,  // for compat. w ibase_errcode()
  180.                         '/table.*(not exist|not found|unknown)/i'
  181.                             => MDB2_ERROR_NOSUCHTABLE,
  182.                         '/table .* already exists/i'
  183.                             => MDB2_ERROR_ALREADY_EXISTS,
  184.                         '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
  185.                             => MDB2_ERROR_ALREADY_EXISTS,
  186.                         '/unsuccessful metadata update .* not found/i'
  187.                             => MDB2_ERROR_NOT_FOUND,
  188.                         '/validation error for column .* value "\*\*\* null/i'
  189.                             => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  190.                         '/violation of [\w ]+ constraint/i'
  191.                             => MDB2_ERROR_CONSTRAINT,
  192.                         '/conversion error from string/i'
  193.                             => MDB2_ERROR_INVALID_NUMBER,
  194.                         '/no permission for/i'
  195.                             => MDB2_ERROR_ACCESS_VIOLATION,
  196.                         '/arithmetic exception, numeric overflow, or string truncation/i'
  197.                             => MDB2_ERROR_INVALID,
  198.                     );
  199.                 }
  200.                 foreach ($error_regexps as $regexp => $code{
  201.                     if (preg_match($regexp$native_msg$m)) {
  202.                         $error $code;
  203.                         break;
  204.                     }
  205.                 }
  206.             }
  207.         }
  208.         return array($error$native_code$native_msg);
  209.     }
  210.  
  211.     // }}}
  212.     // {{{ beginTransaction()
  213.  
  214.     /**
  215.      * Start a transaction.
  216.      *
  217.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  218.      * @access public
  219.      */
  220.     function beginTransaction()
  221.     {
  222.         $this->debug('starting transaction''beginTransaction');
  223.         if ($this->in_transaction{
  224.             return MDB2_OK;  //nothing to do
  225.         }
  226.         if (!$this->destructor_registered && $this->opened_persistent{
  227.             $this->destructor_registered = true;
  228.             register_shutdown_function('MDB2_closeOpenTransactions');
  229.         }
  230.         $result = ibase_trans();
  231.         if (!$result{
  232.             return $this->raiseError(MDB2_ERRORnullnull,
  233.                 'beginTransaction: could not start a transaction');
  234.         }
  235.         $this->transaction_id = $result;
  236.         $this->in_transaction = true;
  237.         return MDB2_OK;
  238.     }
  239.  
  240.     // }}}
  241.     // {{{ commit()
  242.  
  243.     /**
  244.      * Commit the database changes done during a transaction that is in
  245.      * progress.
  246.      *
  247.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  248.      * @access public
  249.      */
  250.     function commit()
  251.     {
  252.         $this->debug('commit transaction''commit');
  253.         if (!$this->in_transaction{
  254.             return $this->raiseError(MDB2_ERRORnullnull,
  255.                 'commit: transaction changes are being auto committed');
  256.         }
  257.         if (!ibase_commit($this->transaction_id)) {
  258.             return $this->raiseError(MDB2_ERRORnullnull,
  259.                 'commit: could not commit a transaction');
  260.         }
  261.         $this->in_transaction = false;
  262.         //$this->transaction_id = 0;
  263.         return MDB2_OK;
  264.     }
  265.  
  266.     // }}}
  267.     // {{{ rollback()
  268.  
  269.     /**
  270.      * Cancel any database changes done during a transaction that is in
  271.      * progress.
  272.      *
  273.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  274.      * @access public
  275.      */
  276.     function rollback()
  277.     {
  278.         $this->debug('rolling back transaction''rollback');
  279.         if (!$this->in_transaction{
  280.             return $this->raiseError(MDB2_ERRORnullnull,
  281.                 'rollback: transactions can not be rolled back when changes are auto committed');
  282.         }
  283.         if ($this->transaction_id && !ibase_rollback($this->transaction_id)) {
  284.             return $this->raiseError(MDB2_ERRORnullnull,
  285.                 'rollback: Could not rollback a pending transaction: '.ibase_errmsg());
  286.         }
  287.         $this->in_transaction = false;
  288.         $this->transaction_id = 0;
  289.         return MDB2_OK;
  290.     }
  291.  
  292.     // }}}
  293.     // {{{ getDatabaseFile()
  294.  
  295.     /**
  296.      * Builds the string with path+dbname+extension
  297.      *
  298.      * @return string full database path+file
  299.      * @access protected
  300.      */
  301.     function _getDatabaseFile($database_name)
  302.     {
  303.         $this->database_path = $this->options['database_path'];
  304.         $this->database_extension = $this->options['database_extension'];
  305.  
  306.         return $this->database_path.$database_name.$this->database_extension;
  307.     }
  308.  
  309.     // }}}
  310.     // {{{ _doConnect()
  311.  
  312.     /**
  313.      * Does the grunt work of connecting to the database
  314.      *
  315.      * @return mixed connection resource on success, MDB2 Error Object on failure
  316.      * @access protected
  317.      */
  318.     function _doConnect($database_name$persistent = false)
  319.     {
  320.         $user    $this->dsn['username'];
  321.         $pw      $this->dsn['password'];
  322.         $dbhost  $this->dsn['hostspec'?
  323.             ($this->dsn['hostspec'].':'.$database_name$database_name;
  324.  
  325.         $params = array();
  326.         $params[$dbhost;
  327.         $params[!empty($user$user : null;
  328.         $params[!empty($pw$pw : null;
  329.         $params[= isset($this->dsn['charset']$this->dsn['charset': null;
  330.         $params[= isset($this->dsn['buffers']$this->dsn['buffers': null;
  331.         $params[= isset($this->dsn['dialect']$this->dsn['dialect': null;
  332.         $params[= isset($this->dsn['role'])    $this->dsn['role': null;
  333.  
  334.         $connect_function $persistent 'ibase_pconnect' 'ibase_connect';
  335.  
  336.         $connection @call_user_func_array($connect_function$params);
  337.         if ($connection <= 0{
  338.             return $this->raiseError(MDB2_ERROR_CONNECT_FAILED);
  339.         }
  340.         if (function_exists('ibase_timefmt')) {
  341.             @ibase_timefmt("%Y-%m-%d %H:%M:%S"IBASE_TIMESTAMP);
  342.             @ibase_timefmt("%Y-%m-%d"IBASE_DATE);
  343.         else {
  344.             @ini_set("ibase.timestampformat""%Y-%m-%d %H:%M:%S");
  345.             //@ini_set("ibase.timeformat", "%H:%M:%S");
  346.             @ini_set("ibase.dateformat""%Y-%m-%d");
  347.         }
  348.  
  349.         return $connection;
  350.     }
  351.  
  352.     // }}}
  353.     // {{{ connect()
  354.  
  355.     /**
  356.      * Connect to the database
  357.      *
  358.      * @return true on success, MDB2 Error Object on failure
  359.      * @access public
  360.      */
  361.     function connect()
  362.     {
  363.         $database_file $this->_getDatabaseFile($this->database_name);
  364.         if (is_resource($this->connection)) {
  365.             if (count(array_diff($this->connected_dsn$this->dsn)) == 0
  366.                 && $this->connected_database_name == $database_file
  367.                 && $this->opened_persistent == $this->options['persistent']
  368.             {
  369.                 return MDB2_OK;
  370.             }
  371.             $this->disconnect(false);
  372.         }
  373.         if (!PEAR::loadExtension('interbase')) {
  374.             return $this->raiseError(MDB2_ERROR_NOT_FOUNDnullnull,
  375.                 'connect: extension '.$this->phptype.' is not compiled into PHP');
  376.         }
  377.  
  378.         if (!empty($this->database_name)) {
  379.             $connection $this->_doConnect($database_file$this->options['persistent']);
  380.             if (PEAR::isError($connection)) {
  381.                 return $connection;
  382.             }
  383.             $this->connection =$connection;
  384.             $this->connected_dsn = $this->dsn;
  385.             $this->connected_database_name = $database_file;
  386.             $this->opened_persistent = $this->options['persistent'];
  387.             $this->dbsyntax = $this->dsn['dbsyntax'$this->dsn['dbsyntax'$this->phptype;
  388.         }
  389.         return MDB2_OK;
  390.     }
  391.  
  392.     // }}}
  393.     // {{{ disconnect()
  394.  
  395.     /**
  396.      * Log out and disconnect from the database.
  397.      *
  398.      * @return mixed true on success, false if not connected and error
  399.      *                 object on error
  400.      * @access public
  401.      */
  402.     function disconnect($force = true)
  403.     {
  404.         if (is_resource($this->connection)) {
  405.             if (!$this->opened_persistent || $force{
  406.                 @ibase_close($this->connection);
  407.             }
  408.             $this->connection = 0;
  409.         }
  410.         return MDB2_OK;
  411.     }
  412.  
  413.     // }}}
  414.     // {{{ _doQuery()
  415.  
  416.     /**
  417.      * Execute a query
  418.      * @param string $query  query
  419.      * @param boolean $isManip  if the query is a manipulation query
  420.      * @param resource $connection 
  421.      * @param string $database_name 
  422.      * @return result or error object
  423.      * @access protected
  424.      */
  425.     function _doQuery($query$isManip = false$connection = null$database_name = null)
  426.     {
  427.         $this->last_query = $query;
  428.         $this->debug($query'query');
  429.         if ($this->options['disable_query']{
  430.             if ($isManip{
  431.                 return MDB2_OK;
  432.             }
  433.             return null;
  434.         }
  435.  
  436.         if (is_null($connection)) {
  437.             if ($this->in_transaction{
  438.                 $connection $this->transaction_id;
  439.             else {
  440.                 $error $this->connect();
  441.                 if (PEAR::isError($error)) {
  442.                     return $error;
  443.                 }
  444.                 $connection $this->connection;
  445.             }
  446.         }
  447.         $result = ibase_query($connection$query);
  448.  
  449.         if ($result === false{
  450.             return $this->raiseError();
  451.         }
  452.  
  453.         if ($isManip{
  454.             //return $result;
  455.             return (function_exists('ibase_affected_rows'? ibase_affected_rows($connection: 0);
  456.         }
  457.         return $result;
  458.     }
  459.  
  460.     // }}}
  461.     // {{{ _modifyQuery()
  462.  
  463.     /**
  464.      * Changes a query string for various DBMS specific reasons
  465.      *
  466.      * @param string $query  query to modify
  467.      * @return the new (modified) query
  468.      * @access protected
  469.      */
  470.     function _modifyQuery($query$isManip$limit$offset)
  471.     {
  472.         if ($limit > 0 && $this->dsn['dbsyntax'== 'firebird'{
  473.             $query preg_replace('/^([\s(])*SELECT(?!\s*FIRST\s*\d+)/i',
  474.                 "SELECT FIRST $limit SKIP $offset"$query);
  475.         }
  476.         return $query;
  477.     }
  478.  
  479.     // }}}
  480.     // {{{ prepare()
  481.  
  482.     /**
  483.      * Prepares a query for multiple execution with execute().
  484.      * With some database backends, this is emulated.
  485.      * prepare() requires a generic query as string like
  486.      * 'INSERT INTO numbers VALUES(?,?)' or
  487.      * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  488.      * The ? and :[a-zA-Z] and  are placeholders which can be set using
  489.      * bindParam() and the query can be send off using the execute() method.
  490.      *
  491.      * @param string $query the query to prepare
  492.      * @param mixed   $types  array that contains the types of the placeholders
  493.      * @param mixed   $result_types  array that contains the types of the columns in
  494.      *                         the result set
  495.      * @return mixed resource handle for the prepared query on success, a MDB2
  496.      *         error on failure
  497.      * @access public
  498.      * @see bindParam, execute
  499.      */
  500.     function &prepare($query$types = null$result_types = null)
  501.     {
  502.         $this->debug($query'prepare');
  503.         $placeholder_type_guess $placeholder_type = null;
  504.         $question '?';
  505.         $colon ':';
  506.         $position = 0;
  507.         while ($position strlen($query)) {
  508.             $q_position strpos($query$question$position);
  509.             $c_position strpos($query$colon$position);
  510.             if ($q_position && $c_position{
  511.                 $p_position min($q_position$c_position);
  512.             elseif($q_position{
  513.                 $p_position $q_position;
  514.             elseif($c_position{
  515.                 $p_position $c_position;
  516.             else {
  517.                 break;
  518.             }
  519.             if (is_null($placeholder_type)) {
  520.                 $placeholder_type_guess $query[$p_position];
  521.             }
  522.             if (is_int($quote strpos($query"'"$position)) && $quote $p_position{
  523.                 if (!is_int($end_quote strpos($query"'"$quote + 1))) {
  524.                     return $this->raiseError(MDB2_ERROR_SYNTAXnullnull,
  525.                         'prepare: query with an unterminated text string specified');
  526.                 }
  527.                 switch ($this->escape_quotes{
  528.                 case '':
  529.                 case "'":
  530.                     $position $end_quote + 1;
  531.                     break;
  532.                 default:
  533.                     if ($end_quote == $quote + 1{
  534.                         $position $end_quote + 1;
  535.                     else {
  536.                         if ($query[$end_quote-1== $this->escape_quotes{
  537.                             $position $end_quote;
  538.                         else {
  539.                             $position $end_quote + 1;
  540.                         }
  541.                     }
  542.                     break;
  543.                 }
  544.             elseif ($query[$position== $placeholder_type_guess{
  545.                 if ($placeholder_type_guess == '?'{
  546.                     break;
  547.                 }
  548.                 if (is_null($placeholder_type)) {
  549.                     $placeholder_type $query[$p_position];
  550.                     $question $colon $placeholder_type;
  551.                 }
  552.                 $name preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/i''\\1'$query);
  553.                 if ($name === ''{
  554.                     return $this->raiseError(MDB2_ERROR_SYNTAXnullnull,
  555.                         'prepare: named parameter with an empty name');
  556.                 }
  557.                 $query substr_replace($query'?'$positionstrlen($name)+1);
  558.                 $position $p_position + 1;
  559.             else {
  560.                 $position $p_position;
  561.             }
  562.         }
  563.         $connection ($this->in_transaction ? $this->transaction_id : $this->connection);
  564.         $statement = ibase_prepare($connection$query);
  565.  
  566.         $class_name 'MDB2_Statement_'.$this->phptype;
  567.         $obj =new $class_name($this$statement$query$types$result_types);
  568.         return $obj;
  569.     }
  570.  
  571.     // }}}
  572.     // {{{ nextID()
  573.  
  574.     /**
  575.      * returns the next free id of a sequence
  576.      *
  577.      * @param string  $seq_name name of the sequence
  578.      * @param boolean $ondemand when true the seqence is
  579.      *                           automatic created, if it
  580.      *                           not exists
  581.      * @return mixed MDB2 Error Object or id
  582.      * @access public
  583.      */
  584.     function nextID($seq_name$ondemand = true)
  585.     {
  586.         $sequence_name $this->getSequenceName($seq_name);
  587.         $query 'SELECT GEN_ID('.strtoupper($sequence_name).', 1) as the_value FROM RDB$DATABASE';
  588.         $this->expectError('*');
  589.         $result $this->queryOne($query'integer');
  590.         $this->popExpect();
  591.         if (PEAR::isError($result)) {
  592.             if ($ondemand{
  593.                 $this->loadModule('Manager');
  594.                 // Since we are creating the sequence on demand
  595.                 // we know the first id = 1 so initialize the
  596.                 // sequence at 2
  597.                 $result $this->manager->createSequence($seq_name2);
  598.                 if (PEAR::isError($result)) {
  599.                     return $this->raiseError(MDB2_ERRORnullnull,
  600.                         'nextID: on demand sequence could not be created');
  601.                 else {
  602.                     // First ID of a newly created sequence is 1
  603.                     // return 1;
  604.                     // BUT generators are not always reset, so return the actual value
  605.                     return $this->currID($seq_name);
  606.                 }
  607.             }
  608.         }
  609.         return $result;
  610.     }
  611.  
  612.     // }}}
  613.     // {{{ currID()
  614.  
  615.     /**
  616.      * returns the current id of a sequence
  617.      *
  618.      * @param string  $seq_name name of the sequence
  619.      * @return mixed MDB2 Error Object or id
  620.      * @access public
  621.      */
  622.     function currID($seq_name)
  623.     {
  624.         $sequence_name $this->getSequenceName($seq_name);
  625.         $query 'SELECT GEN_ID('.strtoupper($sequence_name).', 0) as the_value FROM RDB$DATABASE';
  626.         $value @$this->queryOne($query);
  627.         if (PEAR::isError($value)) {
  628.             return $this->raiseError(MDB2_ERRORnullnull,
  629.                 'currID: Unable to select from ' $seq_name;
  630.         }
  631.         if (!is_numeric($value)) {
  632.             return $this->raiseError(MDB2_ERRORnullnull,
  633.                 'currID: could not find value in sequence table');
  634.         }
  635.         return $value;
  636.     }
  637.  
  638.     // }}}
  639. }
  640.  
  641. class MDB2_Result_ibase extends MDB2_Result_Common
  642. {
  643.     // {{{ _skipLimitOffset()
  644.  
  645.     /**
  646.      * Skip the first row of a result set.
  647.      *
  648.      * @param resource $result 
  649.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  650.      * @access protected
  651.      */
  652.     function _skipLimitOffset()
  653.     {
  654.         if ($db->dsn['dbsyntax'== 'firebird'{
  655.             return true;
  656.         }
  657.         if ($this->limit{
  658.             if ($this->rownum $this->limit{
  659.                 return false;
  660.             }
  661.         }
  662.         if ($this->offset{
  663.             while ($this->offset_count $this->offset{
  664.                 ++$this->offset_count;
  665.                 if (!is_array(@ibase_fetch_row($this->result))) {
  666.                     $this->offset_count $this->offset;
  667.                     return false;
  668.                 }
  669.             }
  670.         }
  671.         return true;
  672.     }
  673.  
  674.     // }}}
  675.     // {{{ fetchRow()
  676.  
  677.     /**
  678.      * Fetch a row and insert the data into an existing array.
  679.      *
  680.      * @param int  $fetchmode how the array data should be indexed
  681.      * @param int  $rownum    number of the row where the data can be found
  682.      * @return int data array on success, a MDB2 error on failure
  683.      * @access public
  684.      */
  685.     function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT$rownum = null)
  686.     {
  687.         if ($this->result === true{
  688.             //query successfully executed, but without results...
  689.             return null;
  690.         }
  691.         if (!$this->_skipLimitOffset()) {
  692.             return null;
  693.         }
  694.         if (!is_null($rownum)) {
  695.             $seek $this->seek($rownum);
  696.             if (PEAR::isError($seek)) {
  697.                 return $seek;
  698.             }
  699.         }
  700.         if ($fetchmode == MDB2_FETCHMODE_DEFAULT{
  701.             $fetchmode $this->db->fetchmode;
  702.         }
  703.         if ($fetchmode MDB2_FETCHMODE_ASSOC{
  704.             $row @ibase_fetch_assoc($this->result);
  705.             if (is_array($row)
  706.                 && $this->db->options['portability'MDB2_PORTABILITY_LOWERCASE
  707.             {
  708.                 $row array_change_key_case($rowCASE_LOWER);
  709.             }
  710.         else {
  711.             $row @ibase_fetch_row($this->result);
  712.         }
  713.         if (!$row{
  714.             if (is_null($this->result)) {
  715.                 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  716.                     'fetchRow: resultset has already been freed');
  717.             }
  718.             return null;
  719.         }
  720.         if ($this->db->options['portability'MDB2_PORTABILITY_RTRIM{
  721.             $this->db->_rtrimArrayValues($row);
  722.         }
  723.         if ($this->db->options['portability'MDB2_PORTABILITY_EMPTY_TO_NULL{
  724.             $this->db->_convertEmptyArrayValuesToNull($row);
  725.         }
  726.         if (isset($this->values)) {
  727.             $this->_assignBindColumns($row);
  728.         }
  729.         if (isset($this->types)) {
  730.             $row $this->db->datatype->convertResultRow($this->types$row);
  731.         }
  732.         if ($fetchmode === MDB2_FETCHMODE_OBJECT{
  733.             $object_class $this->db->options['fetch_class'];
  734.             if ($object_class == 'stdClass'{
  735.                 $row = (object) $row;
  736.             else {
  737.                 $row &new $object_class($row);
  738.             }
  739.         }
  740.         ++$this->rownum;
  741.         return $row;
  742.     }
  743.  
  744.     // }}}
  745.     // {{{ _getColumnNames()
  746.  
  747.     /**
  748.      * Retrieve the names of columns returned by the DBMS in a query result.
  749.      *
  750.      * @return mixed associative array variable
  751.      *       that holds the names of columns. The indexes of the array are
  752.      *       the column names mapped to lower case and the values are the
  753.      *       respective numbers of the columns starting from 0. Some DBMS may
  754.      *       not return any columns when the result set does not contain any
  755.      *       rows.
  756.      * @access private
  757.      */
  758.     function _getColumnNames()
  759.     {
  760.         $columns = array();
  761.         $numcols $this->numCols();
  762.         if (PEAR::isError($numcols)) {
  763.             return $numcols;
  764.         }
  765.         for ($column = 0; $column $numcols$column++{
  766.             $column_info @ibase_field_info($this->result$column);
  767.             $columns[$column_info['alias']] $column;
  768.         }
  769.         if ($this->db->options['portability'MDB2_PORTABILITY_LOWERCASE{
  770.             $columns array_change_key_case($columnsCASE_LOWER);
  771.         }
  772.         return $columns;
  773.     }
  774.  
  775.     // }}}
  776.     // {{{ numCols()
  777.  
  778.     /**
  779.      * Count the number of columns returned by the DBMS in a query result.
  780.      *
  781.      * @return mixed integer value with the number of columns, a MDB2 error
  782.      *       on failure
  783.      * @access public
  784.      */
  785.     function numCols()
  786.     {
  787.         if ($this->result === true{
  788.             //query successfully executed, but without results...
  789.             return 0;
  790.         }
  791.  
  792.         if (!is_resource($this->result)) {
  793.             return $this->db->raiseError('numCols(): not a valid ibase resource');
  794.         }
  795.         $cols @ibase_num_fields($this->result);
  796.         if (is_null($cols)) {
  797.             if (is_null($this->result)) {
  798.                 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  799.                     'numCols: resultset has already been freed');
  800.             }
  801.             return $this->db->raiseError();
  802.         }
  803.         return $cols;
  804.     }
  805.  
  806.     // }}}
  807.     // {{{ free()
  808.  
  809.     /**
  810.      * Free the internal resources associated with $result.
  811.      *
  812.      * @return boolean true on success, false if $result is invalid
  813.      * @access public
  814.      */
  815.     function free()
  816.     {
  817.         if (is_resource($this->result)) {
  818.             $free @ibase_free_result($this->result);
  819.             if (!$free{
  820.                 if (is_null($this->result)) {
  821.                     return MDB2_OK;
  822.                 }
  823.                 return $this->db->raiseError();
  824.             }
  825.         }
  826.         $this->result = null;
  827.         return MDB2_OK;
  828.     }
  829.  
  830.     // }}}
  831. }
  832.  
  833. class MDB2_BufferedResult_ibase extends MDB2_Result_ibase
  834. {
  835.     // {{{ class vars
  836.  
  837.     var $buffer;
  838.     var $buffer_rownum = - 1;
  839.  
  840.     // }}}
  841.     // {{{ _fillBuffer()
  842.  
  843.     /**
  844.      * Fill the row buffer
  845.      *
  846.      * @param int $rownum   row number upto which the buffer should be filled
  847.      *                       if the row number is null all rows are ready into the buffer
  848.      * @return boolean true on success, false on failure
  849.      * @access protected
  850.      */
  851.     function _fillBuffer($rownum = null)
  852.     {
  853.         if (isset($this->buffer&& is_array($this->buffer)) {
  854.             if (is_null($rownum)) {
  855.                 if (!end($this->buffer)) {
  856.                     return false;
  857.                 }
  858.             elseif (isset($this->buffer[$rownum])) {
  859.                 return (bool) $this->buffer[$rownum];
  860.             }
  861.         }
  862.  
  863.         if (!$this->_skipLimitOffset()) {
  864.             return false;
  865.         }
  866.  
  867.         $buffer = true;
  868.         while ((is_null($rownum|| $this->buffer_rownum $rownum)
  869.             && (!$this->limit || $this->buffer_rownum $this->limit)
  870.             && ($buffer @ibase_fetch_row($this->result))
  871.         {
  872.             ++$this->buffer_rownum;
  873.             $this->buffer[$this->buffer_rownum$buffer;
  874.         }
  875.  
  876.         if (!$buffer{
  877.             ++$this->buffer_rownum;
  878.             $this->buffer[$this->buffer_rownum= false;
  879.             return false;
  880.         elseif ($this->limit && $this->buffer_rownum >= $this->limit{
  881.             ++$this->buffer_rownum;
  882.             $this->buffer[$this->buffer_rownum= false;
  883.         }
  884.         return true;
  885.     }
  886.  
  887.     // }}}
  888.     // {{{ fetchRow()
  889.  
  890.     /**
  891.      * Fetch a row and insert the data into an existing array.
  892.      *
  893.      * @param int       $fetchmode  how the array data should be indexed
  894.      * @param int    $rownum    number of the row where the data can be found
  895.      * @return int data array on success, a MDB2 error on failure
  896.      * @access public
  897.      */
  898.     function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT$rownum = null)
  899.     {
  900.         if ($this->result === true{
  901.             //query successfully executed, but without results...
  902.             return null;
  903.         }
  904.         if (is_null($this->result)) {
  905.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  906.                 'fetchRow: resultset has already been freed');
  907.         }
  908.         if (!is_null($rownum)) {
  909.             $seek $this->seek($rownum);
  910.             if (PEAR::isError($seek)) {
  911.                 return $seek;
  912.             }
  913.         }
  914.         $target_rownum $this->rownum + 1;
  915.         if ($fetchmode == MDB2_FETCHMODE_DEFAULT{
  916.             $fetchmode $this->db->fetchmode;
  917.         }
  918.         if (!$this->_fillBuffer($target_rownum)) {
  919.             return null;
  920.         }
  921.         $row $this->buffer[$target_rownum];
  922.         if ($fetchmode MDB2_FETCHMODE_ASSOC{
  923.             $column_names $this->getColumnNames();
  924.             foreach ($column_names as $name => $i{
  925.                 $column_names[$name$row[$i];
  926.             }
  927.             $row $column_names;
  928.         }
  929.         if ($this->db->options['portability'MDB2_PORTABILITY_EMPTY_TO_NULL{
  930.             $this->db->_convertEmptyArrayValuesToNull($row);
  931.         }
  932.         if (isset($this->values)) {
  933.             $this->_assignBindColumns($row);
  934.         }
  935.         if (isset($this->types)) {
  936.             $row $this->db->datatype->convertResultRow($this->types$row);
  937.         }
  938.         if ($this->db->options['portability'MDB2_PORTABILITY_RTRIM{
  939.             $this->db->_rtrimArrayValues($row);
  940.         }
  941.         if ($this->db->options['portability'MDB2_PORTABILITY_EMPTY_TO_NULL{
  942.             $this->db->_convertEmptyArrayValuesToNull($row);
  943.         }
  944.         if ($fetchmode === MDB2_FETCHMODE_OBJECT{
  945.             $object_class $this->db->options['fetch_class'];
  946.             if ($object_class == 'stdClass'{
  947.                 $row = (object) $row;
  948.             else {
  949.                 $row &new $object_class($row);
  950.             }
  951.         }
  952.         ++$this->rownum;
  953.         return $row;
  954.     }
  955.  
  956.     // }}}
  957.     // {{{ seek()
  958.  
  959.     /**
  960.      * seek to a specific row in a result set
  961.      *
  962.      * @param int    $rownum    number of the row where the data can be found
  963.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  964.      * @access public
  965.      */
  966.     function seek($rownum = 0)
  967.     {
  968.         if (is_null($this->result)) {
  969.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  970.                 'seek: resultset has already been freed');
  971.         }
  972.         $this->rownum $rownum - 1;
  973.         return MDB2_OK;
  974.     }
  975.  
  976.     // }}}
  977.     // {{{ valid()
  978.  
  979.     /**
  980.      * check if the end of the result set has been reached
  981.      *
  982.      * @return mixed true or false on sucess, a MDB2 error on failure
  983.      * @access public
  984.      */
  985.     function valid()
  986.     {
  987.         if (is_null($this->result)) {
  988.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  989.                 'valid: resultset has already been freed');
  990.         }
  991.         if ($this->_fillBuffer($this->rownum + 1)) {
  992.             return true;
  993.         }
  994.         return false;
  995.     }
  996.  
  997.     // }}}
  998.     // {{{ numRows()
  999.  
  1000.     /**
  1001.      * returns the number of rows in a result object
  1002.      *
  1003.      * @return mixed MDB2 Error Object or the number of rows
  1004.      * @access public
  1005.      */
  1006.     function numRows()
  1007.     {
  1008.         if (is_null($this->result)) {
  1009.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  1010.                 'seek: resultset has already been freed');
  1011.         }
  1012.         $this->_fillBuffer();
  1013.         return $this->buffer_rownum;
  1014.     }
  1015.  
  1016.     // }}}
  1017.     // {{{ free()
  1018.  
  1019.     /**
  1020.      * Free the internal resources associated with $result.
  1021.      *
  1022.      * @return boolean true on success, false if $result is invalid
  1023.      * @access public
  1024.      */
  1025.     function free()
  1026.     {
  1027.         $this->buffer = null;
  1028.         $this->buffer_rownum = null;
  1029.         $free = parent::free();
  1030.     }
  1031.  
  1032.     // }}}
  1033. }
  1034.  
  1035. class MDB2_Statement_ibase extends MDB2_Statement_Common
  1036. {
  1037.     // {{{ execute()
  1038.  
  1039.     /**
  1040.      * Execute a prepared query statement.
  1041.      *
  1042.      * @param mixed $result_class string which specifies which result class to use
  1043.      * @param mixed $result_wrap_class string which specifies which class to wrap results in
  1044.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  1045.      * @access public
  1046.      */
  1047.     function &execute($result_class = true$result_wrap_class = false)
  1048.     {
  1049.         $isManip MDB2::isManip($this->query);
  1050.         $this->db->last_query = $this->query;
  1051.         $this->db->debug($this->query'query');
  1052.         if ($this->db->getOption('disable_query')) {
  1053.             return $isManip MDB2_OK : null;
  1054.         }
  1055.  
  1056.         $connected $this->db->connect();
  1057.         if (PEAR::isError($connected)) {
  1058.             return $connected;
  1059.         }
  1060.         $connection ($this->db->in_transaction ? $this->db->transaction_id : $this->db->connection);
  1061.  
  1062.         $parameters $this->quoteParamsForPreparedQuery();
  1063.  
  1064.         $this->db->row_offset = $this->row_offset;
  1065.         $this->db->row_limit  = $this->row_limit;
  1066.  
  1067.         array_unshift($parameters$this->statement);
  1068.         $result call_user_func_array('ibase_execute'$parameters);
  1069.         if ($result === false{
  1070.             return $this->db->raiseError();
  1071.         }
  1072.  
  1073.         if ($isManip{
  1074.             //return $result;
  1075.             return (function_exists('ibase_affected_rows'? ibase_affected_rows($connection: 0);
  1076.         }
  1077.  
  1078.         return $this->db->_wrapResult($result$this->types,
  1079.             $result_class$result_wrap_class$this->row_limit$this->row_offset);
  1080.     }
  1081.  
  1082.     // }}}
  1083.     // {{{ quoteParamsForPreparedQuery()
  1084.  
  1085.     function quoteParamsForPreparedQuery()
  1086.     {
  1087.         $parameters = array();
  1088.         $must_quote = array('decimal''boolean''blob''clob');
  1089.         foreach ($this->values as $parameter => $value{
  1090.             if (!isset($value)) {
  1091.                 $parameters[= null;
  1092.             else {
  1093.                 $type = isset($this->types[$parameter]$this->types[$parameter: null;
  1094.                 if (in_array($type$must_quote)) {
  1095.                     if ($type == 'boolean'{
  1096.                         $value ($value 'Y' 'N');
  1097.                     else {
  1098.                         $value $this->db->quote($value$type);
  1099.                     }
  1100.                 elseif (strpos($value'?'!== false{
  1101.                     $value "'".$this->db->escape($value)."'";
  1102.                 }
  1103.                 $parameters[$value;
  1104.             }
  1105.         }
  1106.         return $parameters;
  1107.     }
  1108.  
  1109.     // }}}
  1110. }
  1111. ?>

Documentation generated on Mon, 11 Mar 2019 14:20:18 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.