Database_Pgsql_Driver::compile_select()   F
last analyzed

Complexity

Conditions 11
Paths 512

Size

Total Lines 44
Code Lines 25

Duplication

Lines 44
Ratio 100 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 11
eloc 25
c 2
b 1
f 0
nc 512
nop 1
dl 44
loc 44
rs 3.1764

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 defined('SYSPATH') or die('No direct access allowed.');
2
/**
3
 * PostgreSQL 8.1+ Database Driver
4
 *
5
 * $Id: Pgsql.php 4344 2009-05-11 16:41:39Z zombor $
6
 *
7
 * @package    Core
8
 * @author     Kohana Team
9
 * @copyright  (c) 2007-2008 Kohana Team
10
 * @license    http://kohanaphp.com/license.html
11
 */
12
class Database_Pgsql_Driver extends Database_Driver
13
{
14
15
    // Database connection link
16
    protected $link;
17
    protected $db_config;
18
19
    /**
20
     * Sets the config for the class.
21
     *
22
     * @param  array  database configuration
23
     */
24
    public function __construct($config)
25
    {
26
        $this->db_config = $config;
27
28
        Kohana::log('debug', 'PgSQL Database Driver Initialized');
29
    }
30
31
    public function connect()
32
    {
33
        // Check if link already exists
34
        if (is_resource($this->link)) {
35
            return $this->link;
36
        }
37
38
        // Import the connect variables
39
        extract($this->db_config['connection']);
40
41
        // Persistent connections enabled?
42
        $connect = ($this->db_config['persistent'] == true) ? 'pg_pconnect' : 'pg_connect';
43
44
        // Build the connection info
45
        $port = isset($port) ? 'port=\''.$port.'\'' : '';
46
        $host = isset($host) ? 'host=\''.$host.'\' '.$port : ''; // if no host, connect with the socket
47
48
        $connection_string = $host.' dbname=\''.$database.'\' user=\''.$user.'\' password=\''.$pass.'\'';
49
        // Make the connection and select the database
50
        if ($this->link = $connect($connection_string)) {
51
            if ($charset = $this->db_config['character_set']) {
52
                echo $this->set_charset($charset);
53
            }
54
55
            // Clear password after successful connect
56
            $this->db_config['connection']['pass'] = null;
57
58
            return $this->link;
59
        }
60
61
        return false;
62
    }
63
64
    /**
65
     * @param string $sql
66
     */
67 View Code Duplication
    public function query($sql)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
68
    {
69
        // Only cache if it's turned on, and only cache if it's not a write statement
70
        if ($this->db_config['cache'] and ! preg_match('#\b(?:INSERT|UPDATE|SET)\b#i', $sql)) {
71
            $hash = $this->query_hash($sql);
72
73
            if (! isset($this->query_cache[$hash])) {
74
                // Set the cached object
75
                $this->query_cache[$hash] = new Pgsql_Result(pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql);
76
            } else {
77
                // Rewind cached result
78
                $this->query_cache[$hash]->rewind();
79
            }
80
81
            return $this->query_cache[$hash];
82
        }
83
84
        // Suppress warning triggered when a database error occurs (e.g., a constraint violation)
85
        return new Pgsql_Result(@pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql);
86
    }
87
88
    public function set_charset($charset)
89
    {
90
        $this->query('SET client_encoding TO '.pg_escape_string($this->link, $charset));
91
    }
92
93 View Code Duplication
    public function escape_table($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
94
    {
95
        if (!$this->db_config['escape']) {
96
            return $table;
97
        }
98
99
        return '"'.str_replace('.', '"."', $table).'"';
100
    }
101
102 View Code Duplication
    public function escape_column($column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
103
    {
104
        if (!$this->db_config['escape']) {
105
            return $column;
106
        }
107
108
        if ($column == '*') {
109
            return $column;
110
        }
111
112
        // This matches any functions we support to SELECT.
113
        if (preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches)) {
114
            if (count($matches) == 3) {
115
                return $matches[1].'('.$this->escape_column($matches[2]).')';
116
            } elseif (count($matches) == 5) {
117
                return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]);
118
            }
119
        }
120
121
        // This matches any modifiers we support to SELECT.
122
        if (! preg_match('/\b(?:all|distinct)\s/i', $column)) {
123
            if (stripos($column, ' AS ') !== false) {
124
                // Force 'AS' to uppercase
125
                $column = str_ireplace(' AS ', ' AS ', $column);
126
127
                // Runs escape_column on both sides of an AS statement
128
                $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
129
130
                // Re-create the AS statement
131
                return implode(' AS ', $column);
132
            }
133
134
            return preg_replace('/[^.*]+/', '"$0"', $column);
135
        }
136
137
        $parts = explode(' ', $column);
138
        $column = '';
139
140
        for ($i = 0, $c = count($parts); $i < $c; $i++) {
141
            // The column is always last
142
            if ($i == ($c - 1)) {
143
                $column .= preg_replace('/[^.*]+/', '"$0"', $parts[$i]);
144
            } else { // otherwise, it's a modifier
145
                $column .= $parts[$i].' ';
146
            }
147
        }
148
        return $column;
149
    }
150
151
    public function regex($field, $match, $type, $num_regexs)
152
    {
153
        $prefix = ($num_regexs == 0) ? '' : $type;
154
155
        return $prefix.' '.$this->escape_column($field).' ~* \''.$this->escape_str($match).'\'';
156
    }
157
158
    public function notregex($field, $match, $type, $num_regexs)
159
    {
160
        $prefix = $num_regexs == 0 ? '' : $type;
161
162
        return $prefix.' '.$this->escape_column($field).' !~* \''.$this->escape_str($match) . '\'';
163
    }
164
165
    public function limit($limit, $offset = 0)
166
    {
167
        return 'LIMIT '.$limit.' OFFSET '.$offset;
168
    }
169
170 View Code Duplication
    public function compile_select($database)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
171
    {
172
        $sql = ($database['distinct'] == true) ? 'SELECT DISTINCT ' : 'SELECT ';
173
        $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
174
175
        if (count($database['from']) > 0) {
176
            $sql .= "\nFROM ";
177
            $sql .= implode(', ', $database['from']);
178
        }
179
180
        if (count($database['join']) > 0) {
181
            foreach ($database['join'] as $join) {
182
                $sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions'];
183
            }
184
        }
185
186
        if (count($database['where']) > 0) {
187
            $sql .= "\nWHERE ";
188
        }
189
190
        $sql .= implode("\n", $database['where']);
191
192
        if (count($database['groupby']) > 0) {
193
            $sql .= "\nGROUP BY ";
194
            $sql .= implode(', ', $database['groupby']);
195
        }
196
197
        if (count($database['having']) > 0) {
198
            $sql .= "\nHAVING ";
199
            $sql .= implode("\n", $database['having']);
200
        }
201
202
        if (count($database['orderby']) > 0) {
203
            $sql .= "\nORDER BY ";
204
            $sql .= implode(', ', $database['orderby']);
205
        }
206
207
        if (is_numeric($database['limit'])) {
208
            $sql .= "\n";
209
            $sql .= $this->limit($database['limit'], $database['offset']);
210
        }
211
212
        return $sql;
213
    }
214
215 View Code Duplication
    public function escape_str($str)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
216
    {
217
        if (!$this->db_config['escape']) {
218
            return $str;
219
        }
220
221
        is_resource($this->link) or $this->connect();
222
223
        return pg_escape_string($this->link, $str);
224
    }
225
226
    public function list_tables()
227
    {
228
        $sql    = 'SELECT table_schema || \'.\' || table_name FROM information_schema.tables WHERE table_schema NOT IN (\'pg_catalog\', \'information_schema\')';
229
        $result = $this->query($sql)->result(false, PGSQL_ASSOC);
0 ignored issues
show
Documentation introduced by
PGSQL_ASSOC is of type integer, but the function expects a boolean.

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...
230
231
        $retval = array();
232
        foreach ($result as $row) {
233
            $retval[] = current($row);
234
        }
235
236
        return $retval;
237
    }
238
239
    public function show_error()
240
    {
241
        return pg_last_error($this->link);
242
    }
243
244
    public function list_fields($table)
245
    {
246
        $result = null;
247
248
        foreach ($this->field_data($table) as $row) {
249
            // Make an associative array
250
            $result[$row->column_name] = $this->sql_type($row->data_type);
251
252
            if (!strncmp($row->column_default, 'nextval(', 8)) {
253
                $result[$row->column_name]['sequenced'] = true;
254
            }
255
256
            if ($row->is_nullable === 'YES') {
257
                $result[$row->column_name]['null'] = true;
258
            }
259
        }
260
261
        if (!isset($result)) {
262
            throw new Kohana_Database_Exception('database.table_not_found', $table);
263
        }
264
265
        return $result;
266
    }
267
268
    public function field_data($table)
269
    {
270
        // http://www.postgresql.org/docs/8.3/static/infoschema-columns.html
271
        $result = $this->query('
272
			SELECT column_name, column_default, is_nullable, data_type, udt_name,
273
				character_maximum_length, numeric_precision, numeric_precision_radix, numeric_scale
274
			FROM information_schema.columns
275
			WHERE table_name = \''. $this->escape_str($table) .'\'
276
			ORDER BY ordinal_position
277
		');
278
279
        return $result->result_array(true);
280
    }
281
} // End Database_Pgsql_Driver Class
282
283
/**
284
 * PostgreSQL Result
285
 */
286
class Pgsql_Result extends Database_Result
287
{
288
289
    // Data fetching types
290
    protected $fetch_type  = 'pgsql_fetch_object';
291
    protected $return_type = PGSQL_ASSOC;
292
293
    /**
294
     * Sets up the result variables.
295
     *
296
     * @param  resource  query result
297
     * @param  resource  database link
298
     * @param  boolean   return objects or arrays
299
     * @param  string    SQL query that was run
300
     */
301
    public function __construct($result, $link, $object = true, $sql)
302
    {
303
        $this->link = $link;
0 ignored issues
show
Bug introduced by
The property link does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
304
        $this->result = $result;
305
306
        // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query
307
        if (is_resource($result)) {
308
            // Its an DELETE, INSERT, REPLACE, or UPDATE query
309
            if (preg_match('/^(?:delete|insert|replace|update)\b/iD', trim($sql), $matches)) {
310
                $this->insert_id  = (strtolower($matches[0]) == 'insert') ? $this->insert_id() : false;
311
                $this->total_rows = pg_affected_rows($this->result);
312
            } else {
313
                $this->current_row = 0;
314
                $this->total_rows  = pg_num_rows($this->result);
315
                $this->fetch_type = ($object === true) ? 'pg_fetch_object' : 'pg_fetch_array';
316
            }
317
        } else {
318
            throw new Kohana_Database_Exception('database.error', pg_last_error().' - '.$sql);
319
        }
320
321
        // Set result type
322
        $this->result($object);
323
324
        // Store the SQL
325
        $this->sql = $sql;
326
    }
327
328
    /**
329
     * Magic __destruct function, frees the result.
330
     */
331
    public function __destruct()
332
    {
333
        if (is_resource($this->result)) {
334
            pg_free_result($this->result);
335
        }
336
    }
337
338 View Code Duplication
    public function result($object = true, $type = PGSQL_ASSOC)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
339
    {
340
        $this->fetch_type = ((bool) $object) ? 'pg_fetch_object' : 'pg_fetch_array';
341
342
        // This check has to be outside the previous statement, because we do not
343
        // know the state of fetch_type when $object = NULL
344
        // NOTE - The class set by $type must be defined before fetching the result,
345
        // autoloading is disabled to save a lot of stupid overhead.
346
        if ($this->fetch_type == 'pg_fetch_object') {
347
            $this->return_type = (is_string($type) and Kohana::auto_load($type)) ? $type : 'stdClass';
348
        } else {
349
            $this->return_type = $type;
350
        }
351
352
        return $this;
353
    }
354
355
    public function as_array($object = null, $type = PGSQL_ASSOC)
356
    {
357
        return $this->result_array($object, $type);
358
    }
359
360 View Code Duplication
    public function result_array($object = null, $type = PGSQL_ASSOC)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
361
    {
362
        $rows = array();
363
364
        if (is_string($object)) {
365
            $fetch = $object;
366
        } elseif (is_bool($object)) {
367
            if ($object === true) {
368
                $fetch = 'pg_fetch_object';
369
370
                // NOTE - The class set by $type must be defined before fetching the result,
371
                // autoloading is disabled to save a lot of stupid overhead.
372
                $type = (is_string($type) and Kohana::auto_load($type)) ? $type : 'stdClass';
373
            } else {
374
                $fetch = 'pg_fetch_array';
375
            }
376
        } else {
377
            // Use the default config values
378
            $fetch = $this->fetch_type;
379
380
            if ($fetch == 'pg_fetch_object') {
381
                $type = (is_string($type) and Kohana::auto_load($type)) ? $type : 'stdClass';
382
            }
383
        }
384
385
        if ($this->total_rows) {
386
            pg_result_seek($this->result, 0);
387
388
            while ($row = $fetch($this->result, null, $type)) {
389
                $rows[] = $row;
390
            }
391
        }
392
393
        return $rows;
394
    }
395
396
    public function insert_id()
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
397
    {
398
        if ($this->insert_id === null) {
399
            $query = 'SELECT LASTVAL() AS insert_id';
400
401
            // Disable error reporting for this, just to silence errors on
402
            // tables that have no serial column.
403
            $ER = error_reporting(0);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ER. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
404
405
            $result = pg_query($this->link, $query);
406
            $insert_id = pg_fetch_array($result, null, PGSQL_ASSOC);
407
408
            $this->insert_id = $insert_id['insert_id'];
409
410
            // Reset error reporting
411
            error_reporting($ER);
412
        }
413
414
        return $this->insert_id;
415
    }
416
417 View Code Duplication
    public function seek($offset)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
418
    {
419
        if ($this->offsetExists($offset) and pg_result_seek($this->result, $offset)) {
420
            // Set the current row to the offset
421
            $this->current_row = $offset;
422
423
            return true;
424
        }
425
426
        return false;
427
    }
428
429
    public function list_fields()
430
    {
431
        $field_names = array();
432
433
        $fields = pg_num_fields($this->result);
434
        for ($i = 0; $i < $fields; ++$i) {
435
            $field_names[] = pg_field_name($this->result, $i);
436
        }
437
438
        return $field_names;
439
    }
440
441
    /**
442
     * ArrayAccess: offsetGet
443
     */
444 View Code Duplication
    public function offsetGet($offset)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
445
    {
446
        if (! $this->seek($offset)) {
447
            return false;
448
        }
449
450
        // Return the row by calling the defined fetching callback
451
        $fetch = $this->fetch_type;
452
        return $fetch($this->result, null, $this->return_type);
453
    }
454
} // End Pgsql_Result Class
455
456
/**
457
 * PostgreSQL Prepared Statement (experimental)
458
 */
459
class Kohana_Pgsql_Statement
460
{
461
    protected $link = null;
462
    protected $stmt;
463
464
    public function __construct($sql, $link)
465
    {
466
        $this->link = $link;
467
468
        $this->stmt = $this->link->prepare($sql);
469
470
        return $this;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
471
    }
472
473
    public function __destruct()
474
    {
475
        $this->stmt->close();
476
    }
477
478
    // Sets the bind parameters
479
    public function bind_params()
480
    {
481
        $argv = func_get_args();
0 ignored issues
show
Unused Code introduced by
$argv is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
482
        return $this;
483
    }
484
485
    // sets the statement values to the bound parameters
486
    public function set_vals()
487
    {
488
        return $this;
489
    }
490
491
    // Runs the statement
492
    public function execute()
493
    {
494
        return $this;
495
    }
496
}
497