~jlosito/wordpress/wp-plugin-yoast

« back to all changes in this revision

Viewing changes to vendor_prefixed/ruckusing/lib/Ruckusing/Adapter/PgSQL/Base.php

  • Committer: John Losito
  • Date: 2019-11-08 15:58:32 UTC
  • Revision ID: john.losito@canonical.com-20191108155832-bjb8eep3l9naaf8f
Updated to 12.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
namespace YoastSEO_Vendor;
 
4
 
 
5
/**
 
6
 * Ruckusing
 
7
 *
 
8
 * @category  Ruckusing
 
9
 * @package   Ruckusing_Adapter
 
10
 * @subpackage PgSQL
 
11
 * @author    Cody Caughlan <codycaughlan % gmail . com>
 
12
 * @link      https://github.com/ruckus/ruckusing-migrations
 
13
 */
 
14
// max length of an identifier like a column or index name
 
15
\define('YoastSEO_Vendor\\PG_MAX_IDENTIFIER_LENGTH', 64);
 
16
/**
 
17
 * Implementation of Ruckusing_Adapter_PgSQL_Base
 
18
 *
 
19
 * @category Ruckusing
 
20
 * @package  Ruckusing_Adapter
 
21
 * @subpackage PgSQL
 
22
 * @author   Cody Caughlan <codycaughlan % gmail . com>
 
23
 * @link      https://github.com/ruckus/ruckusing-migrations
 
24
 */
 
25
class Ruckusing_Adapter_PgSQL_Base extends \YoastSEO_Vendor\Ruckusing_Adapter_Base implements \YoastSEO_Vendor\Ruckusing_Adapter_Interface
 
26
{
 
27
    /**
 
28
     * Name of adapter
 
29
     *
 
30
     * @var string
 
31
     */
 
32
    private $_name = "Postgres";
 
33
    /**
 
34
     * tables
 
35
     *
 
36
     * @var array
 
37
     */
 
38
    private $_tables = array();
 
39
    /**
 
40
     * tables_loaded
 
41
     *
 
42
     * @var boolean
 
43
     */
 
44
    private $_tables_loaded = \false;
 
45
    /**
 
46
     * version
 
47
     *
 
48
     * @var string
 
49
     */
 
50
    private $_version = '1.0';
 
51
    /**
 
52
     * Indicate if is in transaction
 
53
     *
 
54
     * @var boolean
 
55
     */
 
56
    private $_in_trx = \false;
 
57
    /**
 
58
     * Creates an instance of Ruckusing_Adapter_PgSQL_Base
 
59
     *
 
60
     * @param array                 $dsn    The current dsn being used
 
61
     * @param Ruckusing_Util_Logger $logger the current logger
 
62
     *
 
63
     * @return Ruckusing_Adapter_PgSQL_Base
 
64
     */
 
65
    public function __construct($dsn, $logger)
 
66
    {
 
67
        parent::__construct($dsn);
 
68
        $this->connect($dsn);
 
69
        $this->set_logger($logger);
 
70
    }
 
71
    /**
 
72
     * Get the current db name
 
73
     *
 
74
     * @return string
 
75
     */
 
76
    public function get_database_name()
 
77
    {
 
78
        return $this->db_info['database'];
 
79
    }
 
80
    /**
 
81
     * Check support for migrations
 
82
     *
 
83
     * @return boolean
 
84
     */
 
85
    public function supports_migrations()
 
86
    {
 
87
        return \true;
 
88
    }
 
89
    /**
 
90
     * Get the column native types
 
91
     *
 
92
     * @return array
 
93
     */
 
94
    public function native_database_types()
 
95
    {
 
96
        $types = array('primary_key' => array('name' => 'serial'), 'string' => array('name' => 'varchar', 'limit' => 255), 'text' => array('name' => 'text'), 'tinytext' => array('name' => 'text'), 'mediumtext' => array('name' => 'text'), 'integer' => array('name' => 'integer'), 'tinyinteger' => array('name' => 'smallint'), 'smallinteger' => array('name' => 'smallint'), 'mediuminteger' => array('name' => 'integer'), 'biginteger' => array('name' => 'bigint'), 'float' => array('name' => 'float'), 'decimal' => array('name' => 'decimal', 'scale' => 0, 'precision' => 10), 'datetime' => array('name' => 'timestamp'), 'timestamp' => array('name' => 'timestamp'), 'time' => array('name' => 'time'), 'date' => array('name' => 'date'), 'binary' => array('name' => 'bytea'), 'tinybinary' => array('name' => "bytea"), 'mediumbinary' => array('name' => "bytea"), 'longbinary' => array('name' => "bytea"), 'boolean' => array('name' => 'boolean'), 'tsvector' => array('name' => 'tsvector'), 'uuid' => array('name' => 'uuid'));
 
97
        return $types;
 
98
    }
 
99
    //-----------------------------------
 
100
    // PUBLIC METHODS
 
101
    //-----------------------------------
 
102
    /**
 
103
     * Create the schema table, if necessary
 
104
     */
 
105
    public function create_schema_version_table()
 
106
    {
 
107
        if (!$this->has_table($this->get_schema_version_table_name())) {
 
108
            $t = $this->create_table($this->get_schema_version_table_name(), array('id' => \false));
 
109
            $t->column('version', 'string');
 
110
            $t->finish();
 
111
            $this->add_index($this->get_schema_version_table_name(), 'version', array('unique' => \true));
 
112
        }
 
113
    }
 
114
    /**
 
115
     * Start Transaction
 
116
     */
 
117
    public function start_transaction()
 
118
    {
 
119
        if ($this->inTransaction() === \false) {
 
120
            $this->beginTransaction();
 
121
        }
 
122
    }
 
123
    /**
 
124
     * Commit Transaction
 
125
     */
 
126
    public function commit_transaction()
 
127
    {
 
128
        if ($this->inTransaction()) {
 
129
            $this->commit();
 
130
        }
 
131
    }
 
132
    /**
 
133
     * Rollback Transaction
 
134
     */
 
135
    public function rollback_transaction()
 
136
    {
 
137
        if ($this->inTransaction()) {
 
138
            $this->rollback();
 
139
        }
 
140
    }
 
141
    /**
 
142
     * Column definition
 
143
     *
 
144
     * @param string $column_name the column name
 
145
     * @param string $type        the type of the column
 
146
     * @param array  $options     column options
 
147
     *
 
148
     * @return string
 
149
     */
 
150
    public function column_definition($column_name, $type, $options = null)
 
151
    {
 
152
        $col = new \YoastSEO_Vendor\Ruckusing_Adapter_ColumnDefinition($this, $column_name, $type, $options);
 
153
        return $col->__toString();
 
154
    }
 
155
    /**
 
156
     * Returns a table's primary key and belonging sequence.
 
157
     *
 
158
     * @param string $table the table name
 
159
     *
 
160
     * @return array
 
161
     */
 
162
    public function pk_and_sequence_for($table)
 
163
    {
 
164
        $sql = <<<SQL
 
165
      SELECT attr.attname, seq.relname
 
166
      FROM pg_class      seq,
 
167
           pg_attribute  attr,
 
168
           pg_depend     dep,
 
169
           pg_namespace  name,
 
170
           pg_constraint cons
 
171
      WHERE seq.oid           = dep.objid
 
172
        AND seq.relkind       = 'S'
 
173
        AND attr.attrelid     = dep.refobjid
 
174
        AND attr.attnum       = dep.refobjsubid
 
175
        AND attr.attrelid     = cons.conrelid
 
176
        AND attr.attnum       = cons.conkey[1]
 
177
        AND cons.contype      = 'p'
 
178
        AND dep.refobjid      = '%s'::regclass
 
179
SQL;
 
180
        $sql = \sprintf($sql, $table);
 
181
        $result = $this->select_one($sql);
 
182
        if ($result) {
 
183
            return array($result['attname'], $result['relname']);
 
184
        } else {
 
185
            return array();
 
186
        }
 
187
    }
 
188
    //-------- DATABASE LEVEL OPERATIONS
 
189
    /**
 
190
     * Create database cannot run in a transaction block so if we're in a transaction
 
191
     * than commit it, do our thing and then re-invoke the transaction
 
192
     *
 
193
     * @param string $db the db name
 
194
     *
 
195
     * @param array $options
 
196
     * @return boolean
 
197
     */
 
198
    public function create_database($db, $options = array())
 
199
    {
 
200
        $was_in_transaction = \false;
 
201
        if ($this->inTransaction()) {
 
202
            $this->commit_transaction();
 
203
            $was_in_transaction = \true;
 
204
        }
 
205
        if (!\array_key_exists('encoding', $options)) {
 
206
            $options['encoding'] = 'utf8';
 
207
        }
 
208
        $ddl = \sprintf("CREATE DATABASE %s", $this->identifier($db));
 
209
        if (\array_key_exists('owner', $options)) {
 
210
            $ddl .= " OWNER = \"{$options['owner']}\"";
 
211
        }
 
212
        if (\array_key_exists('template', $options)) {
 
213
            $ddl .= " TEMPLATE = \"{$options['template']}\"";
 
214
        }
 
215
        if (\array_key_exists('encoding', $options)) {
 
216
            $ddl .= " ENCODING = '{$options['encoding']}'";
 
217
        }
 
218
        if (\array_key_exists('tablespace', $options)) {
 
219
            $ddl .= " TABLESPACE = \"{$options['tablespace']}\"";
 
220
        }
 
221
        if (\array_key_exists('connection_limit', $options)) {
 
222
            $connlimit = \intval($options['connection_limit']);
 
223
            $ddl .= " CONNECTION LIMIT = {$connlimit}";
 
224
        }
 
225
        $result = $this->query($ddl);
 
226
        if ($was_in_transaction) {
 
227
            $this->start_transaction();
 
228
            $was_in_transaction = \false;
 
229
        }
 
230
        return $result === \true;
 
231
    }
 
232
    /**
 
233
     * Check if a db exists
 
234
     *
 
235
     * @param string $db the db name
 
236
     *
 
237
     * @return boolean
 
238
     */
 
239
    public function database_exists($db)
 
240
    {
 
241
        $sql = \sprintf("SELECT datname FROM pg_database WHERE datname = '%s'", $db);
 
242
        $result = $this->select_one($sql);
 
243
        return \count($result) == 1 && $result['datname'] == $db;
 
244
    }
 
245
    /**
 
246
     * Drop a database
 
247
     *
 
248
     * @param string $db the db name
 
249
     *
 
250
     * @return boolean
 
251
     */
 
252
    public function drop_database($db)
 
253
    {
 
254
        if (!$this->database_exists($db)) {
 
255
            return \false;
 
256
        }
 
257
        $ddl = \sprintf("DROP DATABASE IF EXISTS %s", $this->quote_table_name($db));
 
258
        $result = $this->query($ddl);
 
259
        return $result === \true;
 
260
    }
 
261
    /**
 
262
     * Dump the complete schema of the DB. This is really just all of the
 
263
     * CREATE TABLE statements for all of the tables in the DB.
 
264
     * NOTE: this does NOT include any INSERT statements or the actual data
 
265
     *
 
266
     * @param string $output_file the filepath to output to
 
267
     *
 
268
     * @return int|FALSE
 
269
     */
 
270
    public function schema($output_file)
 
271
    {
 
272
        $command = \sprintf("pg_dump -U %s -Fp -s -f '%s' %s --host %s", $this->db_info['user'], $output_file, $this->db_info['database'], $this->db_info['host']);
 
273
        return \system($command);
 
274
    }
 
275
    /**
 
276
     * Check if a table exists
 
277
     *
 
278
     * @param string  $tbl           the table name
 
279
     * @param boolean $reload_tables reload table or not
 
280
     *
 
281
     * @return boolean
 
282
     */
 
283
    public function table_exists($tbl, $reload_tables = \false)
 
284
    {
 
285
        $this->load_tables($reload_tables);
 
286
        return \array_key_exists($tbl, $this->_tables);
 
287
    }
 
288
    public function execute($query)
 
289
    {
 
290
        return $this->query($query);
 
291
    }
 
292
    /**
 
293
     * Wrapper to execute a query
 
294
     *
 
295
     * @param string $query query to run
 
296
     *
 
297
     * @throws Ruckusing_Exception
 
298
     * @return boolean
 
299
     */
 
300
    public function query($query)
 
301
    {
 
302
        $this->logger->log($query);
 
303
        $query_type = $this->determine_query_type($query);
 
304
        $data = array();
 
305
        if ($query_type == \YoastSEO_Vendor\SQL_SELECT || $query_type == \YoastSEO_Vendor\SQL_SHOW) {
 
306
            $res = \pg_query($this->conn, $query);
 
307
            if ($this->isError($res)) {
 
308
                throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, \pg_last_error($this->conn)), \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
309
            }
 
310
            while ($row = \pg_fetch_assoc($res)) {
 
311
                $data[] = $row;
 
312
            }
 
313
            return $data;
 
314
        } else {
 
315
            // INSERT, DELETE, etc...
 
316
            $res = \pg_query($this->conn, $query);
 
317
            if ($this->isError($res)) {
 
318
                throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, \pg_last_error($this->conn)), \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
319
            }
 
320
            // if the query contained a 'RETURNING' class then grab its value
 
321
            $returning_regex = '/ RETURNING \\"(.+)\\"$/';
 
322
            $matches = array();
 
323
            if (\preg_match($returning_regex, $query, $matches)) {
 
324
                if (\count($matches) == 2) {
 
325
                    $returning_column_value = \pg_fetch_result($res, 0, $matches[1]);
 
326
                    return $returning_column_value;
 
327
                }
 
328
            }
 
329
            return \true;
 
330
        }
 
331
    }
 
332
    /**
 
333
     * Execute several queries
 
334
     *
 
335
     * @param string $queries queries to run
 
336
     *
 
337
     * @throws Ruckusing_Exception
 
338
     * @return boolean
 
339
     */
 
340
    public function multi_query($queries)
 
341
    {
 
342
        $res = \pg_query($this->conn, $queries);
 
343
        if ($this->isError($res)) {
 
344
            throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error executing 'query' with:\n%s\n\nReason: %s\n\n", $queries, \pg_last_error($this->conn)), \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
345
        }
 
346
        return \true;
 
347
    }
 
348
    /**
 
349
     * Select one
 
350
     *
 
351
     * @param string $query query to run
 
352
     *
 
353
     * @throws Ruckusing_Exception
 
354
     * @return array
 
355
     */
 
356
    public function select_one($query)
 
357
    {
 
358
        $this->logger->log($query);
 
359
        $query_type = $this->determine_query_type($query);
 
360
        if ($query_type == \YoastSEO_Vendor\SQL_SELECT || $query_type == \YoastSEO_Vendor\SQL_SHOW) {
 
361
            $res = \pg_query($this->conn, $query);
 
362
            if ($this->isError($res)) {
 
363
                throw new \YoastSEO_Vendor\Ruckusing_Exception(\sprintf("Error executing 'query' with:\n%s\n\nReason: %s\n\n", $query, \pg_last_error($this->conn)), \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
364
            }
 
365
            return \pg_fetch_assoc($res);
 
366
        } else {
 
367
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Query for select_one() is not one of SELECT or SHOW: {$query}", \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
368
        }
 
369
    }
 
370
    /**
 
371
     * Select all
 
372
     *
 
373
     * @param string $query query to run
 
374
     *
 
375
     * @return array
 
376
     */
 
377
    public function select_all($query)
 
378
    {
 
379
        return $this->query($query);
 
380
    }
 
381
    /**
 
382
     * Use this method for non-SELECT queries
 
383
     * Or anything where you dont necessarily expect a result string, e.g. DROPs, CREATEs, etc.
 
384
     *
 
385
     * @param string $ddl query to run
 
386
     *
 
387
     * @return boolean
 
388
     */
 
389
    public function execute_ddl($ddl)
 
390
    {
 
391
        $result = $this->query($ddl);
 
392
        return \true;
 
393
    }
 
394
    /**
 
395
     * Drop table
 
396
     *
 
397
     * @param string $tbl the table name
 
398
     *
 
399
     * @return boolean
 
400
     */
 
401
    public function drop_table($tbl)
 
402
    {
 
403
        $ddl = \sprintf("DROP TABLE IF EXISTS %s", $this->quote_table_name($tbl));
 
404
        $result = $this->query($ddl);
 
405
        return \true;
 
406
    }
 
407
    /**
 
408
     * Create table
 
409
     * @param string $table_name the table name
 
410
     * @param array $options the options
 
411
     * @return bool|Ruckusing_Adapter_PgSQL_TableDefinition
 
412
     */
 
413
    public function create_table($table_name, $options = array())
 
414
    {
 
415
        return new \YoastSEO_Vendor\Ruckusing_Adapter_PgSQL_TableDefinition($this, $table_name, $options);
 
416
    }
 
417
    /**
 
418
     * Escape a string for mysql
 
419
     *
 
420
     * @param string $string the string
 
421
     *
 
422
     * @return string
 
423
     */
 
424
    public function quote_string($string)
 
425
    {
 
426
        return \pg_escape_string($string);
 
427
    }
 
428
    /**
 
429
     * Quote a string
 
430
     *
 
431
     * @param string $string the string
 
432
     *
 
433
     * @return string
 
434
     */
 
435
    public function identifier($string)
 
436
    {
 
437
        return '"' . $string . '"';
 
438
    }
 
439
    /**
 
440
     * Quote table name
 
441
     *
 
442
     * @param string $string the string
 
443
     *
 
444
     * @return string
 
445
     */
 
446
    public function quote_table_name($string)
 
447
    {
 
448
        return '"' . $string . '"';
 
449
    }
 
450
    /**
 
451
     * Quote column name
 
452
     *
 
453
     * @param string $string the string
 
454
     *
 
455
     * @return string
 
456
     */
 
457
    public function quote_column_name($string)
 
458
    {
 
459
        return '"' . $string . '"';
 
460
    }
 
461
    /**
 
462
     * Quote a string
 
463
     *
 
464
     * @param string $value  the string
 
465
     * @param string $column the column
 
466
     *
 
467
     * @return string
 
468
     */
 
469
    public function quote($value, $column = null)
 
470
    {
 
471
        $type = \gettype($value);
 
472
        if ($type == "double") {
 
473
            return "'{$value}'";
 
474
        } elseif ($type == "integer") {
 
475
            return "'{$value}'";
 
476
        } else {
 
477
            // TODO: this global else is probably going to be problematic.
 
478
            // I think eventually we'll need to do more introspection and handle all possible types
 
479
            return "'{$value}'";
 
480
        }
 
481
        /*
 
482
         "boolean"
 
483
        "integer"
 
484
        "double" (for historical reasons "double" is returned in case of a float, and not simply "float")
 
485
        "string"
 
486
        "array"
 
487
        "object"
 
488
        "resource"
 
489
        "NULL"
 
490
        "unknown type"
 
491
        */
 
492
    }
 
493
    /*
 
494
     */
 
495
    /**
 
496
     * Renames a table.
 
497
     * Also renames a table's primary key sequence if the sequence name matches the Ruckusing Migrations default.
 
498
     *
 
499
     * @param string $name the current table name
 
500
     * @param string $new_name the new table name
 
501
     *
 
502
     * @throws Ruckusing_Exception
 
503
     * @return boolean
 
504
     */
 
505
    public function rename_table($name, $new_name)
 
506
    {
 
507
        if (empty($name)) {
 
508
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing original column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
509
        }
 
510
        if (empty($new_name)) {
 
511
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing new column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
512
        }
 
513
        $sql = \sprintf("ALTER TABLE %s RENAME TO %s", $this->identifier($name), $this->identifier($new_name));
 
514
        $this->execute_ddl($sql);
 
515
        $pk_and_sequence_for = $this->pk_and_sequence_for($new_name);
 
516
        if (!empty($pk_and_sequence_for)) {
 
517
            list($pk, $seq) = $pk_and_sequence_for;
 
518
            if ($seq == "{$name}_{$pk}_seq") {
 
519
                $new_seq = "{$new_name}_{$pk}_seq";
 
520
                $this->execute_ddl("ALTER TABLE {$seq} RENAME TO {$new_seq}");
 
521
            }
 
522
        }
 
523
    }
 
524
    /**
 
525
     * Add a column
 
526
     *
 
527
     * @param string $table_name the table name
 
528
     * @param string $column_name the column name
 
529
     * @param string $type the column type
 
530
     * @param array $options column options
 
531
     *
 
532
     * @throws Ruckusing_Exception
 
533
     * @return boolean
 
534
     */
 
535
    public function add_column($table_name, $column_name, $type, $options = array())
 
536
    {
 
537
        if (empty($table_name)) {
 
538
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
539
        }
 
540
        if (empty($column_name)) {
 
541
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
542
        }
 
543
        if (empty($type)) {
 
544
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing type parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
545
        }
 
546
        //default types
 
547
        if (!\array_key_exists('limit', $options)) {
 
548
            $options['limit'] = null;
 
549
        }
 
550
        if (!\array_key_exists('precision', $options)) {
 
551
            $options['precision'] = null;
 
552
        }
 
553
        if (!\array_key_exists('scale', $options)) {
 
554
            $options['scale'] = null;
 
555
        }
 
556
        $sql = \sprintf("ALTER TABLE %s ADD COLUMN %s %s", $this->quote_table_name($table_name), $this->quote_column_name($column_name), $this->type_to_sql($type, $options));
 
557
        $sql .= $this->add_column_options($type, $options);
 
558
        return $this->execute_ddl($sql);
 
559
    }
 
560
    /**
 
561
     * Drop a column
 
562
     *
 
563
     * @param string $table_name  the table name
 
564
     * @param string $column_name the column name
 
565
     *
 
566
     * @return boolean
 
567
     */
 
568
    public function remove_column($table_name, $column_name)
 
569
    {
 
570
        $sql = \sprintf("ALTER TABLE %s DROP COLUMN %s", $this->quote_table_name($table_name), $this->quote_column_name($column_name));
 
571
        return $this->execute_ddl($sql);
 
572
    }
 
573
    /**
 
574
     * Rename a column
 
575
     *
 
576
     * @param string $table_name the table name
 
577
     * @param string $column_name the column name
 
578
     * @param string $new_column_name the new column name
 
579
     *
 
580
     * @throws Ruckusing_Exception
 
581
     * @return boolean
 
582
     */
 
583
    public function rename_column($table_name, $column_name, $new_column_name)
 
584
    {
 
585
        if (empty($table_name)) {
 
586
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
587
        }
 
588
        if (empty($column_name)) {
 
589
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing original column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
590
        }
 
591
        if (empty($new_column_name)) {
 
592
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing new column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
593
        }
 
594
        $column_info = $this->column_info($table_name, $column_name);
 
595
        $current_type = $column_info['type'];
 
596
        $sql = \sprintf("ALTER TABLE %s RENAME COLUMN %s TO %s", $this->quote_table_name($table_name), $this->quote_column_name($column_name), $this->quote_column_name($new_column_name));
 
597
        return $this->execute_ddl($sql);
 
598
    }
 
599
    /**
 
600
     * Change a column
 
601
     *
 
602
     * @param string $table_name the table name
 
603
     * @param string $column_name the column name
 
604
     * @param string $type the column type
 
605
     * @param array $options column options
 
606
     *
 
607
     * @throws Ruckusing_Exception
 
608
     * @return boolean
 
609
     */
 
610
    public function change_column($table_name, $column_name, $type, $options = array())
 
611
    {
 
612
        if (empty($table_name)) {
 
613
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
614
        }
 
615
        if (empty($column_name)) {
 
616
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing original column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
617
        }
 
618
        if (empty($type)) {
 
619
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing type parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
620
        }
 
621
        $column_info = $this->column_info($table_name, $column_name);
 
622
        //default types
 
623
        if (!\array_key_exists('limit', $options)) {
 
624
            $options['limit'] = null;
 
625
        }
 
626
        if (!\array_key_exists('precision', $options)) {
 
627
            $options['precision'] = null;
 
628
        }
 
629
        if (!\array_key_exists('scale', $options)) {
 
630
            $options['scale'] = null;
 
631
        }
 
632
        $sql = \sprintf("ALTER TABLE %s ALTER COLUMN %s TYPE %s", $this->quote_table_name($table_name), $this->quote_column_name($column_name), $this->type_to_sql($type, $options));
 
633
        $sql .= $this->add_column_options($type, $options, \true);
 
634
        if (\array_key_exists('default', $options)) {
 
635
            $this->change_column_default($table_name, $column_name, $options['default']);
 
636
        }
 
637
        if (\array_key_exists('null', $options)) {
 
638
            $default = \array_key_exists('default', $options) ? $options['default'] : null;
 
639
            $this->change_column_null($table_name, $column_name, $options['null'], $default);
 
640
        }
 
641
        return $this->execute_ddl($sql);
 
642
    }
 
643
    /**
 
644
     * Change column default
 
645
     *
 
646
     * @param string $table_name  the table name
 
647
     * @param string $column_name the column name
 
648
     * @param string $default
 
649
     *
 
650
     * @return boolean
 
651
     */
 
652
    private function change_column_default($table_name, $column_name, $default)
 
653
    {
 
654
        $sql = \sprintf("ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s", $this->quote_table_name($table_name), $this->quote_column_name($column_name), $this->quote($default));
 
655
        $this->execute_ddl($sql);
 
656
    }
 
657
    /**
 
658
     * Change column null
 
659
     *
 
660
     * @param string $table_name  the table name
 
661
     * @param string $column_name the column name
 
662
     * @param string $null
 
663
     * @param string $default
 
664
     *
 
665
     * @return boolean
 
666
     */
 
667
    private function change_column_null($table_name, $column_name, $null, $default = null)
 
668
    {
 
669
        if ($null === \false || $default !== null) {
 
670
            $sql = \sprintf("UPDATE %s SET %s=%s WHERE %s IS NULL", $this->quote_table_name($table_name), $this->quote_column_name($column_name), $this->quote($default), $this->quote_column_name($column_name));
 
671
            $this->query($sql);
 
672
        }
 
673
        $sql = \sprintf("ALTER TABLE %s ALTER %s %s NOT NULL", $this->quote_table_name($table_name), $this->quote_column_name($column_name), $null ? 'DROP' : 'SET');
 
674
        $this->query($sql);
 
675
    }
 
676
    /**
 
677
     * Get a column info
 
678
     *
 
679
     * @param string $table the table name
 
680
     * @param string $column the column name
 
681
     *
 
682
     * @throws Ruckusing_Exception
 
683
     * @return array
 
684
     */
 
685
    public function column_info($table, $column)
 
686
    {
 
687
        if (empty($table)) {
 
688
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
689
        }
 
690
        if (empty($column)) {
 
691
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing original column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
692
        }
 
693
        try {
 
694
            $sql = <<<SQL
 
695
      SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
 
696
        FROM pg_attribute a LEFT JOIN pg_attrdef d
 
697
          ON a.attrelid = d.adrelid AND a.attnum = d.adnum
 
698
       WHERE a.attrelid = '%s'::regclass
 
699
         AND a.attname = '%s'
 
700
         AND a.attnum > 0 AND NOT a.attisdropped
 
701
       ORDER BY a.attnum
 
702
SQL;
 
703
            $sql = \sprintf($sql, $this->quote_table_name($table), $column);
 
704
            $result = $this->select_one($sql);
 
705
            $data = array();
 
706
            if (\is_array($result)) {
 
707
                $data['type'] = $result['format_type'];
 
708
                $data['name'] = $column;
 
709
                $data['field'] = $column;
 
710
                $data['null'] = $result['attnotnull'] == 'f';
 
711
                $data['default'] = $result['adsrc'];
 
712
            } else {
 
713
                $data = null;
 
714
            }
 
715
            return $data;
 
716
        } catch (\Exception $e) {
 
717
            return null;
 
718
        }
 
719
    }
 
720
    /**
 
721
     * Add an index
 
722
     *
 
723
     * @param string $table_name the table name
 
724
     * @param string $column_name the column name
 
725
     * @param array $options index options
 
726
     *
 
727
     * @throws Ruckusing_Exception
 
728
     * @return boolean
 
729
     */
 
730
    public function add_index($table_name, $column_name, $options = array())
 
731
    {
 
732
        if (empty($table_name)) {
 
733
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
734
        }
 
735
        if (empty($column_name)) {
 
736
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
737
        }
 
738
        //unique index?
 
739
        if (\is_array($options) && \array_key_exists('unique', $options) && $options['unique'] === \true) {
 
740
            $unique = \true;
 
741
        } else {
 
742
            $unique = \false;
 
743
        }
 
744
        //did the user specify an index name?
 
745
        if (\is_array($options) && \array_key_exists('name', $options)) {
 
746
            $index_name = $options['name'];
 
747
        } else {
 
748
            $index_name = \YoastSEO_Vendor\Ruckusing_Util_Naming::index_name($table_name, $column_name);
 
749
        }
 
750
        if (\strlen($index_name) > \YoastSEO_Vendor\PG_MAX_IDENTIFIER_LENGTH) {
 
751
            $msg = "The auto-generated index name is too long for Postgres (max is 64 chars). ";
 
752
            $msg .= "Considering using 'name' option parameter to specify a custom name for this index.";
 
753
            $msg .= " Note: you will also need to specify";
 
754
            $msg .= " this custom name in a drop_index() - if you have one.";
 
755
            throw new \YoastSEO_Vendor\Ruckusing_Exception($msg, \YoastSEO_Vendor\Ruckusing_Exception::INVALID_INDEX_NAME);
 
756
        }
 
757
        if (!\is_array($column_name)) {
 
758
            $column_names = array($column_name);
 
759
        } else {
 
760
            $column_names = $column_name;
 
761
        }
 
762
        $cols = array();
 
763
        foreach ($column_names as $name) {
 
764
            $cols[] = $this->quote_column_name($name);
 
765
        }
 
766
        $sql = \sprintf("CREATE %sINDEX %s ON %s(%s)", $unique ? "UNIQUE " : "", $this->quote_column_name($index_name), $this->quote_column_name($table_name), \join(", ", $cols));
 
767
        return $this->execute_ddl($sql);
 
768
    }
 
769
    /**
 
770
     * Drop an index
 
771
     *
 
772
     * @param string $table_name the table name
 
773
     * @param string $column_name the column name
 
774
     * @param array $options index options
 
775
     *
 
776
     * @throws Ruckusing_Exception
 
777
     * @return boolean
 
778
     */
 
779
    public function remove_index($table_name, $column_name, $options = array())
 
780
    {
 
781
        if (empty($table_name)) {
 
782
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
783
        }
 
784
        if (empty($column_name)) {
 
785
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
786
        }
 
787
        //did the user specify an index name?
 
788
        if (\is_array($options) && \array_key_exists('name', $options)) {
 
789
            $index_name = $options['name'];
 
790
        } else {
 
791
            $index_name = \YoastSEO_Vendor\Ruckusing_Util_Naming::index_name($table_name, $column_name);
 
792
        }
 
793
        $sql = \sprintf("DROP INDEX %s", $this->quote_column_name($index_name));
 
794
        return $this->execute_ddl($sql);
 
795
    }
 
796
    /**
 
797
     * Add timestamps
 
798
     *
 
799
     * @param string $table_name          The table name
 
800
     * @param string $created_column_name Created at column name
 
801
     * @param string $updated_column_name Updated at column name
 
802
     *
 
803
     * @return boolean
 
804
     */
 
805
    public function add_timestamps($table_name, $created_column_name, $updated_column_name)
 
806
    {
 
807
        if (empty($table_name)) {
 
808
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
809
        }
 
810
        if (empty($created_column_name)) {
 
811
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing created at column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
812
        }
 
813
        if (empty($updated_column_name)) {
 
814
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing updated at column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
815
        }
 
816
        $created_at = $this->add_column($table_name, $created_column_name, "datetime", array("null" => \false));
 
817
        $updated_at = $this->add_column($table_name, $updated_column_name, "datetime", array("null" => \false));
 
818
        return $created_at && $updated_at;
 
819
    }
 
820
    /**
 
821
     * Remove timestamps
 
822
     *
 
823
     * @param string $table_name          The table name
 
824
     * @param string $created_column_name Created at column name
 
825
     * @param string $updated_column_name Updated at column name
 
826
     *
 
827
     * @return boolean
 
828
     */
 
829
    public function remove_timestamps($table_name, $created_column_name, $updated_column_name)
 
830
    {
 
831
        if (empty($table_name)) {
 
832
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
833
        }
 
834
        if (empty($created_column_name)) {
 
835
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing created at column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
836
        }
 
837
        if (empty($updated_column_name)) {
 
838
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing updated at column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
839
        }
 
840
        $created_at = $this->remove_column($table_name, $created_column_name);
 
841
        $updated_at = $this->remove_column($table_name, $updated_column_name);
 
842
        return $created_at && $updated_at;
 
843
    }
 
844
    /**
 
845
     * Check an index
 
846
     *
 
847
     * @param string $table_name the table name
 
848
     * @param string $column_name the column name
 
849
     * @param array $options index options
 
850
     *
 
851
     * @throws Ruckusing_Exception
 
852
     * @return boolean
 
853
     */
 
854
    public function has_index($table_name, $column_name, $options = array())
 
855
    {
 
856
        if (empty($table_name)) {
 
857
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing table name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
858
        }
 
859
        if (empty($column_name)) {
 
860
            throw new \YoastSEO_Vendor\Ruckusing_Exception("Missing column name parameter", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
861
        }
 
862
        //did the user specify an index name?
 
863
        if (\is_array($options) && \array_key_exists('name', $options)) {
 
864
            $index_name = $options['name'];
 
865
        } else {
 
866
            $index_name = \YoastSEO_Vendor\Ruckusing_Util_Naming::index_name($table_name, $column_name);
 
867
        }
 
868
        $indexes = $this->indexes($table_name);
 
869
        foreach ($indexes as $idx) {
 
870
            if ($idx['name'] == $index_name) {
 
871
                return \true;
 
872
            }
 
873
        }
 
874
        return \false;
 
875
    }
 
876
    /**
 
877
     * Return all indexes of a table
 
878
     *
 
879
     * @param string $table_name the table name
 
880
     *
 
881
     * @return array
 
882
     */
 
883
    public function indexes($table_name)
 
884
    {
 
885
        $sql = <<<SQL
 
886
       SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
 
887
       FROM pg_class t
 
888
       INNER JOIN pg_index d ON t.oid = d.indrelid
 
889
       INNER JOIN pg_class i ON d.indexrelid = i.oid
 
890
       WHERE i.relkind = 'i'
 
891
         AND d.indisprimary = 'f'
 
892
         AND t.relname = '%s'
 
893
         AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
 
894
      ORDER BY i.relname
 
895
SQL;
 
896
        $sql = \sprintf($sql, $table_name);
 
897
        $result = $this->select_all($sql);
 
898
        $indexes = array();
 
899
        foreach ($result as $row) {
 
900
            $indexes[] = array('name' => $row['relname'], 'unique' => $row['indisunique'] == 't' ? \true : \false);
 
901
        }
 
902
        return $indexes;
 
903
    }
 
904
    /**
 
905
     * get primary keys
 
906
     *
 
907
     * @param string $table_name the table name
 
908
     *
 
909
     * @return array
 
910
     */
 
911
    public function primary_keys($table_name)
 
912
    {
 
913
        $sql = <<<SQL
 
914
      SELECT
 
915
        pg_attribute.attname,
 
916
        format_type(pg_attribute.atttypid, pg_attribute.atttypmod)
 
917
      FROM pg_index, pg_class, pg_attribute
 
918
      WHERE
 
919
        pg_class.oid = '%s'::regclass AND
 
920
        indrelid = pg_class.oid AND
 
921
        pg_attribute.attrelid = pg_class.oid AND
 
922
        pg_attribute.attnum = any(pg_index.indkey)
 
923
        AND indisprimary
 
924
SQL;
 
925
        $sql = \sprintf($sql, $table_name);
 
926
        $result = $this->select_all($sql);
 
927
        $primary_keys = array();
 
928
        foreach ($result as $row) {
 
929
            $primary_keys[] = array('name' => $row['attname'], 'type' => $row['format_type']);
 
930
        }
 
931
        return $primary_keys;
 
932
    }
 
933
    /**
 
934
     * Convert type to sql
 
935
     *
 
936
     * @param string $type the native type
 
937
     * @param array $options
 
938
     *
 
939
     * @throws Ruckusing_Exception
 
940
     * @return string
 
941
     */
 
942
    public function type_to_sql($type, $options = array())
 
943
    {
 
944
        $natives = $this->native_database_types();
 
945
        if (!\array_key_exists($type, $natives)) {
 
946
            $error = \sprintf("Error: I dont know what column type of '%s' maps to for Postgres.", $type);
 
947
            $error .= "\nYou provided: {$type}\n";
 
948
            $error .= "Valid types are: \n";
 
949
            $types = \array_keys($natives);
 
950
            foreach ($types as $t) {
 
951
                if ($t == 'primary_key') {
 
952
                    continue;
 
953
                }
 
954
                $error .= "\t{$t}\n";
 
955
            }
 
956
            throw new \YoastSEO_Vendor\Ruckusing_Exception($error, \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
957
        }
 
958
        $scale = null;
 
959
        $precision = null;
 
960
        $limit = null;
 
961
        if (isset($options['precision'])) {
 
962
            $precision = $options['precision'];
 
963
        }
 
964
        if (isset($options['scale'])) {
 
965
            $scale = $options['scale'];
 
966
        }
 
967
        if (isset($options['limit'])) {
 
968
            $limit = $options['limit'];
 
969
        }
 
970
        $native_type = $natives[$type];
 
971
        if (\is_array($native_type) && \array_key_exists('name', $native_type)) {
 
972
            $column_type_sql = $native_type['name'];
 
973
        } else {
 
974
            return $native_type;
 
975
        }
 
976
        if ($type == "decimal") {
 
977
            //ignore limit, use precison and scale
 
978
            if ($precision == null && \array_key_exists('precision', $native_type)) {
 
979
                $precision = $native_type['precision'];
 
980
            }
 
981
            if ($scale == null && \array_key_exists('scale', $native_type)) {
 
982
                $scale = $native_type['scale'];
 
983
            }
 
984
            if ($precision != null) {
 
985
                if (\is_int($scale)) {
 
986
                    $column_type_sql .= \sprintf("(%d, %d)", $precision, $scale);
 
987
                } else {
 
988
                    $column_type_sql .= \sprintf("(%d)", $precision);
 
989
                }
 
990
                //scale
 
991
            } else {
 
992
                if ($scale) {
 
993
                    throw new \YoastSEO_Vendor\Ruckusing_Exception("Error adding decimal column: precision cannot be empty if scale is specified", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_ARGUMENT);
 
994
                }
 
995
            }
 
996
            //pre
 
997
        }
 
998
        // integer columns dont support limit (sizing)
 
999
        if ($native_type['name'] != "integer") {
 
1000
            if ($limit == null && \array_key_exists('limit', $native_type)) {
 
1001
                $limit = $native_type['limit'];
 
1002
            }
 
1003
            if ($limit) {
 
1004
                $column_type_sql .= \sprintf("(%d)", $limit);
 
1005
            }
 
1006
        }
 
1007
        return $column_type_sql;
 
1008
    }
 
1009
    //type_to_sql
 
1010
    /**
 
1011
     * Add column options
 
1012
     *
 
1013
     * @param string  $type              the native type
 
1014
     * @param array   $options
 
1015
     * @param boolean $performing_change
 
1016
     *
 
1017
     * @return string
 
1018
     */
 
1019
    public function add_column_options($type, $options, $performing_change = \false)
 
1020
    {
 
1021
        $sql = "";
 
1022
        if (!\is_array($options)) {
 
1023
            return $sql;
 
1024
        }
 
1025
        if (!$performing_change) {
 
1026
            if (\array_key_exists('default', $options) && $options['default'] !== null) {
 
1027
                if (\is_int($options['default'])) {
 
1028
                    $default_format = '%d';
 
1029
                } elseif (\is_bool($options['default'])) {
 
1030
                    $default_format = "'%d'";
 
1031
                } else {
 
1032
                    $default_format = "'%s'";
 
1033
                }
 
1034
                $default_value = \sprintf($default_format, $options['default']);
 
1035
                $sql .= \sprintf(" DEFAULT %s", $default_value);
 
1036
            }
 
1037
            if (\array_key_exists('null', $options) && $options['null'] === \false) {
 
1038
                $sql .= " NOT NULL";
 
1039
            }
 
1040
        }
 
1041
        return $sql;
 
1042
    }
 
1043
    //add_column_options
 
1044
    /**
 
1045
     * Set current version
 
1046
     *
 
1047
     * @param string $version the version
 
1048
     *
 
1049
     * @return boolean
 
1050
     */
 
1051
    public function set_current_version($version)
 
1052
    {
 
1053
        $sql = \sprintf("INSERT INTO %s (version) VALUES ('%s')", $this->get_schema_version_table_name(), $version);
 
1054
        return $this->execute_ddl($sql);
 
1055
    }
 
1056
    /**
 
1057
     * remove a version
 
1058
     *
 
1059
     * @param string $version the version
 
1060
     *
 
1061
     * @return boolean
 
1062
     */
 
1063
    public function remove_version($version)
 
1064
    {
 
1065
        $sql = \sprintf("DELETE FROM %s WHERE version = '%s'", $this->get_schema_version_table_name(), $version);
 
1066
        return $this->execute_ddl($sql);
 
1067
    }
 
1068
    /**
 
1069
     * Return a message displaying the current version
 
1070
     *
 
1071
     * @return string
 
1072
     */
 
1073
    public function __toString()
 
1074
    {
 
1075
        return "Ruckusing_Adapter_PgSQL_Base, version " . $this->_version;
 
1076
    }
 
1077
    //-----------------------------------
 
1078
    // PRIVATE METHODS
 
1079
    //-----------------------------------
 
1080
    /**
 
1081
     * Connect to the db
 
1082
     *
 
1083
     * @param string $dsn the current dsn
 
1084
     */
 
1085
    private function connect($dsn)
 
1086
    {
 
1087
        $this->db_connect($dsn);
 
1088
    }
 
1089
    /**
 
1090
     * Connect to the db
 
1091
     *
 
1092
     * @param string $dsn the current dsn
 
1093
     *
 
1094
     * @throws Ruckusing_Exception
 
1095
     * @return boolean
 
1096
     */
 
1097
    private function db_connect($dsn)
 
1098
    {
 
1099
        if (!\function_exists('pg_connect')) {
 
1100
            throw new \YoastSEO_Vendor\Ruckusing_Exception("\nIt appears you have not compiled PHP with Postgres support: missing function pg_connect()", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
 
1101
        }
 
1102
        $db_info = $this->get_dsn();
 
1103
        if ($db_info) {
 
1104
            $this->db_info = $db_info;
 
1105
            $conninfo = \sprintf('host=%s port=%s dbname=%s user=%s password=%s', $db_info['host'], !empty($db_info['port']) ? $db_info['port'] : '5432', $db_info['database'], $db_info['user'], $db_info['password']);
 
1106
            $this->conn = \pg_connect($conninfo);
 
1107
            if ($this->conn === \FALSE) {
 
1108
                throw new \YoastSEO_Vendor\Ruckusing_Exception("\n\nCould not connect to the DB, check host / user / password\n\n", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
 
1109
            }
 
1110
            return \true;
 
1111
        } else {
 
1112
            throw new \YoastSEO_Vendor\Ruckusing_Exception("\n\nCould not extract DB connection information from: {$dsn}\n\n", \YoastSEO_Vendor\Ruckusing_Exception::INVALID_CONFIG);
 
1113
        }
 
1114
    }
 
1115
    /**
 
1116
     * Delegate to PEAR
 
1117
     *
 
1118
     * @param boolean $o
 
1119
     *
 
1120
     * @return boolean
 
1121
     */
 
1122
    private function isError($o)
 
1123
    {
 
1124
        return $o === \FALSE;
 
1125
    }
 
1126
    /**
 
1127
     * Initialize an array of table names
 
1128
     *
 
1129
     * @param boolean $reload
 
1130
     */
 
1131
    private function load_tables($reload = \true)
 
1132
    {
 
1133
        if ($this->_tables_loaded == \false || $reload) {
 
1134
            $this->_tables = array();
 
1135
            //clear existing structure
 
1136
            $sql = "SELECT tablename FROM pg_tables WHERE schemaname = ANY (current_schemas(false))";
 
1137
            $res = \pg_query($this->conn, $sql);
 
1138
            while ($row = \pg_fetch_row($res)) {
 
1139
                $table = $row[0];
 
1140
                $this->_tables[$table] = \true;
 
1141
            }
 
1142
        }
 
1143
    }
 
1144
    /**
 
1145
     * Check query type
 
1146
     *
 
1147
     * @param string $query query to run
 
1148
     *
 
1149
     * @return int
 
1150
     */
 
1151
    private function determine_query_type($query)
 
1152
    {
 
1153
        $query = \strtolower(\trim($query));
 
1154
        $match = array();
 
1155
        \preg_match('/^(\\w)*/i', $query, $match);
 
1156
        $type = $match[0];
 
1157
        switch ($type) {
 
1158
            case 'select':
 
1159
                return \YoastSEO_Vendor\SQL_SELECT;
 
1160
            case 'update':
 
1161
                return \YoastSEO_Vendor\SQL_UPDATE;
 
1162
            case 'delete':
 
1163
                return \YoastSEO_Vendor\SQL_DELETE;
 
1164
            case 'insert':
 
1165
                return \YoastSEO_Vendor\SQL_INSERT;
 
1166
            case 'alter':
 
1167
                return \YoastSEO_Vendor\SQL_ALTER;
 
1168
            case 'drop':
 
1169
                return \YoastSEO_Vendor\SQL_DROP;
 
1170
            case 'create':
 
1171
                return \YoastSEO_Vendor\SQL_CREATE;
 
1172
            case 'show':
 
1173
                return \YoastSEO_Vendor\SQL_SHOW;
 
1174
            case 'rename':
 
1175
                return \YoastSEO_Vendor\SQL_RENAME;
 
1176
            case 'set':
 
1177
                return \YoastSEO_Vendor\SQL_SET;
 
1178
            default:
 
1179
                return \YoastSEO_Vendor\SQL_UNKNOWN_QUERY_TYPE;
 
1180
        }
 
1181
    }
 
1182
    private function is_select($query_type)
 
1183
    {
 
1184
        return $query_type == \YoastSEO_Vendor\SQL_SELECT;
 
1185
    }
 
1186
    /**
 
1187
     * Detect whether or not the string represents a function call and if so
 
1188
     * do not wrap it in single-quotes, otherwise do wrap in single quotes.
 
1189
     *
 
1190
     * @param string $str
 
1191
     *
 
1192
     * @return boolean
 
1193
     */
 
1194
    private function is_sql_method_call($str)
 
1195
    {
 
1196
        $str = \trim($str);
 
1197
        return \substr($str, -2, 2) == "()";
 
1198
    }
 
1199
    /**
 
1200
     * Check if in transaction
 
1201
     *
 
1202
     * @return boolean
 
1203
     */
 
1204
    private function inTransaction()
 
1205
    {
 
1206
        return $this->_in_trx;
 
1207
    }
 
1208
    /**
 
1209
     * Start transaction
 
1210
     */
 
1211
    private function beginTransaction()
 
1212
    {
 
1213
        if ($this->_in_trx === \true) {
 
1214
            throw new \YoastSEO_Vendor\Ruckusing_Exception('Transaction already started', \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
1215
        }
 
1216
        \pg_query($this->conn, "BEGIN");
 
1217
        $this->_in_trx = \true;
 
1218
    }
 
1219
    /**
 
1220
     * Commit a transaction
 
1221
     */
 
1222
    private function commit()
 
1223
    {
 
1224
        if ($this->_in_trx === \false) {
 
1225
            throw new \YoastSEO_Vendor\Ruckusing_Exception('Transaction not started', \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
1226
        }
 
1227
        \pg_query($this->conn, "COMMIT");
 
1228
        $this->_in_trx = \false;
 
1229
    }
 
1230
    /**
 
1231
     * Rollback a transaction
 
1232
     */
 
1233
    private function rollback()
 
1234
    {
 
1235
        if ($this->_in_trx === \false) {
 
1236
            throw new \YoastSEO_Vendor\Ruckusing_Exception('Transaction not started', \YoastSEO_Vendor\Ruckusing_Exception::QUERY_ERROR);
 
1237
        }
 
1238
        \pg_query($this->conn, "ROLLBACK");
 
1239
        $this->_in_trx = \false;
 
1240
    }
 
1241
}