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.106 2005/10/10 12:23:21 quipo 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. class MDB2_Driver_ibase extends MDB2_Driver_Common
  56. {
  57.     // {{{ properties
  58.     var $escape_quotes = "'";
  59.  
  60.     var $transaction_id = 0;
  61.  
  62.     var $query_parameters = array();
  63.     var $query_parameter_values = array();
  64.  
  65.     // }}}
  66.     // {{{ constructor
  67.  
  68.     /**
  69.      * Constructor
  70.      */
  71.     function __construct()
  72.     {
  73.         parent::__construct();
  74.  
  75.         $this->phptype  'ibase';
  76.         $this->dbsyntax 'ibase';
  77.  
  78.         $this->supported['sequences'= true;
  79.         $this->supported['indexes'= true;
  80.         $this->supported['affected_rows'function_exists('ibase_affected_rows');
  81.         $this->supported['summary_functions'= true;
  82.         $this->supported['order_by_text'= true;
  83.         $this->supported['transactions'= true;
  84.         $this->supported['current_id'= true;
  85.         // maybe this needs different handling for ibase and firebird?
  86.         $this->supported['limit_queries''emulated';
  87.         $this->supported['LOBs'= true;
  88.         $this->supported['replace'= false;
  89.         $this->supported['sub_selects'= true;
  90.         $this->supported['auto_increment'= true;
  91.         $this->supported['primary_key'= true;
  92.  
  93.         $this->options['database_path''';
  94.         $this->options['database_extension''.gdb';
  95.         $this->options['default_text_field_length'= 4096;
  96.     }
  97.  
  98.     // }}}
  99.     // {{{ errorInfo()
  100.  
  101.     /**
  102.      * This method is used to collect information about an error
  103.      *
  104.      * @param integer $error 
  105.      * @return array 
  106.      * @access public
  107.      */
  108.     function errorInfo($error = null)
  109.     {
  110.         $native_msg @ibase_errmsg();
  111.  
  112.         if (function_exists('ibase_errcode')) {
  113.             $native_code @ibase_errcode();
  114.         else {
  115.             // memo for the interbase php module hackers: we need something similar
  116.             // to mysql_errno() to retrieve error codes instead of this ugly hack
  117.             if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/'$native_msg$m)) {
  118.                 $native_code = (int)$m[2];
  119.             else {
  120.                 $native_code = null;
  121.             }
  122.         }
  123.         if (is_null($error)) {
  124.             $error = MDB2_ERROR;
  125.             if ($native_code{
  126.                 // try to interpret Interbase error code (that's why we need ibase_errno()
  127.                 // in the interbase module to return the real error code)
  128.                 switch ($native_code{
  129.                 case -204:
  130.                     if (isset($m[3]&& is_int(strpos($m[3]'Table unknown'))) {
  131.                         $errno = MDB2_ERROR_NOSUCHTABLE;
  132.                     }
  133.                 break;
  134.                 default:
  135.                     static $ecode_map;
  136.                     if (empty($ecode_map)) {
  137.                         $ecode_map = array(
  138.                             -104 => MDB2_ERROR_SYNTAX,
  139.                             -150 => MDB2_ERROR_ACCESS_VIOLATION,
  140.                             -151 => MDB2_ERROR_ACCESS_VIOLATION,
  141.                             -155 => MDB2_ERROR_NOSUCHTABLE,
  142.                             -157 => MDB2_ERROR_NOSUCHFIELD,
  143.                             -158 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  144.                             -170 => MDB2_ERROR_MISMATCH,
  145.                             -171 => MDB2_ERROR_MISMATCH,
  146.                             -172 => MDB2_ERROR_INVALID,
  147.                             // -204 =>  // Covers too many errors, need to use regex on msg
  148.                             -205 => MDB2_ERROR_NOSUCHFIELD,
  149.                             -206 => MDB2_ERROR_NOSUCHFIELD,
  150.                             -208 => MDB2_ERROR_INVALID,
  151.                             -219 => MDB2_ERROR_NOSUCHTABLE,
  152.                             -297 => MDB2_ERROR_CONSTRAINT,
  153.                             -303 => MDB2_ERROR_INVALID,
  154.                             -413 => MDB2_ERROR_INVALID_NUMBER,
  155.                             -530 => MDB2_ERROR_CONSTRAINT,
  156.                             -551 => MDB2_ERROR_ACCESS_VIOLATION,
  157.                             -552 => MDB2_ERROR_ACCESS_VIOLATION,
  158.                             // -607 =>  // Covers too many errors, need to use regex on msg
  159.                             -625 => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  160.                             -803 => MDB2_ERROR_CONSTRAINT,
  161.                             -804 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
  162.                             -904 => MDB2_ERROR_CONNECT_FAILED,
  163.                             -922 => MDB2_ERROR_NOSUCHDB,
  164.                             -923 => MDB2_ERROR_CONNECT_FAILED,
  165.                             -924 => MDB2_ERROR_CONNECT_FAILED
  166.                         );
  167.                     }
  168.                     if (isset($ecode_map[$native_code])) {
  169.                         $error $ecode_map[$native_code];
  170.                     }
  171.                     break;
  172.                 }
  173.             else {
  174.                 static $error_regexps;
  175.                 if (!isset($error_regexps)) {
  176.                     $error_regexps = array(
  177.                         '/generator .* is not defined/'
  178.                             => MDB2_ERROR_SYNTAX,  // for compat. w ibase_errcode()
  179.                         '/table.*(not exist|not found|unknown)/i'
  180.                             => MDB2_ERROR_NOSUCHTABLE,
  181.                         '/table .* already exists/i'
  182.                             => MDB2_ERROR_ALREADY_EXISTS,
  183.                         '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
  184.                             => MDB2_ERROR_ALREADY_EXISTS,
  185.                         '/unsuccessful metadata update .* not found/i'
  186.                             => MDB2_ERROR_NOT_FOUND,
  187.                         '/validation error for column .* value "\*\*\* null/i'
  188.                             => MDB2_ERROR_CONSTRAINT_NOT_NULL,
  189.                         '/violation of [\w ]+ constraint/i'
  190.                             => MDB2_ERROR_CONSTRAINT,
  191.                         '/conversion error from string/i'
  192.                             => MDB2_ERROR_INVALID_NUMBER,
  193.                         '/no permission for/i'
  194.                             => MDB2_ERROR_ACCESS_VIOLATION,
  195.                         '/arithmetic exception, numeric overflow, or string truncation/i'
  196.                             => MDB2_ERROR_INVALID,
  197.                     );
  198.                 }
  199.                 foreach ($error_regexps as $regexp => $code{
  200.                     if (preg_match($regexp$native_msg$m)) {
  201.                         $error $code;
  202.                         break;
  203.                     }
  204.                 }
  205.             }
  206.         }
  207.         return array($error$native_code$native_msg);
  208.     }
  209.  
  210.     // }}}
  211.     // {{{ beginTransaction()
  212.  
  213.     /**
  214.      * Start a transaction.
  215.      *
  216.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  217.      * @access public
  218.      */
  219.     function beginTransaction()
  220.     {
  221.         $this->debug('starting transaction''beginTransaction');
  222.         if ($this->in_transaction{
  223.             return MDB2_OK;  //nothing to do
  224.         }
  225.         if (!$this->destructor_registered && $this->opened_persistent{
  226.             $this->destructor_registered = true;
  227.             register_shutdown_function('MDB2_closeOpenTransactions');
  228.         }
  229.         $result = ibase_trans();
  230.         if (!$result{
  231.             return $this->raiseError(MDB2_ERRORnullnull,
  232.                 'beginTransaction: could not start a transaction');
  233.         }
  234.         $this->transaction_id = $result;
  235.         $this->in_transaction = true;
  236.         return MDB2_OK;
  237.     }
  238.  
  239.     // }}}
  240.     // {{{ commit()
  241.  
  242.     /**
  243.      * Commit the database changes done during a transaction that is in
  244.      * progress.
  245.      *
  246.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  247.      * @access public
  248.      */
  249.     function commit()
  250.     {
  251.         $this->debug('commit transaction''commit');
  252.         if (!$this->in_transaction{
  253.             return $this->raiseError(MDB2_ERRORnullnull,
  254.                 'commit: transaction changes are being auto committed');
  255.         }
  256.         if (!ibase_commit($this->transaction_id)) {
  257.             return $this->raiseError(MDB2_ERRORnullnull,
  258.                 'commit: could not commit a transaction');
  259.         }
  260.         $this->in_transaction = false;
  261.         //$this->transaction_id = 0;
  262.         return MDB2_OK;
  263.     }
  264.  
  265.     // }}}
  266.     // {{{ rollback()
  267.  
  268.     /**
  269.      * Cancel any database changes done during a transaction that is in
  270.      * progress.
  271.      *
  272.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  273.      * @access public
  274.      */
  275.     function rollback()
  276.     {
  277.         $this->debug('rolling back transaction''rollback');
  278.         if (!$this->in_transaction{
  279.             return $this->raiseError(MDB2_ERRORnullnull,
  280.                 'rollback: transactions can not be rolled back when changes are auto committed');
  281.         }
  282.         if ($this->transaction_id && !ibase_rollback($this->transaction_id)) {
  283.             return $this->raiseError(MDB2_ERRORnullnull,
  284.                 'rollback: Could not rollback a pending transaction: '.ibase_errmsg());
  285.         }
  286.         $this->in_transaction = false;
  287.         $this->transaction_id = 0;
  288.         return MDB2_OK;
  289.     }
  290.  
  291.     // }}}
  292.     // {{{ getDatabaseFile()
  293.  
  294.     /**
  295.      * Builds the string with path+dbname+extension
  296.      *
  297.      * @return string full database path+file
  298.      * @access protected
  299.      */
  300.     function _getDatabaseFile($database_name)
  301.     {
  302.         if ($database_name == ''{
  303.             return $database_name;
  304.         }
  305.         return $this->options['database_path'].$database_name.$this->options['database_extension'];
  306.     }
  307.  
  308.     // }}}
  309.     // {{{ _doConnect()
  310.  
  311.     /**
  312.      * Does the grunt work of connecting to the database
  313.      *
  314.      * @return mixed connection resource on success, MDB2 Error Object on failure
  315.      * @access protected
  316.      */
  317.     function _doConnect($database_name$persistent = false)
  318.     {
  319.         $user    $this->dsn['username'];
  320.         $pw      $this->dsn['password'];
  321.         $dbhost  $this->dsn['hostspec'?
  322.             ($this->dsn['hostspec'].':'.$database_name$database_name;
  323.  
  324.         $params = array();
  325.         $params[$dbhost;
  326.         $params[!empty($user$user : null;
  327.         $params[!empty($pw$pw : null;
  328.         $params[= isset($this->dsn['charset']$this->dsn['charset': null;
  329.         $params[= isset($this->dsn['buffers']$this->dsn['buffers': null;
  330.         $params[= isset($this->dsn['dialect']$this->dsn['dialect': null;
  331.         $params[= isset($this->dsn['role'])    $this->dsn['role': null;
  332.  
  333.         $connect_function $persistent 'ibase_pconnect' 'ibase_connect';
  334.  
  335.         $connection @call_user_func_array($connect_function$params);
  336.         if ($connection <= 0{
  337.             return $this->raiseError(MDB2_ERROR_CONNECT_FAILED);
  338.         }
  339.         if (function_exists('ibase_timefmt')) {
  340.             @ibase_timefmt("%Y-%m-%d %H:%M:%S"IBASE_TIMESTAMP);
  341.             @ibase_timefmt("%Y-%m-%d"IBASE_DATE);
  342.         else {
  343.             @ini_set("ibase.timestampformat""%Y-%m-%d %H:%M:%S");
  344.             //@ini_set("ibase.timeformat", "%H:%M:%S");
  345.             @ini_set("ibase.dateformat""%Y-%m-%d");
  346.         }
  347.  
  348.         return $connection;
  349.     }
  350.  
  351.     // }}}
  352.     // {{{ connect()
  353.  
  354.     /**
  355.      * Connect to the database
  356.      *
  357.      * @return true on success, MDB2 Error Object on failure
  358.      * @access public
  359.      */
  360.     function connect()
  361.     {
  362.         $database_file $this->_getDatabaseFile($this->database_name);
  363.         if (is_resource($this->connection)) {
  364.             if (count(array_diff($this->connected_dsn$this->dsn)) == 0
  365.                 && $this->connected_database_name == $database_file
  366.                 && $this->opened_persistent == $this->options['persistent']
  367.             {
  368.                 return MDB2_OK;
  369.             }
  370.             $this->disconnect(false);
  371.         }
  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->getOption('disable_query')) {
  430.             if ($isManip{
  431.                 return 0;
  432.             }
  433.             return null;
  434.         }
  435.  
  436.         if (is_null($connection)) {
  437.             if ($this->in_transaction{
  438.                 $connection $this->transaction_id;
  439.             else {
  440.                 $err $this->connect();
  441.                 if (PEAR::isError($err)) {
  442.                     return $err;
  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.                     $err =$this->raiseError(MDB2_ERROR_SYNTAXnullnull,
  525.                         'prepare: query with an unterminated text string specified');
  526.                     return $err;
  527.                 }
  528.                 switch ($this->escape_quotes{
  529.                 case '':
  530.                 case "'":
  531.                     $position $end_quote + 1;
  532.                     break;
  533.                 default:
  534.                     if ($end_quote == $quote + 1{
  535.                         $position $end_quote + 1;
  536.                     else {
  537.                         if ($query[$end_quote-1== $this->escape_quotes{
  538.                             $position $end_quote;
  539.                         else {
  540.                             $position $end_quote + 1;
  541.                         }
  542.                     }
  543.                     break;
  544.                 }
  545.             elseif ($query[$position== $placeholder_type_guess{
  546.                 if ($placeholder_type_guess == '?'{
  547.                     break;
  548.                 }
  549.                 if (is_null($placeholder_type)) {
  550.                     $placeholder_type $query[$p_position];
  551.                     $question $colon $placeholder_type;
  552.                 }
  553.                 $name preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/si''\\1'$query);
  554.                 if ($name === ''{
  555.                     $err =$this->raiseError(MDB2_ERROR_SYNTAXnullnull,
  556.                         'prepare: named parameter with an empty name');
  557.                     return $err;
  558.                 }
  559.                 $query substr_replace($query'?'$positionstrlen($name)+1);
  560.                 $position $p_position + 1;
  561.             else {
  562.                 $position $p_position;
  563.             }
  564.         }
  565.         $connection ($this->in_transaction $this->transaction_id : $this->connection);
  566.         $statement = ibase_prepare($connection$query);
  567.  
  568.         $class_name 'MDB2_Statement_'.$this->phptype;
  569.         $obj =new $class_name($this$statement$query$types$result_types$this->row_limit$this->row_offset);
  570.         return $obj;
  571.     }
  572.  
  573.     // }}}
  574.     // {{{ nextID()
  575.  
  576.     /**
  577.      * returns the next free id of a sequence
  578.      *
  579.      * @param string $seq_name name of the sequence
  580.      * @param boolean $ondemand when true the seqence is
  581.      *                           automatic created, if it
  582.      *                           not exists
  583.      * @return mixed MDB2 Error Object or id
  584.      * @access public
  585.      */
  586.     function nextID($seq_name$ondemand = true)
  587.     {
  588.         $sequence_name $this->getSequenceName($seq_name);
  589.         $query 'SELECT GEN_ID('.strtoupper($sequence_name).', 1) as the_value FROM RDB$DATABASE';
  590.         $this->expectError('*');
  591.         $result $this->queryOne($query'integer');
  592.         $this->popExpect();
  593.         if (PEAR::isError($result)) {
  594.             if ($ondemand{
  595.                 $this->loadModule('Manager');
  596.                 // Since we are creating the sequence on demand
  597.                 // we know the first id = 1 so initialize the
  598.                 // sequence at 2
  599.                 $result $this->manager->createSequence($seq_name2);
  600.                 if (PEAR::isError($result)) {
  601.                     return $this->raiseError(MDB2_ERRORnullnull,
  602.                         'nextID: on demand sequence could not be created');
  603.                 else {
  604.                     // First ID of a newly created sequence is 1
  605.                     // return 1;
  606.                     // BUT generators are not always reset, so return the actual value
  607.                     return $this->currID($seq_name);
  608.                 }
  609.             }
  610.         }
  611.         return $result;
  612.     }
  613.  
  614.     // }}}
  615.     // {{{ currID()
  616.  
  617.     /**
  618.      * returns the current id of a sequence
  619.      *
  620.      * @param string $seq_name name of the sequence
  621.      * @return mixed MDB2 Error Object or id
  622.      * @access public
  623.      */
  624.     function currID($seq_name)
  625.     {
  626.         $sequence_name $this->getSequenceName($seq_name);
  627.         $query 'SELECT GEN_ID('.strtoupper($sequence_name).', 0) as the_value FROM RDB$DATABASE';
  628.         $value @$this->queryOne($query);
  629.         if (PEAR::isError($value)) {
  630.             return $this->raiseError(MDB2_ERRORnullnull,
  631.                 'currID: Unable to select from ' $seq_name;
  632.         }
  633.         if (!is_numeric($value)) {
  634.             return $this->raiseError(MDB2_ERRORnullnull,
  635.                 'currID: could not find value in sequence table');
  636.         }
  637.         return $value;
  638.     }
  639.  
  640.     // }}}
  641. }
  642.  
  643. class MDB2_Result_ibase extends MDB2_Result_Common
  644. {
  645.     // {{{ _skipLimitOffset()
  646.  
  647.     /**
  648.      * Skip the first row of a result set.
  649.      *
  650.      * @param resource $result 
  651.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  652.      * @access protected
  653.      */
  654.     function _skipLimitOffset()
  655.     {
  656.         if ($this->db->dsn['dbsyntax'== 'firebird'{
  657.             return true;
  658.         }
  659.         if ($this->limit{
  660.             if ($this->rownum $this->limit{
  661.                 return false;
  662.             }
  663.         }
  664.         if ($this->offset{
  665.             while ($this->offset_count $this->offset{
  666.                 ++$this->offset_count;
  667.                 if (!is_array(@ibase_fetch_row($this->result))) {
  668.                     $this->offset_count $this->offset;
  669.                     return false;
  670.                 }
  671.             }
  672.         }
  673.         return true;
  674.     }
  675.  
  676.     // }}}
  677.     // {{{ fetchRow()
  678.  
  679.     /**
  680.      * Fetch a row and insert the data into an existing array.
  681.      *
  682.      * @param int  $fetchmode how the array data should be indexed
  683.      * @param int  $rownum    number of the row where the data can be found
  684.      * @return int data array on success, a MDB2 error on failure
  685.      * @access public
  686.      */
  687.     function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT$rownum = null)
  688.     {
  689.         if ($this->result === true{
  690.             //query successfully executed, but without results...
  691.             $null = null;
  692.             return $null;
  693.         }
  694.         if (!$this->_skipLimitOffset()) {
  695.             $null = null;
  696.             return $null;
  697.         }
  698.         if (!is_null($rownum)) {
  699.             $seek $this->seek($rownum);
  700.             if (PEAR::isError($seek)) {
  701.                 return $seek;
  702.             }
  703.         }
  704.         if ($fetchmode == MDB2_FETCHMODE_DEFAULT{
  705.             $fetchmode $this->db->fetchmode;
  706.         }
  707.         if ($fetchmode MDB2_FETCHMODE_ASSOC{
  708.             $row @ibase_fetch_assoc($this->result);
  709.             if (is_array($row)
  710.                 && $this->db->options['portability'MDB2_PORTABILITY_FIX_CASE
  711.             {
  712.                 $row array_change_key_case($row$this->db->options['field_case']);
  713.             }
  714.         else {
  715.             $row @ibase_fetch_row($this->result);
  716.         }
  717.         if (!$row{
  718.             if (is_null($this->result)) {
  719.                 $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  720.                     'fetchRow: resultset has already been freed');
  721.                 return $err;
  722.             }
  723.             $null = null;
  724.             return $null;
  725.         }
  726.         if (($mode ($this->db->options['portability'MDB2_PORTABILITY_RTRIM)
  727.             + ($this->db->options['portability'MDB2_PORTABILITY_EMPTY_TO_NULL))
  728.         {
  729.             $this->db->_fixResultArrayValues($row$mode);
  730.         }
  731.         if (!empty($this->values)) {
  732.             $this->_assignBindColumns($row);
  733.         }
  734.         if (!empty($this->types)) {
  735.             $row $this->db->datatype->convertResultRow($this->types$row);
  736.         }
  737.         if ($fetchmode === MDB2_FETCHMODE_OBJECT{
  738.             $object_class $this->db->options['fetch_class'];
  739.             if ($object_class == 'stdClass'{
  740.                 $row = (object) $row;
  741.             else {
  742.                 $row &new $object_class($row);
  743.             }
  744.         }
  745.         ++$this->rownum;
  746.         return $row;
  747.     }
  748.  
  749.     // }}}
  750.     // {{{ _getColumnNames()
  751.  
  752.     /**
  753.      * Retrieve the names of columns returned by the DBMS in a query result.
  754.      *
  755.      * @return mixed associative array variable
  756.      *       that holds the names of columns. The indexes of the array are
  757.      *       the column names mapped to lower case and the values are the
  758.      *       respective numbers of the columns starting from 0. Some DBMS may
  759.      *       not return any columns when the result set does not contain any
  760.      *       rows.
  761.      * @access private
  762.      */
  763.     function _getColumnNames()
  764.     {
  765.         $columns = array();
  766.         $numcols $this->numCols();
  767.         if (PEAR::isError($numcols)) {
  768.             return $numcols;
  769.         }
  770.         for ($column = 0; $column $numcols$column++{
  771.             $column_info @ibase_field_info($this->result$column);
  772.             $columns[$column_info['alias']] $column;
  773.         }
  774.         if ($this->db->options['portability'MDB2_PORTABILITY_FIX_CASE{
  775.             $columns array_change_key_case($columns$this->db->options['field_case']);
  776.         }
  777.         return $columns;
  778.     }
  779.  
  780.     // }}}
  781.     // {{{ numCols()
  782.  
  783.     /**
  784.      * Count the number of columns returned by the DBMS in a query result.
  785.      *
  786.      * @return mixed integer value with the number of columns, a MDB2 error
  787.      *       on failure
  788.      * @access public
  789.      */
  790.     function numCols()
  791.     {
  792.         if ($this->result === true{
  793.             //query successfully executed, but without results...
  794.             return 0;
  795.         }
  796.  
  797.         if (!is_resource($this->result)) {
  798.             return $this->db->raiseError('numCols(): not a valid ibase resource');
  799.         }
  800.         $cols @ibase_num_fields($this->result);
  801.         if (is_null($cols)) {
  802.             if (is_null($this->result)) {
  803.                 return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  804.                     'numCols: resultset has already been freed');
  805.             }
  806.             return $this->db->raiseError();
  807.         }
  808.         return $cols;
  809.     }
  810.  
  811.     // }}}
  812.     // {{{ free()
  813.  
  814.     /**
  815.      * Free the internal resources associated with $result.
  816.      *
  817.      * @return boolean true on success, false if $result is invalid
  818.      * @access public
  819.      */
  820.     function free()
  821.     {
  822.         if (is_resource($this->result)) {
  823.             $free @ibase_free_result($this->result);
  824.             if (!$free{
  825.                 if (is_null($this->result)) {
  826.                     return MDB2_OK;
  827.                 }
  828.                 return $this->db->raiseError();
  829.             }
  830.         }
  831.         $this->result = null;
  832.         return MDB2_OK;
  833.     }
  834.  
  835.     // }}}
  836. }
  837.  
  838. {
  839.     // {{{ class vars
  840.  
  841.     var $buffer;
  842.     var $buffer_rownum = - 1;
  843.  
  844.     // }}}
  845.     // {{{ _fillBuffer()
  846.  
  847.     /**
  848.      * Fill the row buffer
  849.      *
  850.      * @param int $rownum   row number upto which the buffer should be filled
  851.      *                       if the row number is null all rows are ready into the buffer
  852.      * @return boolean true on success, false on failure
  853.      * @access protected
  854.      */
  855.     function _fillBuffer($rownum = null)
  856.     {
  857.         if (isset($this->buffer&& is_array($this->buffer)) {
  858.             if (is_null($rownum)) {
  859.                 if (!end($this->buffer)) {
  860.                     return false;
  861.                 }
  862.             elseif (isset($this->buffer[$rownum])) {
  863.                 return (bool) $this->buffer[$rownum];
  864.             }
  865.         }
  866.  
  867.         if (!$this->_skipLimitOffset()) {
  868.             return false;
  869.         }
  870.  
  871.         $buffer = true;
  872.         while ((is_null($rownum|| $this->buffer_rownum < $rownum)
  873.             && (!$this->limit || $this->buffer_rownum < $this->limit)
  874.             && ($buffer @ibase_fetch_row($this->result))
  875.         {
  876.             ++$this->buffer_rownum;
  877.             $this->buffer[$this->buffer_rownum$buffer;
  878.         }
  879.  
  880.         if (!$buffer{
  881.             ++$this->buffer_rownum;
  882.             $this->buffer[$this->buffer_rownum= false;
  883.             return false;
  884.         elseif ($this->limit && $this->buffer_rownum >= $this->limit{
  885.             ++$this->buffer_rownum;
  886.             $this->buffer[$this->buffer_rownum= false;
  887.         }
  888.         return true;
  889.     }
  890.  
  891.     // }}}
  892.     // {{{ fetchRow()
  893.  
  894.     /**
  895.      * Fetch a row and insert the data into an existing array.
  896.      *
  897.      * @param int       $fetchmode  how the array data should be indexed
  898.      * @param int    $rownum    number of the row where the data can be found
  899.      * @return int data array on success, a MDB2 error on failure
  900.      * @access public
  901.      */
  902.     function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT$rownum = null)
  903.     {
  904.         if ($this->result === true{
  905.             //query successfully executed, but without results...
  906.             $null = null;
  907.             return $null;
  908.         }
  909.         if (is_null($this->result)) {
  910.             $err =$this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  911.                 'fetchRow: resultset has already been freed');
  912.             return $err;
  913.         }
  914.         if (!is_null($rownum)) {
  915.             $seek $this->seek($rownum);
  916.             if (PEAR::isError($seek)) {
  917.                 return $seek;
  918.             }
  919.         }
  920.         $target_rownum $this->rownum + 1;
  921.         if ($fetchmode == MDB2_FETCHMODE_DEFAULT{
  922.             $fetchmode $this->db->fetchmode;
  923.         }
  924.         if (!$this->_fillBuffer($target_rownum)) {
  925.             $null = null;
  926.             return $null;
  927.         }
  928.         $row $this->buffer[$target_rownum];
  929.         if ($fetchmode MDB2_FETCHMODE_ASSOC{
  930.             $column_names $this->getColumnNames();
  931.             foreach ($column_names as $name => $i{
  932.                 $column_names[$name$row[$i];
  933.             }
  934.             $row $column_names;
  935.         }
  936.         if (($mode ($this->db->options['portability'MDB2_PORTABILITY_RTRIM)
  937.             + ($this->db->options['portability'MDB2_PORTABILITY_EMPTY_TO_NULL))
  938.         {
  939.             $this->db->_fixResultArrayValues($row$mode);
  940.         }
  941.         if (!empty($this->values)) {
  942.             $this->_assignBindColumns($row);
  943.         }
  944.         if (!empty($this->types)) {
  945.             $row $this->db->datatype->convertResultRow($this->types$row);
  946.         }
  947.         if ($fetchmode === MDB2_FETCHMODE_OBJECT{
  948.             $object_class $this->db->options['fetch_class'];
  949.             if ($object_class == 'stdClass'{
  950.                 $row = (object) $row;
  951.             else {
  952.                 $row &new $object_class($row);
  953.             }
  954.         }
  955.         ++$this->rownum;
  956.         return $row;
  957.     }
  958.  
  959.     // }}}
  960.     // {{{ seek()
  961.  
  962.     /**
  963.      * seek to a specific row in a result set
  964.      *
  965.      * @param int    $rownum    number of the row where the data can be found
  966.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  967.      * @access public
  968.      */
  969.     function seek($rownum = 0)
  970.     {
  971.         if (is_null($this->result)) {
  972.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  973.                 'seek: resultset has already been freed');
  974.         }
  975.         $this->rownum $rownum - 1;
  976.         return MDB2_OK;
  977.     }
  978.  
  979.     // }}}
  980.     // {{{ valid()
  981.  
  982.     /**
  983.      * check if the end of the result set has been reached
  984.      *
  985.      * @return mixed true or false on sucess, a MDB2 error on failure
  986.      * @access public
  987.      */
  988.     function valid()
  989.     {
  990.         if (is_null($this->result)) {
  991.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  992.                 'valid: resultset has already been freed');
  993.         }
  994.         if ($this->_fillBuffer($this->rownum + 1)) {
  995.             return true;
  996.         }
  997.         return false;
  998.     }
  999.  
  1000.     // }}}
  1001.     // {{{ numRows()
  1002.  
  1003.     /**
  1004.      * returns the number of rows in a result object
  1005.      *
  1006.      * @return mixed MDB2 Error Object or the number of rows
  1007.      * @access public
  1008.      */
  1009.     function numRows()
  1010.     {
  1011.         if (is_null($this->result)) {
  1012.             return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATAnullnull,
  1013.                 'seek: resultset has already been freed');
  1014.         }
  1015.         $this->_fillBuffer();
  1016.         return $this->buffer_rownum;
  1017.     }
  1018.  
  1019.     // }}}
  1020.     // {{{ free()
  1021.  
  1022.     /**
  1023.      * Free the internal resources associated with $result.
  1024.      *
  1025.      * @return boolean true on success, false if $result is invalid
  1026.      * @access public
  1027.      */
  1028.     function free()
  1029.     {
  1030.         $this->buffer = null;
  1031.         $this->buffer_rownum = null;
  1032.         $free = parent::free();
  1033.     }
  1034.  
  1035.     // }}}
  1036. }
  1037.  
  1038. class MDB2_Statement_ibase extends MDB2_Statement_Common
  1039. {
  1040.     // {{{ _execute()
  1041.  
  1042.     /**
  1043.      * Execute a prepared query statement helper method.
  1044.      *
  1045.      * @param mixed $result_class string which specifies which result class to use
  1046.      * @param mixed $result_wrap_class string which specifies which class to wrap results in
  1047.      * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
  1048.      * @access private
  1049.      */
  1050.     function &_execute($result_class = true$result_wrap_class = false)
  1051.     {
  1052.         $isManip = MDB2::isManip($this->query);
  1053.         $this->db->last_query = $this->query;
  1054.         $this->db->debug($this->query'execute');
  1055.         if ($this->db->getOption('disable_query')) {
  1056.             if ($isManip{
  1057.                 $return = 0;
  1058.                 return $return;
  1059.             }
  1060.             $null = null;
  1061.             return $null;
  1062.         }
  1063.  
  1064.         $connected $this->db->connect();
  1065.         if (PEAR::isError($connected)) {
  1066.             return $connected;
  1067.         }
  1068.         $connection $this->db->in_transaction
  1069.             ? $this->db->transaction_id : $this->db->connection;
  1070.  
  1071.         $parameters = array(0 => $this->statement);
  1072.         $i = 0;
  1073.         foreach ($this->values as $parameter => $value{
  1074.             $type array_key_exists($parameter$this->types$this->types[$parameter: null;
  1075.             $parameters[$this->db->quote($value$typefalse);
  1076.             ++$i;
  1077.         }
  1078.  
  1079.         $result call_user_func_array('ibase_execute'$parameters);
  1080.         if ($result === false{
  1081.             $err =$this->db->raiseError();
  1082.             return $err;
  1083.         }
  1084.  
  1085.         if ($isManip{
  1086.             $affected_rows (function_exists('ibase_affected_rows'? ibase_affected_rows($connection: 0);
  1087.             return $affected_rows;
  1088.         }
  1089.  
  1090.         $result =$this->db->_wrapResult($result$this->types,
  1091.             $result_class$result_wrap_class$this->row_limit$this->row_offset);
  1092.         return $result;
  1093.     }
  1094.  
  1095.     // }}}
  1096.  
  1097.     // }}}
  1098.     // {{{ free()
  1099.  
  1100.     /**
  1101.      * Release resources allocated for the specified prepared query.
  1102.      *
  1103.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  1104.      * @access public
  1105.      */
  1106.     function free()
  1107.     {
  1108.         if (!@ibase_free_query($this->statement)) {
  1109.             return $this->db->raiseError();
  1110.         }
  1111.         return MDB2_OK;
  1112.     }
  1113. }
  1114. ?>

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