1<?php
2/**
3 * Zend Framework
4 *
5 * LICENSE
6 *
7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * http://framework.zend.com/license/new-bsd
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@zend.com so we can send you a copy immediately.
14 *
15 * @category   Zend
16 * @package    Zend_Db
17 * @subpackage Statement
18 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
20 * @version    $Id$
21 */
22
23/**
24 * @see Zend_Db_Statement
25 */
26
27/**
28 * Extends for DB2 native adapter.
29 *
30 * @package    Zend_Db
31 * @subpackage Statement
32 * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
33 * @license    http://framework.zend.com/license/new-bsd     New BSD License
34 */
35class Zend_Db_Statement_Db2 extends Zend_Db_Statement
36{
37
38    /**
39     * Column names.
40     */
41    protected $_keys;
42
43    /**
44     * Fetched result values.
45     */
46    protected $_values;
47
48    /**
49     * Prepare a statement handle.
50     *
51     * @param string $sql
52     * @return void
53     * @throws Zend_Db_Statement_Db2_Exception
54     */
55    public function _prepare($sql)
56    {
57        $connection = $this->_adapter->getConnection();
58
59        // db2_prepare on i5 emits errors, these need to be
60        // suppressed so that proper exceptions can be thrown
61        $this->_stmt = @db2_prepare($connection, $sql);
62
63        if (!$this->_stmt) {
64            /**
65             * @see Zend_Db_Statement_Db2_Exception
66             */
67            throw new Zend_Db_Statement_Db2_Exception(
68                db2_stmt_errormsg(),
69                db2_stmt_error()
70            );
71        }
72    }
73
74    /**
75     * Binds a parameter to the specified variable name.
76     *
77     * @param mixed $parameter Name the parameter, either integer or string.
78     * @param mixed $variable  Reference to PHP variable containing the value.
79     * @param mixed $type      OPTIONAL Datatype of SQL parameter.
80     * @param mixed $length    OPTIONAL Length of SQL parameter.
81     * @param mixed $options   OPTIONAL Other options.
82     * @return bool
83     * @throws Zend_Db_Statement_Db2_Exception
84     */
85    public function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
86    {
87        if ($type === null) {
88            $type = DB2_PARAM_IN;
89        }
90
91        if (isset($options['data-type'])) {
92            $datatype = $options['data-type'];
93        } else {
94            $datatype = DB2_CHAR;
95        }
96
97        if (!db2_bind_param($this->_stmt, $parameter, "variable", $type, $datatype)) {
98            /**
99             * @see Zend_Db_Statement_Db2_Exception
100             */
101            throw new Zend_Db_Statement_Db2_Exception(
102                db2_stmt_errormsg(),
103                db2_stmt_error()
104            );
105        }
106
107        return true;
108    }
109
110    /**
111     * Closes the cursor, allowing the statement to be executed again.
112     *
113     * @return bool
114     */
115    public function closeCursor()
116    {
117        if (!$this->_stmt) {
118            return false;
119        }
120        db2_free_stmt($this->_stmt);
121        $this->_stmt = false;
122        return true;
123    }
124
125
126    /**
127     * Returns the number of columns in the result set.
128     * Returns null if the statement has no result set metadata.
129     *
130     * @return int The number of columns.
131     */
132    public function columnCount()
133    {
134        if (!$this->_stmt) {
135            return false;
136        }
137        return db2_num_fields($this->_stmt);
138    }
139
140    /**
141     * Retrieves the error code, if any, associated with the last operation on
142     * the statement handle.
143     *
144     * @return string error code.
145     */
146    public function errorCode()
147    {
148        if (!$this->_stmt) {
149            return false;
150        }
151
152        $error = db2_stmt_error();
153        if ($error === '') {
154            return false;
155        }
156
157        return $error;
158    }
159
160    /**
161     * Retrieves an array of error information, if any, associated with the
162     * last operation on the statement handle.
163     *
164     * @return array
165     */
166    public function errorInfo()
167    {
168        $error = $this->errorCode();
169        if ($error === false){
170            return false;
171        }
172
173        /*
174         * Return three-valued array like PDO.  But DB2 does not distinguish
175         * between SQLCODE and native RDBMS error code, so repeat the SQLCODE.
176         */
177        return array(
178            $error,
179            $error,
180            db2_stmt_errormsg()
181        );
182    }
183
184    /**
185     * Executes a prepared statement.
186     *
187     * @param array $params OPTIONAL Values to bind to parameter placeholders.
188     * @return bool
189     * @throws Zend_Db_Statement_Db2_Exception
190     */
191    public function _execute(array $params = null)
192    {
193        if (!$this->_stmt) {
194            return false;
195        }
196
197        $retval = true;
198        if ($params !== null) {
199            $retval = @db2_execute($this->_stmt, $params);
200        } else {
201            $retval = @db2_execute($this->_stmt);
202        }
203
204        if ($retval === false) {
205            /**
206             * @see Zend_Db_Statement_Db2_Exception
207             */
208            throw new Zend_Db_Statement_Db2_Exception(
209                db2_stmt_errormsg(),
210                db2_stmt_error());
211        }
212
213        $this->_keys = array();
214        if ($field_num = $this->columnCount()) {
215            for ($i = 0; $i < $field_num; $i++) {
216                $name = db2_field_name($this->_stmt, $i);
217                $this->_keys[] = $name;
218            }
219        }
220
221        $this->_values = array();
222        if ($this->_keys) {
223            $this->_values = array_fill(0, count($this->_keys), null);
224        }
225
226        return $retval;
227    }
228
229    /**
230     * Fetches a row from the result set.
231     *
232     * @param int $style  OPTIONAL Fetch mode for this fetch operation.
233     * @param int $cursor OPTIONAL Absolute, relative, or other.
234     * @param int $offset OPTIONAL Number for absolute or relative cursors.
235     * @return mixed Array, object, or scalar depending on fetch mode.
236     * @throws Zend_Db_Statement_Db2_Exception
237     */
238    public function fetch($style = null, $cursor = null, $offset = null)
239    {
240        if (!$this->_stmt) {
241            return false;
242        }
243
244        if ($style === null) {
245            $style = $this->_fetchMode;
246        }
247
248        switch ($style) {
249            case Zend_Db::FETCH_NUM :
250                $row = db2_fetch_array($this->_stmt);
251                break;
252            case Zend_Db::FETCH_ASSOC :
253                $row = db2_fetch_assoc($this->_stmt);
254                break;
255            case Zend_Db::FETCH_BOTH :
256                $row = db2_fetch_both($this->_stmt);
257                break;
258            case Zend_Db::FETCH_OBJ :
259                $row = db2_fetch_object($this->_stmt);
260                break;
261            case Zend_Db::FETCH_BOUND:
262                $row = db2_fetch_both($this->_stmt);
263                if ($row !== false) {
264                    return $this->_fetchBound($row);
265                }
266                break;
267            default:
268                /**
269                 * @see Zend_Db_Statement_Db2_Exception
270                 */
271                throw new Zend_Db_Statement_Db2_Exception("Invalid fetch mode '$style' specified");
272                break;
273        }
274
275        return $row;
276    }
277
278    /**
279     * Fetches the next row and returns it as an object.
280     *
281     * @param string $class  OPTIONAL Name of the class to create.
282     * @param array  $config OPTIONAL Constructor arguments for the class.
283     * @return mixed One object instance of the specified class.
284     */
285    public function fetchObject($class = 'stdClass', array $config = array())
286    {
287        $obj = $this->fetch(Zend_Db::FETCH_OBJ);
288        return $obj;
289    }
290
291    /**
292     * Retrieves the next rowset (result set) for a SQL statement that has
293     * multiple result sets.  An example is a stored procedure that returns
294     * the results of multiple queries.
295     *
296     * @return bool
297     * @throws Zend_Db_Statement_Db2_Exception
298     */
299    public function nextRowset()
300    {
301        /**
302         * @see Zend_Db_Statement_Db2_Exception
303         */
304        throw new Zend_Db_Statement_Db2_Exception(__FUNCTION__ . '() is not implemented');
305    }
306
307    /**
308     * Returns the number of rows affected by the execution of the
309     * last INSERT, DELETE, or UPDATE statement executed by this
310     * statement object.
311     *
312     * @return int     The number of rows affected.
313     */
314    public function rowCount()
315    {
316        if (!$this->_stmt) {
317            return false;
318        }
319
320        $num = @db2_num_rows($this->_stmt);
321
322        if ($num === false) {
323            return 0;
324        }
325
326        return $num;
327    }
328
329     /**
330     * Returns an array containing all of the result set rows.
331     *
332     * @param int $style OPTIONAL Fetch mode.
333     * @param int $col   OPTIONAL Column number, if fetch mode is by column.
334     * @return array Collection of rows, each in a format by the fetch mode.
335     *
336     * Behaves like parent, but if limit()
337     * is used, the final result removes the extra column
338     * 'zend_db_rownum'
339     */
340    public function fetchAll($style = null, $col = null)
341    {
342        $data = parent::fetchAll($style, $col);
343        $results = array();
344        $remove = $this->_adapter->foldCase('ZEND_DB_ROWNUM');
345
346        foreach ($data as $row) {
347            if (is_array($row) && array_key_exists($remove, $row)) {
348                unset($row[$remove]);
349            }
350            $results[] = $row;
351        }
352        return $results;
353    }
354}
355