Failed Conditions
Pull Request — master (#4007)
by Sergei
62:50
created

DB2Statement::fetch()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 9.7518

Importance

Changes 0
Metric Value
cc 6
eloc 14
nc 6
nop 1
dl 0
loc 24
ccs 9
cts 17
cp 0.5294
crap 9.7518
rs 9.2222
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A DB2Statement::fetchColumn() 0 3 1
A DB2Statement::fetchAllNumeric() 0 3 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Driver\IBMDB2;
6
7
use Doctrine\DBAL\Driver\DriverException;
8
use Doctrine\DBAL\Driver\FetchUtils;
9
use Doctrine\DBAL\Driver\Statement;
10
use Doctrine\DBAL\ParameterType;
11
use Traversable;
12
use function assert;
13
use function db2_bind_param;
14
use function db2_execute;
15
use function db2_fetch_array;
16
use function db2_fetch_assoc;
17
use function db2_free_result;
18
use function db2_num_fields;
19
use function db2_num_rows;
20
use function error_get_last;
21
use function fclose;
22
use function fwrite;
23
use function is_int;
24
use function is_resource;
25
use function ksort;
26
use function stream_copy_to_stream;
27
use function stream_get_meta_data;
28
use function tmpfile;
29
use const DB2_BINARY;
30
use const DB2_CHAR;
31
use const DB2_LONG;
32
use const DB2_PARAM_FILE;
33
use const DB2_PARAM_IN;
34
35
final class DB2Statement implements Statement
36
{
37
    /** @var resource */
38
    private $stmt;
39
40
    /** @var mixed[] */
41
    private $bindParam = [];
42
43
    /**
44
     * Map of LOB parameter positions to the tuples containing reference to the variable bound to the driver statement
45
     * and the temporary file handle bound to the underlying statement
46
     *
47
     * @var mixed[][]
48
     */
49
    private $lobs = [];
50
51
    /**
52
     * Indicates whether the statement is in the state when fetching results is possible
53
     *
54
     * @var bool
55
     */
56
    private $result = false;
57
58
    /**
59
     * @param resource $stmt
60
     */
61
    public function __construct($stmt)
62
    {
63
        $this->stmt = $stmt;
64
    }
65 305
66
    /**
67 305
     * {@inheritdoc}
68 305
     */
69
    public function bindValue($param, $value, int $type = ParameterType::STRING) : void
70
    {
71
        $this->bindParam($param, $value, $type);
72
    }
73 111
74
    /**
75 111
     * {@inheritdoc}
76 111
     */
77
    public function bindParam($param, &$variable, int $type = ParameterType::STRING, ?int $length = null) : void
78
    {
79
        assert(is_int($param));
80
81 133
        switch ($type) {
82
            case ParameterType::INTEGER:
83 133
                $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG);
84
                break;
85 133
86
            case ParameterType::LARGE_OBJECT:
87 39
                if (isset($this->lobs[$param])) {
88 39
                    [, $handle] = $this->lobs[$param];
89
                    fclose($handle);
90
                }
91 7
92
                $handle = $this->createTemporaryFile();
93
                $path   = stream_get_meta_data($handle)['uri'];
94
95
                $this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY);
96 7
97 7
                $this->lobs[$param] = [&$variable, $handle];
98
                break;
99 7
100
            default:
101 7
                $this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR);
102 7
                break;
103
        }
104
    }
105 111
106 111
    public function closeCursor() : void
107
    {
108 133
        $this->bindParam = [];
109
110 19
        if (! $this->result) {
111
            return;
112 19
        }
113
114 19
        db2_free_result($this->stmt);
115 3
116
        $this->result = false;
117
    }
118 16
119
    public function columnCount() : int
120 16
    {
121 16
        $count = db2_num_fields($this->stmt);
122
123 4
        if ($count !== false) {
124
            return $count;
125 4
        }
126
127 4
        return 0;
128 4
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function execute(?array $params = null) : void
134
    {
135
        if ($params === null) {
136
            ksort($this->bindParam);
137 298
138
            $params = [];
139 298
140 295
            foreach ($this->bindParam as $column => $value) {
141
                $params[] = $value;
142 295
            }
143
        }
144 295
145 133
        foreach ($this->lobs as [$source, $target]) {
146
            if (is_resource($source)) {
147
                $this->copyStreamToStream($source, $target);
148
149 298
                continue;
150 7
            }
151 3
152
            $this->writeStringToStream($source, $target);
153 3
        }
154
155
        $retval = db2_execute($this->stmt, $params);
156 5
157
        foreach ($this->lobs as [, $handle]) {
158
            fclose($handle);
159 298
        }
160
161 293
        $this->lobs = [];
162 7
163
        if ($retval === false) {
164
            throw DB2Exception::fromStatementError($this->stmt);
165 293
        }
166
167 293
        $this->result = true;
168
    }
169
170
    /**
171 293
     * {@inheritDoc}
172 293
     */
173
    public function fetchNumeric()
174 296
    {
175
        if (! $this->result) {
176 296
            return false;
177 296
        }
178
179
        return db2_fetch_array($this->stmt);
180
    }
181
182 1
    /**
183
     * {@inheritdoc}
184 1
     */
185
    public function fetchAssociative()
186
    {
187
        // do not try fetching from the statement if it's not expected to contain result
188
        // in order to prevent exceptional situation
189
        if (! $this->result) {
190 286
            return false;
191
        }
192
193
        return db2_fetch_assoc($this->stmt);
194 286
    }
195 9
196
    /**
197
     * {@inheritdoc}
198 277
     */
199 277
    public function fetchColumn()
200
    {
201 1
        return FetchUtils::fetchColumnFromNumeric($this);
202
    }
203
204 2
    /**
205
     * {@inheritdoc}
206
     */
207 146
    public function fetchAllNumeric() : array
208
    {
209
        return FetchUtils::fetchAllNumericByOne($this);
210 189
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215
    public function fetchAllAssociative() : array
216
    {
217
        return FetchUtils::fetchAllAssociativeByOne($this);
218
    }
219
220 105
    /**
221
     * {@inheritdoc}
222 105
     */
223
    public function fetchAllColumn() : array
224 105
    {
225
        return FetchUtils::fetchAllColumnByOne($this);
226 10
    }
227 10
228
    /**
229
     * @return Traversable<int,array<int,mixed>>
230 10
     *
231
     * @throws DriverException
232
     */
233 95
    public function iterateNumeric() : Traversable
234 87
    {
235
        return FetchUtils::iterateNumericByOne($this);
236
    }
237
238 105
    /**
239
     * @return Traversable<int,array<string,mixed>>
240
     *
241
     * @throws DriverException
242
     */
243
    public function iterateAssociative() : Traversable
244 183
    {
245
        return FetchUtils::iterateAssociativeByOne($this);
246 183
    }
247
248 183
    /**
249 16
     * @return Traversable<int,mixed>
250
     *
251
     * @throws DriverException
252 177
     */
253
    public function iterateColumn() : Traversable
254
    {
255 86
        return FetchUtils::iterateColumnByOne($this);
256
    }
257 86
258
    public function rowCount() : int
259
    {
260
        return @db2_num_rows($this->stmt);
261
    }
262
263
    /**
264
     * @param int   $position Parameter position
265
     * @param mixed $variable
266 133
     *
267
     * @throws DB2Exception
268 133
     */
269
    private function bind(int $position, &$variable, int $parameterType, int $dataType) : void
270 133
    {
271
        $this->bindParam[$position] =& $variable;
272
273 133
        if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
274
            throw DB2Exception::fromStatementError($this->stmt);
275
        }
276
    }
277
278
    /**
279
     * @return resource
280 7
     *
281
     * @throws DB2Exception
282 7
     */
283
    private function createTemporaryFile()
284 7
    {
285
        $handle = @tmpfile();
286
287
        if ($handle === false) {
288 7
            throw new DB2Exception('Could not create temporary file: ' . error_get_last()['message']);
289
        }
290
291
        return $handle;
292
    }
293
294
    /**
295
     * @param resource $source
296
     * @param resource $target
297 3
     *
298
     * @throws DB2Exception
299 3
     */
300
    private function copyStreamToStream($source, $target) : void
301
    {
302 3
        if (@stream_copy_to_stream($source, $target) === false) {
303
            throw new DB2Exception('Could not copy source stream to temporary file: ' . error_get_last()['message']);
304
        }
305
    }
306
307
    /**
308
     * @param resource $target
309 5
     *
310
     * @throws DB2Exception
311 5
     */
312
    private function writeStringToStream(string $string, $target) : void
313
    {
314 5
        if (@fwrite($target, $string) === false) {
315
            throw new DB2Exception('Could not write string to temporary file: ' . error_get_last()['message']);
316
        }
317
    }
318
}
319