Failed Conditions
Pull Request — master (#4007)
by Sergei
11:47
created

DB2Statement::columnCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.3149

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 9
ccs 4
cts 7
cp 0.5714
crap 2.3149
rs 10
c 0
b 0
f 0
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 301
    public function __construct($stmt)
62
    {
63 301
        $this->stmt = $stmt;
64 301
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 109
    public function bindValue($param, $value, int $type = ParameterType::STRING) : void
70
    {
71 109
        $this->bindParam($param, $value, $type);
72 109
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77 130
    public function bindParam($param, &$variable, int $type = ParameterType::STRING, ?int $length = null) : void
78
    {
79 130
        assert(is_int($param));
80
81 130
        switch ($type) {
82
            case ParameterType::INTEGER:
83 39
                $this->bind($param, $variable, DB2_PARAM_IN, DB2_LONG);
84 39
                break;
85
86
            case ParameterType::LARGE_OBJECT:
87 7
                if (isset($this->lobs[$param])) {
88
                    [, $handle] = $this->lobs[$param];
89
                    fclose($handle);
90
                }
91
92 7
                $handle = $this->createTemporaryFile();
93 7
                $path   = stream_get_meta_data($handle)['uri'];
94
95 7
                $this->bind($param, $path, DB2_PARAM_FILE, DB2_BINARY);
96
97 7
                $this->lobs[$param] = [&$variable, $handle];
98 7
                break;
99
100
            default:
101 108
                $this->bind($param, $variable, DB2_PARAM_IN, DB2_CHAR);
102 108
                break;
103
        }
104 130
    }
105
106 19
    public function closeCursor() : void
107
    {
108 19
        $this->bindParam = [];
109
110 19
        if (! $this->result) {
111 3
            return;
112
        }
113
114 16
        db2_free_result($this->stmt);
115
116 16
        $this->result = false;
117 16
    }
118
119 3
    public function columnCount() : int
120
    {
121 3
        $count = db2_num_fields($this->stmt);
122
123 3
        if ($count !== false) {
124 3
            return $count;
125
        }
126
127
        return 0;
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133 294
    public function execute(?array $params = null) : void
134
    {
135 294
        if ($params === null) {
136 291
            ksort($this->bindParam);
137
138 291
            $params = [];
139
140 291
            foreach ($this->bindParam as $column => $value) {
141 130
                $params[] = $value;
142
            }
143
        }
144
145 294
        foreach ($this->lobs as [$source, $target]) {
146 7
            if (is_resource($source)) {
147 3
                $this->copyStreamToStream($source, $target);
148
149 3
                continue;
150
            }
151
152 5
            $this->writeStringToStream($source, $target);
153
        }
154
155 294
        $retval = db2_execute($this->stmt, $params);
156
157 289
        foreach ($this->lobs as [, $handle]) {
158 7
            fclose($handle);
159
        }
160
161 289
        $this->lobs = [];
162
163 289
        if ($retval === false) {
164
            throw DB2Exception::fromStatementError($this->stmt);
165
        }
166
167 289
        $this->result = true;
168 289
    }
169
170
    /**
171
     * {@inheritDoc}
172
     */
173 189
    public function fetchNumeric()
174
    {
175 189
        if (! $this->result) {
176 3
            return false;
177
        }
178
179 186
        return db2_fetch_array($this->stmt);
180
    }
181
182
    /**
183
     * {@inheritdoc}
184
     */
185 153
    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 153
        if (! $this->result) {
190 6
            return false;
191
        }
192
193 147
        return db2_fetch_assoc($this->stmt);
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     */
199 182
    public function fetchOne()
200
    {
201 182
        return FetchUtils::fetchOneFromNumeric($this);
202
    }
203
204
    /**
205
     * {@inheritdoc}
206
     */
207 2
    public function fetchAllNumeric() : array
208
    {
209 2
        return FetchUtils::fetchAllNumericByOne($this);
210
    }
211
212
    /**
213
     * {@inheritdoc}
214
     */
215 93
    public function fetchAllAssociative() : array
216
    {
217 93
        return FetchUtils::fetchAllAssociativeByOne($this);
218
    }
219
220
    /**
221
     * {@inheritdoc}
222
     */
223 9
    public function fetchColumn() : array
224
    {
225 9
        return FetchUtils::fetchColumnByOne($this);
226
    }
227
228
    /**
229
     * @return Traversable<int,array<int,mixed>>
230
     *
231
     * @throws DriverException
232
     */
233
    public function iterateNumeric() : Traversable
234
    {
235
        return FetchUtils::iterateNumericByOne($this);
236
    }
237
238
    /**
239
     * @return Traversable<int,array<string,mixed>>
240
     *
241
     * @throws DriverException
242
     */
243 1
    public function iterateAssociative() : Traversable
244
    {
245 1
        return FetchUtils::iterateAssociativeByOne($this);
246
    }
247
248
    /**
249
     * @return Traversable<int,mixed>
250
     *
251
     * @throws DriverException
252
     */
253
    public function iterateColumn() : Traversable
254
    {
255
        return FetchUtils::iterateColumnByOne($this);
256
    }
257
258 86
    public function rowCount() : int
259
    {
260 86
        return @db2_num_rows($this->stmt);
261
    }
262
263
    /**
264
     * @param int   $position Parameter position
265
     * @param mixed $variable
266
     *
267
     * @throws DB2Exception
268
     */
269 130
    private function bind(int $position, &$variable, int $parameterType, int $dataType) : void
270
    {
271 130
        $this->bindParam[$position] =& $variable;
272
273 130
        if (! db2_bind_param($this->stmt, $position, 'variable', $parameterType, $dataType)) {
274
            throw DB2Exception::fromStatementError($this->stmt);
275
        }
276 130
    }
277
278
    /**
279
     * @return resource
280
     *
281
     * @throws DB2Exception
282
     */
283 7
    private function createTemporaryFile()
284
    {
285 7
        $handle = @tmpfile();
286
287 7
        if ($handle === false) {
288
            throw new DB2Exception('Could not create temporary file: ' . error_get_last()['message']);
289
        }
290
291 7
        return $handle;
292
    }
293
294
    /**
295
     * @param resource $source
296
     * @param resource $target
297
     *
298
     * @throws DB2Exception
299
     */
300 3
    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 3
    }
306
307
    /**
308
     * @param resource $target
309
     *
310
     * @throws DB2Exception
311
     */
312 5
    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 5
    }
318
}
319