Completed
Push — master ( eec500...6cd1ad )
by Edgard
08:18
created

QueryBuilder   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 273
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 7

Test Coverage

Coverage 22.83%

Importance

Changes 0
Metric Value
wmc 46
lcom 2
cbo 7
dl 0
loc 273
ccs 29
cts 127
cp 0.2283
rs 8.3999
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A truncateTable() 0 4 1
B resetSequence() 0 18 5
D checkIntegrity() 0 44 10
B buildOrderByAndLimit() 0 30 6
B buildLimit() 0 16 5
A alterColumn() 0 6 1
B buildCompositeInCondition() 0 24 6
D insert() 0 32 9
A selectExists() 0 4 1
A dropCommentFromColumn() 0 4 1
A dropCommentFromTable() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like QueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryBuilder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @link http://www.yiiframework.com/
5
 * @copyright Copyright (c) 2008 Yii Software LLC
6
 * @license http://www.yiiframework.com/license/
7
 */
8
9
namespace edgardmessias\db\ibm\db2;
10
11
use yii\base\InvalidParamException;
12
use yii\db\Expression;
13
14
/**
15
 * QueryBuilder is the query builder for DB2 databases.
16
 * 
17
 * @property Connection $db Connetion
18
 *
19
 * @author Edgard Lorraine Messias <[email protected]>
20
 * @author Nikita Verkhovin <[email protected]>
21
 */
22
class QueryBuilder extends \yii\db\QueryBuilder
23
{
24
    public $typeMap = [
25
        Schema::TYPE_PK => 'integer NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) PRIMARY KEY',
26
        Schema::TYPE_BIGPK => 'bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) PRIMARY KEY',
27
        Schema::TYPE_STRING => 'varchar(255)',
28
        Schema::TYPE_TEXT => 'clob',
29
        Schema::TYPE_SMALLINT => 'smallint',
30
        Schema::TYPE_INTEGER => 'integer',
31
        Schema::TYPE_BIGINT => 'bigint',
32
        Schema::TYPE_FLOAT => 'float',
33
        Schema::TYPE_DOUBLE => 'double',
34
        Schema::TYPE_DECIMAL => 'decimal(10,0)',
35
        Schema::TYPE_DATETIME => 'timestamp',
36
        Schema::TYPE_TIMESTAMP => 'timestamp',
37
        Schema::TYPE_TIME => 'time',
38
        Schema::TYPE_DATE => 'date',
39
        Schema::TYPE_BINARY => 'blob',
40
        Schema::TYPE_BOOLEAN => 'smallint',
41
        Schema::TYPE_MONEY => 'decimal(19,4)',
42
    ];
43
44
    /**
45
     * Builds a SQL statement for truncating a DB table.
46
     * @param string $table the table to be truncated. The name will be properly quoted by the method.
47
     * @return string the SQL statement for truncating a DB table.
48
     */
49
    public function truncateTable($table)
50
    {
51
        return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table) . ' IMMEDIATE';
52
    }
53
54
    /**
55
     * @inheritdoc
56
     */
57
    public function resetSequence($tableName, $value = null)
58
    {
59
        $table = $this->db->getTableSchema($tableName);
60
61
        if ($table !== null && isset($table->columns[$table->sequenceName])) {
62
            if ($value === null) {
63
                $sql = 'SELECT MAX("'. $table->sequenceName .'") FROM "'. $tableName . '"';
64
                $value = $this->db->createCommand($sql)->queryScalar() + 1;
65
            } else {
66
                $value = (int) $value;
67
            }
68
            return 'ALTER TABLE "' . $tableName . '" ALTER COLUMN "'.$table->sequenceName.'" RESTART WITH ' . $value;
69
        } elseif ($table === null) {
70
            throw new InvalidParamException("Table not found: $tableName");
71
        } else {
72
            throw new InvalidParamException("There is no sequence associated with table '$tableName'.");
73
        }
74
    }
75
76
    /**
77
     * Builds a SQL statement for enabling or disabling integrity check.
78
     * @param boolean $check whether to turn on or off the integrity check.
79
     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
80
     * @param string $table the table name. Defaults to empty string, meaning that no table will be changed.
81
     * @return string the SQL statement for checking integrity
82
     * @throws \yii\base\NotSupportedException if this is not supported by the underlying DBMS
83
     * @see http://www-01.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000998.html?cp=SSEPGG_10.5.0%2F2-12-7-227
84
     */
85
    public function checkIntegrity($check = true, $schema = '', $table = '')
86
    {
87
        if ($table) {
88
            $tableNames = [$table];
89
        } else {
90
            if (!$schema) {
91
                $schema = $this->db->defaultSchema;
92
            }
93
94
            //Return only tables
95
            $sql = "SELECT t.tabname FROM syscat.tables AS t"
96
                    . " WHERE t.type in ('T') AND t.ownertype != 'S'";
97
98
            /**
99
             * Filter by integrity pending
100
             * @see http://www-01.ibm.com/support/knowledgecenter/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0001063.html
101
             */
102
            if ($check) {
103
                $sql .= " AND t.status = 'C'";
104
            }
105
            if ($schema) {
106
                $sql .= ' AND t.tabschema = :schema';
107
            }
108
            
109
            $command = $this->db->createCommand($sql);
110
            if ($schema) {
111
                $command->bindValue(':schema', $schema);
112
            }
113
114
            $tableNames = $command->queryColumn();
115
        }
116
117
        if (empty($tableNames)) {
118
            return '';
119
        }
120
121
        $quotedTableNames = [];
122
        foreach ($tableNames as $tableName) {
123
            $quotedTableNames[] = $this->db->quoteTableName($tableName) . ($check? '' : ' ALL');
124
        }
125
126
        $enable = $check ? 'CHECKED' : 'UNCHECKED';
127
        return 'SET INTEGRITY FOR ' . implode(', ', $quotedTableNames) . ' IMMEDIATE ' . $enable. ';';
128
    }
129
130
    /**
131
     * @inheritdoc
132
     */
133 97
    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
134
    {
135 97
        $limitOffsetStatment = $this->buildLimit($limit, $offset);
136 97
        if ($limitOffsetStatment != '') {
137
            $sql = str_replace(':query', $sql, $limitOffsetStatment);
138
139
            //convert "item"."id" to "id" to use in OVER()
140
            $newOrderBy = [];
141
142
            if(!empty($orderBy)){
143
                foreach ($orderBy as $name => $direction) {
144
                    if(is_string($name)){
145
                        $e = explode('.', $name);
146
                        $name = array_pop($e);
147
                    }
148
                    $newOrderBy[$name] = $direction;
149
                }
150
            }
151
152
            $orderByStatment = $this->buildOrderBy($newOrderBy);
153
154
            $sql = str_replace(':order', $orderByStatment,$sql);
155
        }else{
156 97
            $orderByStatment = $this->buildOrderBy($orderBy);
157 97
            if ($orderByStatment !== '') {
158 1
                $sql .= $this->separator . $orderByStatment;
159 1
            }
160
        }
161 97
        return $sql;
162
    }
163
164
    /**
165
     * @inheritdoc
166
     */
167 97
    public function buildLimit($limit, $offset)
168
    {
169 97
        if (!$this->hasLimit($limit) && !$this->hasOffset($offset)) {
170 97
            return '';
171
        }
172
173
        $limitOffsetStatment = 'SELECT * FROM (SELECT SUBQUERY_.*, ROW_NUMBER() OVER(:order) AS RN_ FROM ( :query ) AS SUBQUERY_) as t WHERE :offset :limit';
174
175
        $replacement = $this->hasOffset($offset) ? 't.RN_ > ' . $offset : 't.RN_ > 0';
176
        $limitOffsetStatment = str_replace(':offset', $replacement, $limitOffsetStatment);
177
178
        $replacement = $this->hasLimit($limit) ? 'AND t.RN_ <= ' . ($limit + $offset) : '';
179
        $limitOffsetStatment = str_replace(':limit', $replacement, $limitOffsetStatment);
180
181
        return $limitOffsetStatment;
182
    }
183
184
    /**
185
     * @inheritdoc
186
     */
187
    public function alterColumn($table, $column, $type)
188
    {
189
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
190
        . $this->db->quoteColumnName($column) . ' SET DATA TYPE '
191
        . $this->getColumnType($type);
192
    }
193
194
    /**
195
     * @inheritdoc
196
     */
197 4
    protected function buildCompositeInCondition($operator, $columns, $values, &$params)
198
    {
199 4
        $vss = [];
200 4
        foreach ($values as $value) {
201 4
            $vs = [];
202 4
            foreach ($columns as $column) {
203 4
                if (isset($value[$column])) {
204 4
                    $phName = self::PARAM_PREFIX . count($params);
205 4
                    $params[$phName] = $value[$column];
206 4
                    $vs[] = $phName;
207 4
                } else {
208
                    $vs[] = 'NULL';
209
                }
210 4
            }
211 4
            $vss[] = 'select ' . implode(', ', $vs) . ' from SYSIBM.SYSDUMMY1';
212 4
        }
213
214 4
        $sqlColumns = [];
215 4
        foreach ($columns as $i => $column) {
216 4
            $sqlColumns[] = strpos($column, '(') === false ? $this->db->quoteColumnName($column) : $column;
217 4
        }
218
219 4
        return '(' . implode(', ', $sqlColumns) . ") $operator (" . implode(' UNION ', $vss) . ')';
220
    }
221
222
    /**
223
     * @inheritdoc
224
     */
225
    public function insert($table, $columns, &$params)
226
    {
227
        $schema = $this->db->getSchema();
228
        if (($tableSchema = $schema->getTableSchema($table)) !== null) {
229
            $columnSchemas = $tableSchema->columns;
230
        } else {
231
            $columnSchemas = [];
232
        }
233
        $names = [];
234
        $placeholders = [];
235
        foreach ($columns as $name => $value) {
236
            $names[] = $schema->quoteColumnName($name);
237
            if ($value instanceof Expression) {
238
                $placeholders[] = $value->expression;
239
                foreach ($value->params as $n => $v) {
240
                    $params[$n] = $v;
241
                }
242
            } else {
243
                $phName = self::PARAM_PREFIX . count($params);
244
                $placeholders[] = $phName;
245
                $params[$phName] = !is_array($value) && isset($columnSchemas[$name]) ? $columnSchemas[$name]->dbTypecast($value) : $value;
246
            }
247
        }
248
249
        if (empty($placeholders)) {
250
            $placeholders = array_fill(0, count($columnSchemas), 'DEFAULT');
251
        }
252
253
        return 'INSERT INTO ' . $schema->quoteTableName($table)
254
        . (!empty($names) ? ' (' . implode(', ', $names) . ')' : '')
255
        . ' VALUES (' . implode(', ', $placeholders) . ')';
256
    }
257
    
258
    /**
259
     * Creates a SELECT EXISTS() SQL statement.
260
     * @param string $rawSql the subquery in a raw form to select from.
261
     * @return string the SELECT EXISTS() SQL statement.
262
     *
263
     * @since 2.0.8
264
     */
265
    public function selectExists($rawSql)
266
    {
267
        return 'SELECT CASE WHEN EXISTS(' . $rawSql . ') THEN 1 ELSE 0 END FROM SYSIBM.SYSDUMMY1';
268
    }
269
270
    /**
271
     * Builds a SQL command for adding comment to column
272
     *
273
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
274
     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
275
     * @return string the SQL statement for adding comment on column
276
     * @since 2.0.8
277
     */
278
    public function dropCommentFromColumn($table, $column)
279
    {
280
        return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . " IS ''";
281
    }
282
283
    /**
284
     * Builds a SQL command for adding comment to table
285
     *
286
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
287
     * @return string the SQL statement for adding comment on column
288
     * @since 2.0.8
289
     */
290
    public function dropCommentFromTable($table)
291
    {
292
        return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . " IS ''";
293
    }
294
}
295