Passed
Push — master ( 5a27f9...28edde )
by Igor
02:52
created

Statement::getRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 3
rs 10
ccs 0
cts 2
cp 0
cc 1
eloc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace ClickHouseDB;
4
5
use ClickHouseDB\Exception\DatabaseException;
6
use ClickHouseDB\Exception\QueryException;
7
use ClickHouseDB\Query\Query;
8
use ClickHouseDB\Transport\CurlerRequest;
9
use ClickHouseDB\Transport\CurlerResponse;
10
11
class Statement
12
{
13
    /**
14
     * @var string|mixed
15
     */
16
    private $_rawData;
17
18
    /**
19
     * @var int
20
     */
21
    private $_http_code = -1;
22
23
    /**
24
     * @var CurlerRequest
25
     */
26
    private $_request = null;
27
28
    /**
29
     * @var bool
30
     */
31
    private $_init = false;
32
33
    /**
34
     * @var Query
35
     */
36
    private $query;
37
38
    /**
39
     * @var mixed
40
     */
41
    private $format;
42
43
    /**
44
     * @var string
45
     */
46
    private $sql = '';
47
48
    /**
49
     * @var array
50
     */
51
    private $meta;
52
53
    /**
54
     * @var array
55
     */
56
    private $data;
57
58
    /**
59
     * @var array
60
     */
61
    private $totals;
62
63
    /**
64
     * @var array
65
     */
66
    private $extremes;
67
68
    /**
69
     * @var int
70
     */
71
    private $rows;
72
73
    /**
74
     * @var bool|integer
75
     */
76
    private $rows_before_limit_at_least = false;
77
78
    /**
79
     * @var array
80
     */
81
    private $array_data = [];
82
83
    /**
84
     * @var array|null
85
     */
86
    private $statistics = null;
87
88
89 36
    public function __construct(CurlerRequest $request)
90
    {
91 36
        $this->_request = $request;
92 36
        $this->format = $this->_request->getRequestExtendedInfo('format');
93 36
        $this->query = $this->_request->getRequestExtendedInfo('query');
94 36
        $this->sql = $this->_request->getRequestExtendedInfo('sql');
95 36
    }
96
97
    /**
98
     * @return CurlerRequest
99
     */
100
    public function getRequest()
101
    {
102
        return $this->_request;
103
    }
104
105
    /**
106
     * @return CurlerResponse
107
     * @throws Exception\TransportException
108
     */
109 35
    private function response()
110
    {
111 35
        return $this->_request->response();
112
    }
113
114
    /**
115
     * @return mixed
116
     * @throws Exception\TransportException
117
     */
118 1
    public function responseInfo()
119
    {
120 1
        return $this->response()->info();
121
    }
122
123
    /**
124
     * @return mixed|string
125
     */
126 9
    public function sql()
127
    {
128 9
        return $this->sql;
129
    }
130
131
    /**
132
     * @param string $body
133
     * @return array|bool
134
     */
135 5
    private function parseErrorClickHouse($body)
136
    {
137 5
        $body = trim($body);
138 5
        $mathes = [];
139
140
        // Code: 115, e.displayText() = DB::Exception: Unknown setting readonly[0], e.what() = DB::Exception
141
        // Code: 192, e.displayText() = DB::Exception: Unknown user x, e.what() = DB::Exception
142
        // Code: 60, e.displayText() = DB::Exception: Table default.ZZZZZ doesn't exist., e.what() = DB::Exception
143
144 5
        if (preg_match("%Code: (\d+),\se\.displayText\(\) \=\s*DB\:\:Exception\s*:\s*(.*)\,\s*e\.what.*%ius", $body, $mathes)) {
145 5
            return ['code' => $mathes[1], 'message' => $mathes[2]];
146
        }
147
148
        return false;
149
    }
150
151
    /**
152
     * @return bool
153
     * @throws Exception\TransportException
154
     */
155 10
    public function error()
156
    {
157 10
        if (!$this->isError()) {
158 2
            return false;
159
        }
160
161 8
        $body = $this->response()->body();
162 8
        $error_no = $this->response()->error_no();
163 8
        $error = $this->response()->error();
164
165 8
        if (!$error_no && !$error) {
166 5
            $parse = $this->parseErrorClickHouse($body);
167
168 5
            if ($parse) {
169 5
                throw new DatabaseException($parse['message'] . "\nIN:" . $this->sql(), $parse['code']);
170
            } else {
171
                $code = $this->response()->http_code();
172
                $message = "HttpCode:" . $this->response()->http_code() . " ; " . $this->response()->error() . " ;" . $body;
173
            }
174
        } else {
175 3
            $code = $error_no;
176 3
            $message = $this->response()->error();
177
        }
178
179 3
        throw new QueryException($message, $code);
180
    }
181
182
    /**
183
     * @return bool
184
     * @throws Exception\TransportException
185
     */
186 35
    public function isError()
187
    {
188 35
        return ($this->response()->http_code() !== 200 || $this->response()->error_no());
189
    }
190
191
    /**
192
     * @return bool
193
     * @throws Exception\TransportException
194
     */
195 35
    private function check()
196
    {
197 35
        if (!$this->_request->isResponseExists()) {
198
            throw new QueryException('Not have response');
199
        }
200
201 35
        if ($this->isError()) {
202 3
            $this->error();
203
        }
204
205 35
        return true;
206
    }
207
208
    /**
209
     * @return bool
210
     * @throws Exception\TransportException
211
     */
212 35
    private function init()
213
    {
214 35
        if ($this->_init) {
215
            return false;
216
        }
217
218 35
        $this->check();
219
220
221 35
        $this->_rawData = $this->response()->rawDataOrJson($this->format);
222
223 35
        if (!$this->_rawData) {
224
            $this->_init = true;
225
            return false;
226
        }
227
228 35
        foreach (['meta', 'data', 'totals', 'extremes', 'rows', 'rows_before_limit_at_least', 'statistics'] as $key) {
229 35
            if (isset($this->_rawData[$key])) {
230 35
                $this->{$key} = $this->_rawData[$key];
231
            }
232
        }
233
234 35
        if (empty($this->meta)) {
235
            throw  new QueryException('Can`t find meta');
236
        }
237
238 35
        $this->array_data = [];
239 35
        foreach ($this->data as $rows) {
240 35
            $r = [];
241
242 35
            foreach ($this->meta as $meta) {
243 35
                $r[$meta['name']] = $rows[$meta['name']];
244
            }
245
246 35
            $this->array_data[] = $r;
247
        }
248
249 35
        return true;
250
    }
251
252
    /**
253
     * @return array
254
     * @throws \Exception
255
     */
256
    public function extremes()
257
    {
258
        $this->init();
259
        return $this->extremes;
260
    }
261
262
    /**
263
     * @return mixed
264
     * @throws Exception\TransportException
265
     */
266 1
    public function totalTimeRequest()
267
    {
268 1
        $this->check();
269 1
        return $this->response()->total_time();
270
271
    }
272
273
    /**
274
     * @return array
275
     * @throws \Exception
276
     */
277 1
    public function extremesMin()
278
    {
279 1
        $this->init();
280
281 1
        if (empty($this->extremes['min'])) {
282
            return [];
283
        }
284
285 1
        return $this->extremes['min'];
286
    }
287
288
    /**
289
     * @return array
290
     * @throws \Exception
291
     */
292
    public function extremesMax()
293
    {
294
        $this->init();
295
296
        if (empty($this->extremes['max'])) {
297
            return [];
298
        }
299
300
        return $this->extremes['max'];
301
    }
302
303
    /**
304
     * @return array
305
     * @throws Exception\TransportException
306
     */
307 1
    public function totals()
308
    {
309 1
        $this->init();
310 1
        return $this->totals;
311
    }
312
313
    /**
314
     *
315
     */
316
    public function dumpRaw()
317
    {
318
        print_r($this->_rawData);
319
    }
320
321
    /**
322
     *
323
     */
324
    public function dump()
325
    {
326
        $this->_request->dump();
327
        $this->response()->dump();
328
    }
329
330
    /**
331
     * @return bool|int
332
     * @throws Exception\TransportException
333
     */
334 2
    public function countAll()
335
    {
336 2
        $this->init();
337 2
        return $this->rows_before_limit_at_least;
338
    }
339
340
    /**
341
     * @param bool $key
342
     * @return array|mixed|null
343
     * @throws Exception\TransportException
344
     */
345
    public function statistics($key = false)
346
    {
347
        $this->init();
348
        if ($key)
349
        {
350
            if (!is_array($this->statistics)) {
351
                return null;
352
            }
353
            if (!isset($this->statistics[$key])) {
354
                return null;
355
            }
356
            return $this->statistics[$key];
357
        }
358
        return $this->statistics;
359
    }
360
361
    /**
362
     * @return int
363
     * @throws Exception\TransportException
364
     */
365 6
    public function count()
366
    {
367 6
        $this->init();
368 6
        return $this->rows;
369
    }
370
371
    /**
372
     * @return mixed|string
373
     * @throws Exception\TransportException
374
     */
375 2
    public function rawData()
376
    {
377 2
        if ($this->_init) {
378
            return $this->_rawData;
379
        }
380
381 2
        $this->check();
382
383 2
        return $this->response()->rawDataOrJson($this->format);
384
    }
385
386
    /**
387
     * @param string $key
388
     * @return mixed|null
389
     * @throws Exception\TransportException
390
     */
391 33
    public function fetchOne($key = '')
392
    {
393 33
        $this->init();
394
395 33
        if (isset($this->array_data[0])) {
396 33
            if ($key) {
397 33
                if (isset($this->array_data[0][$key])) {
398 33
                    return $this->array_data[0][$key];
399
                } else {
400
                    return null;
401
                }
402
            }
403
404 2
            return $this->array_data[0];
405
        }
406
407
        return null;
408
    }
409
410
    /**
411
     * @param string|null $path
412
     * @return array
413
     * @throws Exception\TransportException
414
     */
415 4
    public function rowsAsTree($path)
416
    {
417 4
        $this->init();
418
419 4
        $out = [];
420 4
        foreach ($this->array_data as $row) {
421 4
            $d = $this->array_to_tree($row, $path);
422 4
            $out = array_replace_recursive($d, $out);
423
        }
424
425 4
        return $out;
426
    }
427
428
    /**
429
     * Return size_upload,upload_content,speed_upload,time_request
430
     *
431
     * @return array
432
     * @throws Exception\TransportException
433
     */
434
    public function info_upload()
435
    {
436
        $this->check();
437
        return [
438
            'size_upload'    => $this->response()->size_upload(),
439
            'upload_content' => $this->response()->upload_content_length(),
440
            'speed_upload'   => $this->response()->speed_upload(),
441
            'time_request'   => $this->response()->total_time()
442
        ];
443
    }
444
445
    /**
446
     * Return size_upload,upload_content,speed_upload,time_request,starttransfer_time,size_download,speed_download
447
     *
448
     * @return array
449
     * @throws Exception\TransportException
450
     */
451 1
    public function info()
452
    {
453 1
        $this->check();
454
        return [
455 1
            'starttransfer_time'    => $this->response()->starttransfer_time(),
456 1
            'size_download'    => $this->response()->size_download(),
457 1
            'speed_download'    => $this->response()->speed_download(),
458 1
            'size_upload'    => $this->response()->size_upload(),
459 1
            'upload_content' => $this->response()->upload_content_length(),
460 1
            'speed_upload'   => $this->response()->speed_upload(),
461 1
            'time_request'   => $this->response()->total_time()
462
        ];
463
    }
464
465
    /**
466
     * get format in sql
467
     * @return mixed
468
     */
469 1
    public function getFormat()
470
    {
471 1
        return $this->format;
472
    }
473
474
    /**
475
     * @return array
476
     * @throws Exception\TransportException
477
     */
478 3
    public function rows()
479
    {
480 3
        $this->init();
481 2
        return $this->array_data;
482
    }
483
484
    /**
485
     * @param array|string $arr
486
     * @param null|string|array $path
487
     * @return array
488
     */
489 4
    private function array_to_tree($arr, $path = null)
490
    {
491 4
        if (is_array($path)) {
492
            $keys = $path;
493
        } else {
494 4
            $args = func_get_args();
495 4
            array_shift($args);
496
497 4
            if (sizeof($args) < 2) {
498 4
                $separator = '.';
499 4
                $keys = explode($separator, $path);
500
            } else {
501
                $keys = $args;
502
            }
503
        }
504
505
        //
506 4
        $tree = $arr;
507 4
        while (count($keys)) {
508 4
            $key = array_pop($keys);
509
510 4
            if (isset($arr[$key])) {
511 4
                $val = $arr[$key];
512
            } else {
513
                $val = $key;
514
            }
515
516 4
            $tree = array($val => $tree);
517
        }
518 4
        if (!is_array($tree)) {
519
            return [];
520
        }
521 4
        return $tree;
522
    }
523
}
524