Completed
Push — master ( 29cccc...37ec5c )
by Ondřej
04:37
created

QueryResult::initCols()   C

Complexity

Conditions 11
Paths 9

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 19
nc 9
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Ivory\Result;
3
4
use Ivory\Exception\NotImplementedException;
5
use Ivory\Exception\ResultException;
6
use Ivory\Relation\Column;
7
use Ivory\Relation\FilteredRelation;
8
use Ivory\Relation\IRelation;
9
use Ivory\Relation\ITuple;
10
use Ivory\Relation\ProjectedRelation;
11
use Ivory\Relation\RelationMacros;
12
use Ivory\Relation\RenamedRelation;
13
use Ivory\Relation\Tuple;
14
use Ivory\Type\ITypeDictionary;
15
16
class QueryResult extends Result implements IQueryResult
17
{
18
    use RelationMacros;
19
20
    private $numRows;
21
    /** @var Column[] */
22
    private $columns;
23
    /** @var int[] map: column name => offset of the first column of the name */
24
    private $colNameMap;
25
26
27
    /**
28
     * @param resource $resultHandler the result, with the internal pointer at the beginning
29
     * @param ITypeDictionary $typeDictionary
30
     * @param string|null $lastNotice last notice captured on the connection
31
     */
32
    public function __construct($resultHandler, ITypeDictionary $typeDictionary, string $lastNotice = null)
33
    {
34
        parent::__construct($resultHandler, $lastNotice);
35
36
        $this->numRows = $this->fetchNumRows();
37
        $this->initCols($typeDictionary);
38
    }
39
40
    private function fetchNumRows(): int
41
    {
42
        $numRows = pg_num_rows($this->handler);
43
        if ($numRows >= 0 && $numRows !== null) { // NOTE: besides -1, pg_num_rows() might return NULL on error
44
            return $numRows;
45
        } else {
46
            throw new ResultException('Error retrieving number of rows of the result.');
47
        }
48
    }
49
50
    private function initCols(ITypeDictionary $typeDictionary)
51
    {
52
        $numFields = pg_num_fields($this->handler);
53
        if ($numFields < 0 || $numFields === null) {
54
            throw new ResultException('Error retrieving number of fields of the result.');
55
        }
56
        $this->columns = [];
57
        $this->colNameMap = [];
58
        for ($i = 0; $i < $numFields; $i++) {
59
            /* NOTE: pg_field_type() cannot be used for simplicity - multiple types of the same name might exist in
60
             *       different schemas. Thus, the only reasonable way to recognize the types is using their OIDs,
61
             *       returned by pg_field_type_oid(). Up to some extreme cases, within a given database, the same OID
62
             *       will always refer to the same data type.
63
             */
64
            $name = pg_field_name($this->handler, $i);
65
            if ($name === false || $name === null) { // NOTE: besides false, pg_field_name() might return NULL on error
66
                throw new ResultException("Error retrieving name of result column $i.");
67
            }
68
            if ($name == '?column?') {
69
                $name = null;
70
            }
71
            $typeOid = pg_field_type_oid($this->handler, $i);
72
            if ($typeOid === false || $typeOid === null) { // NOTE: besides false, pg_field_type_oid() might return NULL on error
73
                throw new ResultException("Error retrieving type OID of result column $i.");
74
            }
75
            $type = $typeDictionary->requireTypeByOid($typeOid);
76
77
            $this->columns[] = new Column($this, $i, $name, $type);
78
79
            if ($name !== null && !isset($this->colNameMap[$name])) {
80
                $this->colNameMap[$name] = $i;
81
            }
82
        }
83
    }
84
85
86
    //region IRelation
87
88
    public function getColumns()
89
    {
90
        return $this->columns;
91
    }
92
93
    public function filter($decider): IRelation
94
    {
95
        return new FilteredRelation($this, $decider);
96
    }
97
98
    public function project($columns): IRelation
99
    {
100
        return new ProjectedRelation($this, $columns);
101
    }
102
103
    public function rename($renamePairs): IRelation
104
    {
105
        return new RenamedRelation($this, $renamePairs);
106
    }
107
108
    public function uniq($hasher = null, $comparator = null): IRelation
109
    {
110
        throw new NotImplementedException();
111
    }
112
113
    public function tuple(int $offset = 0): ITuple
114
    {
115 View Code Duplication
        if ($offset >= $this->numRows || $offset < -$this->numRows) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
            throw new \OutOfBoundsException("Offset $offset is out of the result bounds [0,{$this->numRows})");
117
        }
118
119
        $effectiveOffset = ($offset >= 0 ? $offset : $this->numRows + $offset);
120
121
        $rawData = pg_fetch_row($this->handler, $effectiveOffset);
122
        if ($rawData === false || $rawData === null) {
123
            throw new ResultException("Error fetching row at offset $offset");
124
        }
125
126
        $data = [];
127
        foreach ($this->columns as $i => $col) {
128
            $data[$i] = $col->getType()->parseValue($rawData[$i]);
129
        }
130
131
        return new Tuple($data, $this->columns, $this->colNameMap);
0 ignored issues
show
Documentation introduced by
$this->colNameMap is of type array<integer,integer>, but the function expects a array<integer,object<int>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
132
    }
133
134
    //endregion
135
136
    //region \Countable
137
138
    public function count()
139
    {
140
        return $this->numRows;
141
    }
142
143
    //endregion
144
}
145