Issues (15)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/QueryBuilder.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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\Constraint;
13
use yii\db\Expression;
14
use yii\db\Query;
15
16
/**
17
 * QueryBuilder is the query builder for DB2 databases.
18
 * 
19
 * @property Connection $db Connetion
20
 *
21
 * @author Edgard Lorraine Messias <[email protected]>
22
 * @author Nikita Verkhovin <[email protected]>
23
 */
24
class QueryBuilder extends \yii\db\QueryBuilder
25
{
26
    public $typeMap = [
27
        Schema::TYPE_PK => 'integer NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) PRIMARY KEY',
28
        Schema::TYPE_BIGPK => 'bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1) PRIMARY KEY',
29
        Schema::TYPE_STRING => 'varchar(255)',
30
        Schema::TYPE_TEXT => 'clob',
31
        Schema::TYPE_SMALLINT => 'smallint',
32
        Schema::TYPE_INTEGER => 'integer',
33
        Schema::TYPE_BIGINT => 'bigint',
34
        Schema::TYPE_FLOAT => 'float',
35
        Schema::TYPE_DOUBLE => 'double',
36
        Schema::TYPE_DECIMAL => 'decimal(10,0)',
37
        Schema::TYPE_DATETIME => 'timestamp',
38
        Schema::TYPE_TIMESTAMP => 'timestamp',
39
        Schema::TYPE_TIME => 'time',
40
        Schema::TYPE_DATE => 'date',
41
        Schema::TYPE_BINARY => 'blob',
42
        Schema::TYPE_BOOLEAN => 'smallint',
43
        Schema::TYPE_MONEY => 'decimal(19,4)',
44
    ];
45
46 337
    protected function defaultExpressionBuilders()
47
    {
48 337
        return array_merge(parent::defaultExpressionBuilders(), [
49 337
            'yii\db\conditions\InCondition' => 'edgardmessias\db\ibm\db2\conditions\InConditionBuilder',
50 337
            'yii\db\conditions\LikeCondition' => 'edgardmessias\db\ibm\db2\conditions\LikeConditionBuilder',
51 337
        ]);
52
    }
53
54
    /**
55
     * Builds a SQL statement for truncating a DB table.
56
     * @param string $table the table to be truncated. The name will be properly quoted by the method.
57
     * @return string the SQL statement for truncating a DB table.
58
     */
59 1
    public function truncateTable($table)
60
    {
61 1
        return 'TRUNCATE TABLE ' . $this->db->quoteTableName($table) . ' IMMEDIATE';
62
    }
63
64
    /**
65
     * @inheritdoc
66
     */
67 5
    public function resetSequence($tableName, $value = null)
68
    {
69 5
        $table = $this->db->getTableSchema($tableName);
70
71 5
        if ($table !== null && isset($table->columns[$table->sequenceName])) {
72 5
            if ($value === null) {
73
                $sql = 'SELECT MAX("'. $table->sequenceName .'") FROM "'. $tableName . '"';
74
                $value = $this->db->createCommand($sql)->queryScalar() + 1;
75
            } else {
76 5
                $value = (int) $value;
77
            }
78 5
            return 'ALTER TABLE "' . $tableName . '" ALTER COLUMN "'.$table->sequenceName.'" RESTART WITH ' . $value;
79
        } elseif ($table === null) {
80
            throw new InvalidParamException("Table not found: $tableName");
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated with message: since 2.0.14. Use [[InvalidArgumentException]] instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
81
        } else {
82
            throw new InvalidParamException("There is no sequence associated with table '$tableName'.");
0 ignored issues
show
Deprecated Code introduced by
The class yii\base\InvalidParamException has been deprecated with message: since 2.0.14. Use [[InvalidArgumentException]] instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
83
        }
84
    }
85
86
    /**
87
     * Builds a SQL statement for enabling or disabling integrity check.
88
     * @param boolean $check whether to turn on or off the integrity check.
89
     * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
90
     * @param string $table the table name. Defaults to empty string, meaning that no table will be changed.
91
     * @return string the SQL statement for checking integrity
92
     * @throws \yii\base\NotSupportedException if this is not supported by the underlying DBMS
93
     * @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
94
     */
95
    public function checkIntegrity($check = true, $schema = '', $table = '')
96
    {
97
        if ($table) {
98
            $tableNames = [$table];
99
        } else {
100
            if (!$schema) {
101
                $schema = $this->db->defaultSchema;
102
            }
103
104
            //Return only tables
105
            $sql = "SELECT t.tabname FROM syscat.tables AS t"
106
                    . " WHERE t.type in ('T') AND t.ownertype != 'S'";
107
108
            /**
109
             * Filter by integrity pending
110
             * @see http://www-01.ibm.com/support/knowledgecenter/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0001063.html
111
             */
112
            if ($check) {
113
                $sql .= " AND t.status = 'C'";
114
            }
115
            if ($schema) {
116
                $sql .= ' AND t.tabschema = :schema';
117
            }
118
            
119
            $command = $this->db->createCommand($sql);
120
            if ($schema) {
121
                $command->bindValue(':schema', $schema);
122
            }
123
124
            $tableNames = $command->queryColumn();
125
        }
126
127
        if (empty($tableNames)) {
128
            return '';
129
        }
130
131
        $quotedTableNames = [];
132
        foreach ($tableNames as $tableName) {
133
            $quotedTableNames[] = $this->db->quoteTableName($tableName) . ($check? '' : ' ALL');
134
        }
135
136
        $enable = $check ? 'CHECKED' : 'UNCHECKED';
137
        return 'SET INTEGRITY FOR ' . implode(', ', $quotedTableNames) . ' IMMEDIATE ' . $enable. ';';
138
    }
139
140
    /**
141
     * @inheritdoc
142
     */
143 263
    public function buildOrderByAndLimit($sql, $orderBy, $limit, $offset)
144
    {
145 263
        $limitOffsetStatment = $this->buildLimit($limit, $offset);
146 263
        if ($limitOffsetStatment != '') {
147 25
            $sql = str_replace(':query', $sql, $limitOffsetStatment);
148
149
            //convert "item"."id" to "id" to use in OVER()
150 25
            $newOrderBy = [];
151
152 25
            if(!empty($orderBy)){
153 9
                foreach ($orderBy as $name => $direction) {
154 9
                    if(is_string($name)){
155 9
                        $e = explode('.', $name);
156 9
                        $name = array_pop($e);
157 9
                    }
158 9
                    $newOrderBy[$name] = $direction;
159 9
                }
160 9
            }
161
162 25
            $orderByStatment = $this->buildOrderBy($newOrderBy);
163
164 25
            $sql = str_replace(':order', $orderByStatment,$sql);
165 25
        }else{
166 256
            $orderByStatment = $this->buildOrderBy($orderBy);
167 256
            if ($orderByStatment !== '') {
168 36
                $sql .= $this->separator . $orderByStatment;
169 36
            }
170
        }
171 263
        return $sql;
172
    }
173
174
    /**
175
     * @inheritdoc
176
     */
177 263
    public function buildLimit($limit, $offset)
178
    {
179 263
        if (!$this->hasLimit($limit) && !$this->hasOffset($offset)) {
180 256
            return '';
181
        }
182
183 25
        if (!$this->hasOffset($offset)) {
184 23
            return ':query :order FETCH FIRST ' . $limit . ' ROWS ONLY';
185
        }
186
187
        /**
188
         * @todo Need remote the `RN_` from result to use in "INSERT" query
189
         */
190 3
        $limitOffsetStatment = 'SELECT * FROM (SELECT SUBQUERY_.*, ROW_NUMBER() OVER(:order) AS RN_ FROM ( :query ) AS SUBQUERY_) as t WHERE :offset :limit';
191
192 3
        $replacement = $this->hasOffset($offset) ? 't.RN_ > ' . $offset : 't.RN_ > 0';
193 3
        $limitOffsetStatment = str_replace(':offset', $replacement, $limitOffsetStatment);
194
195 3
        $replacement = '';
196
        
197 3
        if ($this->hasLimit($limit)) {
198 2
            if ($limit instanceof \yii\db\ExpressionInterface || $offset instanceof \yii\db\ExpressionInterface) {
199 1
                $replacement = 'AND t.RN_ <= (' . $limit  . ' + ' . $offset . ')';
200 1
            } else {
201 1
                $replacement = 'AND t.RN_ <= ' . ($limit + $offset);
202
            }
203 2
        }
204 3
        $limitOffsetStatment = str_replace(':limit', $replacement, $limitOffsetStatment);
205
206 3
        return $limitOffsetStatment;
207
    }
208
209
    /**
210
     * @inheritdoc
211
     */
212 1
    public function alterColumn($table, $column, $type)
213
    {
214 1
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
215 1
        . $this->db->quoteColumnName($column) . ' SET DATA TYPE '
216 1
        . $this->getColumnType($type);
217
    }
218
219
    /**
220
     * @inheritdoc
221
     */
222 62
    public function prepareInsertValues($table, $columns, $params = [])
223
    {
224 62
        $result = parent::prepareInsertValues($table, $columns, $params);
225
226
        // Empty placeholders, replace for (DEFAULT, DEFAULT, ...)
227 59
        if (empty($result[1]) && $result[2] === ' DEFAULT VALUES') {
228 1
            $schema = $this->db->getSchema();
229 1
            if (($tableSchema = $schema->getTableSchema($table)) !== null) {
230 1
                $columnSchemas = $tableSchema->columns;
231 1
            } else {
232
                $columnSchemas = [];
233
            }
234 1
            $result[1] = array_fill(0, count($columnSchemas), 'DEFAULT');
235 1
        }
236
237 59
        return $result;
238
    }
239
    
240
    /**
241
     * @inheritdoc
242
     */
243 17
    public function upsert($table, $insertColumns, $updateColumns, &$params)
244
    {
245
        /** @var Constraint[] $constraints */
246 17
        list($uniqueNames, $insertNames, $updateNames) = $this->prepareUpsertColumns($table, $insertColumns, $updateColumns, $constraints);
247 17
        if (empty($uniqueNames)) {
248 3
            return $this->insert($table, $insertColumns, $params);
249
        }
250
251 14
        $onCondition = ['or'];
252 14
        $quotedTableName = $this->db->quoteTableName($table);
253 14
        foreach ($constraints as $constraint) {
254 14
            $constraintCondition = ['and'];
255 14
            foreach ($constraint->columnNames as $name) {
0 ignored issues
show
The expression $constraint->columnNames of type array<integer,string>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
256 14
                $quotedName = $this->db->quoteColumnName($name);
257 14
                $constraintCondition[] = "$quotedTableName.$quotedName=\"EXCLUDED\".$quotedName";
258 14
            }
259 14
            $onCondition[] = $constraintCondition;
260 14
        }
261 14
        $on = $this->buildCondition($onCondition, $params);
262 14
        list(, $placeholders, $values, $params) = $this->prepareInsertValues($table, $insertColumns, $params);
263 14
        if (!empty($placeholders)) {
264 6
            $usingSelectValues = [];
265 6
            foreach ($insertNames as $index => $name) {
266 6
                $usingSelectValues[$name] = new Expression($placeholders[$index]);
267 6
            }
268 6
            $usingSubQuery = (new Query())
269 6
                ->select($usingSelectValues)
270 6
                ->from('SYSIBM.SYSDUMMY1');
271 6
            list($usingValues, $params) = $this->build($usingSubQuery, $params);
272 6
        }
273 14
        $mergeSql = 'MERGE INTO ' . $this->db->quoteTableName($table) . ' '
274 14
            . 'USING (' . (isset($usingValues) ? $usingValues : ltrim($values, ' ')) . ') "EXCLUDED" '
275 14
            . "ON ($on)";
276 14
        $insertValues = [];
277 14
        foreach ($insertNames as $name) {
278 14
            $quotedName = $this->db->quoteColumnName($name);
279 14
            if (strrpos($quotedName, '.') === false) {
280 14
                $quotedName = '"EXCLUDED".' . $quotedName;
281 14
            }
282 14
            $insertValues[] = $quotedName;
283 14
        }
284 14
        $insertSql = 'INSERT (' . implode(', ', $insertNames) . ')'
285 14
            . ' VALUES (' . implode(', ', $insertValues) . ')';
286 14
        if ($updateColumns === false) {
287 4
            return "$mergeSql WHEN NOT MATCHED THEN $insertSql";
288
        }
289
290 10
        if ($updateColumns === true) {
291 4
            $updateColumns = [];
292 4
            foreach ($updateNames as $name) {
293 4
                $quotedName = $this->db->quoteColumnName($name);
294 4
                if (strrpos($quotedName, '.') === false) {
295 4
                    $quotedName = '"EXCLUDED".' . $quotedName;
296 4
                }
297 4
                $updateColumns[$name] = new Expression($quotedName);
298 4
            }
299 4
        }
300 10
        list($updates, $params) = $this->prepareUpdateSets($table, $updateColumns, $params);
301 10
        $updateSql = 'UPDATE SET ' . implode(', ', $updates);
302 10
        return "$mergeSql WHEN MATCHED THEN $updateSql WHEN NOT MATCHED THEN $insertSql";
303
    }
304
305
    /**
306
     * Creates a SELECT EXISTS() SQL statement.
307
     * @param string $rawSql the subquery in a raw form to select from.
308
     * @return string the SELECT EXISTS() SQL statement.
309
     *
310
     * @since 2.0.8
311
     */
312 21
    public function selectExists($rawSql)
313
    {
314 21
        return 'SELECT CASE WHEN COUNT(*)>0 THEN 1 ELSE 0 END FROM (' . $rawSql . ') CHECKEXISTS';;
315
    }
316
317
    /**
318
     * Builds a SQL command for adding comment to column
319
     *
320
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
321
     * @param string $column the name of the column to be commented. The column name will be properly quoted by the method.
322
     * @return string the SQL statement for adding comment on column
323
     * @since 2.0.8
324
     */
325 2
    public function dropCommentFromColumn($table, $column)
326
    {
327 2
        return 'COMMENT ON COLUMN ' . $this->db->quoteTableName($table) . '.' . $this->db->quoteColumnName($column) . " IS ''";
328
    }
329
330
    /**
331
     * Builds a SQL command for adding comment to table
332
     *
333
     * @param string $table the table whose column is to be commented. The table name will be properly quoted by the method.
334
     * @return string the SQL statement for adding comment on column
335
     * @since 2.0.8
336
     */
337 1
    public function dropCommentFromTable($table)
338
    {
339 1
        return 'COMMENT ON TABLE ' . $this->db->quoteTableName($table) . " IS ''";
340
    }
341
342
    /**
343
     * @inheritdoc
344
     */
345 2
    public function dropIndex($name, $table)
346
    {
347 2
        return 'DROP INDEX ' . $this->db->quoteTableName($name);
348
    }
349
350
    /**
351
     * {@inheritdoc}
352
     */
353 2
    public function addDefaultValue($name, $table, $column, $value)
354
    {
355 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) 
356 2
            . ' ALTER COLUMN ' . $this->db->quoteColumnName($column)
357 2
            . ' SET DEFAULT ' . $this->db->quoteValue($value);
358
    }
359
360
    /**
361
     * {@inheritdoc}
362
     */
363 2
    public function dropDefaultValue($name, $table)
364
    {
365 2
        return 'ALTER TABLE ' . $this->db->quoteTableName($table) 
366 2
            . ' ALTER COLUMN ' . $this->db->quoteColumnName($name)
367 2
            . ' DROP DEFAULT';
368
    }
369
}
370