[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/database/src/Pgsql/ -> PgsqlDriver.php (source)

   1  <?php
   2  /**
   3   * Part of the Joomla Framework Database Package
   4   *
   5   * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
   6   * @license    GNU General Public License version 2 or later; see LICENSE
   7   */
   8  
   9  namespace Joomla\Database\Pgsql;
  10  
  11  use Joomla\Database\Pdo\PdoDriver;
  12  
  13  /**
  14   * PostgreSQL PDO Database Driver
  15   *
  16   * @since  1.5.0
  17   */
  18  class PgsqlDriver extends PdoDriver
  19  {
  20      /**
  21       * The database driver name
  22       *
  23       * @var    string
  24       * @since  1.5.0
  25       */
  26      public $name = 'pgsql';
  27  
  28      /**
  29       * The character(s) used to quote SQL statement names such as table names or field names,
  30       * etc. The child classes should define this as necessary.  If a single character string the
  31       * same character is used for both sides of the quoted name, else the first character will be
  32       * used for the opening quote and the second for the closing quote.
  33       *
  34       * @var    string
  35       * @since  1.5.0
  36       */
  37      protected $nameQuote = '"';
  38  
  39      /**
  40       * The null or zero representation of a timestamp for the database driver.  This should be
  41       * defined in child classes to hold the appropriate value for the engine.
  42       *
  43       * @var    string
  44       * @since  1.5.0
  45       */
  46      protected $nullDate = '1970-01-01 00:00:00';
  47  
  48      /**
  49       * The minimum supported database version.
  50       *
  51       * @var    string
  52       * @since  1.5.0
  53       */
  54      protected static $dbMinimum = '9.4.0';
  55  
  56      /**
  57       * Operator used for concatenation
  58       *
  59       * @var    string
  60       * @since  1.5.0
  61       */
  62      protected $concat_operator = '||';
  63  
  64      /**
  65       * Database object constructor
  66       *
  67       * @param   array  $options  List of options used to configure the connection
  68       *
  69       * @since    1.5.0
  70       */
  71  	public function __construct($options)
  72      {
  73          $options['driver']   = 'pgsql';
  74          $options['host']     = $options['host'] ?? 'localhost';
  75          $options['user']     = $options['user'] ?? '';
  76          $options['password'] = $options['password'] ?? '';
  77          $options['database'] = $options['database'] ?? '';
  78          $options['port']     = $options['port'] ?? null;
  79  
  80          // Finalize initialization
  81          parent::__construct($options);
  82      }
  83  
  84      /**
  85       * Connects to the database if needed.
  86       *
  87       * @return  void
  88       *
  89       * @since   1.5.0
  90       * @throws  \RuntimeException
  91       */
  92  	public function connect()
  93      {
  94          if ($this->getConnection())
  95          {
  96              return;
  97          }
  98  
  99          parent::connect();
 100  
 101          $this->setQuery('SET standard_conforming_strings = off')->execute();
 102      }
 103  
 104      /**
 105       * Method to get the database collation in use by sampling a text field of a table in the database.
 106       *
 107       * @return  string|boolean  The collation in use by the database or boolean false if not supported.
 108       *
 109       * @since   1.5.0
 110       * @throws  \RuntimeException
 111       */
 112  	public function getCollation()
 113      {
 114          $this->setQuery('SHOW LC_COLLATE');
 115          $array = $this->loadAssocList();
 116  
 117          return $array[0]['lc_collate'];
 118      }
 119  
 120      /**
 121       * Method to get the database connection collation in use by sampling a text field of a table in the database.
 122       *
 123       * @return  string|boolean  The collation in use by the database connection (string) or boolean false if not supported.
 124       *
 125       * @since   1.6.0
 126       * @throws  \RuntimeException
 127       */
 128  	public function getConnectionCollation()
 129      {
 130          $this->setQuery('SHOW LC_COLLATE');
 131          $array = $this->loadAssocList();
 132  
 133          return $array[0]['lc_collate'];
 134      }
 135  
 136      /**
 137       * Method to get the database encryption details (cipher and protocol) in use.
 138       *
 139       * @return  string  The database encryption details.
 140       *
 141       * @since   2.0.0
 142       * @throws  \RuntimeException
 143       */
 144  	public function getConnectionEncryption(): string
 145      {
 146          // Requires PostgreSQL 9.5 or newer
 147          if (version_compare($this->getVersion(), '9.5', '<'))
 148          {
 149              return '';
 150          }
 151  
 152          $query = $this->getQuery(true)
 153              ->select($this->quoteName(['version', 'cipher']))
 154              ->from($this->quoteName('pg_stat_ssl'))
 155              ->where($this->quoteName('pid') . ' = pg_backend_pid()');
 156  
 157          $variables = $this->setQuery($query)->loadAssoc();
 158  
 159          if (!empty($variables['cipher']))
 160          {
 161              return $variables['version'] . ' (' . $variables['cipher'] . ')';
 162          }
 163  
 164          return '';
 165      }
 166  
 167      /**
 168       * Method to test if the database TLS connections encryption are supported.
 169       *
 170       * @return  boolean  Whether the database supports TLS connections encryption.
 171       *
 172       * @since   2.0.0
 173       */
 174  	public function isConnectionEncryptionSupported(): bool
 175      {
 176          $variables = $this->setQuery('SHOW "ssl"')->loadAssoc();
 177  
 178          return !empty($variables['ssl']) && $variables['ssl'] === 'on';
 179      }
 180  
 181      /**
 182       * Internal function to get the name of the default schema for the current PostgreSQL connection.
 183       * That is the schema where tables are created by Joomla.
 184       *
 185       * @return  string
 186       *
 187       * @since   1.8.0
 188       */
 189  	private function getDefaultSchema()
 190      {
 191          // Supported since PostgreSQL 7.3
 192          $this->setQuery('SELECT (current_schemas(false))[1]');
 193  
 194          return $this->loadResult();
 195      }
 196  
 197      /**
 198       * Shows the table CREATE statement that creates the given tables.
 199       *
 200       * This is unsupported by PostgreSQL.
 201       *
 202       * @param   mixed  $tables  A table name or a list of table names.
 203       *
 204       * @return  string  An empty string because this function is not supported by PostgreSQL.
 205       *
 206       * @since   1.5.0
 207       * @throws  \RuntimeException
 208       */
 209  	public function getTableCreate($tables)
 210      {
 211          return '';
 212      }
 213  
 214      /**
 215       * Retrieves field information about a given table.
 216       *
 217       * @param   string   $table     The name of the database table.
 218       * @param   boolean  $typeOnly  True to only return field types.
 219       *
 220       * @return  array  An array of fields for the database table.
 221       *
 222       * @since   1.5.0
 223       * @throws  \RuntimeException
 224       */
 225  	public function getTableColumns($table, $typeOnly = true)
 226      {
 227          $this->connect();
 228  
 229          $result        = [];
 230          $tableSub      = $this->replacePrefix($table);
 231          $defaultSchema = $this->getDefaultSchema();
 232  
 233          $this->setQuery('
 234              SELECT a.attname AS "column_name",
 235                  pg_catalog.format_type(a.atttypid, a.atttypmod) as "type",
 236                  CASE WHEN a.attnotnull IS TRUE
 237                      THEN \'NO\'
 238                      ELSE \'YES\'
 239                  END AS "null",
 240                  CASE WHEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) IS NOT NULL
 241                      THEN pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true)
 242                  END as "Default",
 243                  CASE WHEN pg_catalog.col_description(a.attrelid, a.attnum) IS NULL
 244                  THEN \'\'
 245                  ELSE pg_catalog.col_description(a.attrelid, a.attnum)
 246                  END  AS "comments"
 247              FROM pg_catalog.pg_attribute a
 248              LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum
 249              LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
 250              WHERE a.attrelid =
 251                  (SELECT oid FROM pg_catalog.pg_class WHERE relname=' . $this->quote($tableSub) . '
 252                      AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
 253                      nspname = ' . $this->quote($defaultSchema) . ')
 254                  )
 255              AND a.attnum > 0 AND NOT a.attisdropped
 256              ORDER BY a.attnum'
 257          );
 258  
 259          $fields = $this->loadObjectList();
 260  
 261          if ($typeOnly)
 262          {
 263              foreach ($fields as $field)
 264              {
 265                  $result[$field->column_name] = preg_replace('/[(0-9)]/', '', $field->type);
 266              }
 267          }
 268          else
 269          {
 270              foreach ($fields as $field)
 271              {
 272                  if ($field->Default !== null)
 273                  {
 274                      // Normalise default values like datetime
 275                      if (preg_match('/^\'(.*)\'::.*/', $field->Default, $matches))
 276                      {
 277                          $field->Default = $matches[1];
 278                      }
 279  
 280                      // Change Postgresql's NULL::* type with PHP's null one. Do this last to avoid PHP type issues in PHP 8.1 and up
 281                      if (preg_match('/^NULL::*/', $field->Default))
 282                      {
 283                          $field->Default = null;
 284                      }
 285                  }
 286  
 287                  // Do some dirty translation to MySQL output.
 288                  // @todo: Come up with and implement a standard across databases.
 289                  $result[$field->column_name] = (object) [
 290                      'column_name' => $field->column_name,
 291                      'type'        => $field->type,
 292                      'null'        => $field->null,
 293                      'Default'     => $field->Default,
 294                      'comments'    => '',
 295                      'Field'       => $field->column_name,
 296                      'Type'        => $field->type,
 297                      'Null'        => $field->null,
 298                      // @todo: Improve query above to return primary key info as well
 299                      // 'Key' => ($field->PK == '1' ? 'PRI' : '')
 300                  ];
 301              }
 302          }
 303  
 304          return $result;
 305      }
 306  
 307      /**
 308       * Get the details list of keys for a table.
 309       *
 310       * @param   string  $table  The name of the table.
 311       *
 312       * @return  array  An array of the column specification for the table.
 313       *
 314       * @since   1.5.0
 315       * @throws  \RuntimeException
 316       */
 317  	public function getTableKeys($table)
 318      {
 319          $this->connect();
 320  
 321          // To check if table exists and prevent SQL injection
 322          $tableList = $this->getTableList();
 323          $tableSub  = $this->replacePrefix($table);
 324  
 325          if (\in_array($tableSub, $tableList, true))
 326          {
 327              // Get the details columns information.
 328              $this->setQuery('
 329                  SELECT indexname AS "idxName", indisprimary AS "isPrimary", indisunique  AS "isUnique", indkey AS "indKey",
 330                      CASE WHEN indisprimary = true THEN
 331                          ( SELECT \'ALTER TABLE \' || tablename || \' ADD \' || pg_catalog.pg_get_constraintdef(const.oid, true)
 332                              FROM pg_constraint AS const WHERE const.conname= pgClassFirst.relname )
 333                      ELSE pg_catalog.pg_get_indexdef(indexrelid, 0, true)
 334                      END AS "Query"
 335                  FROM pg_indexes
 336                  LEFT JOIN pg_class AS pgClassFirst ON indexname=pgClassFirst.relname
 337                  LEFT JOIN pg_index AS pgIndex ON pgClassFirst.oid=pgIndex.indexrelid
 338                  WHERE tablename=' . $this->quote($tableSub) . ' ORDER BY indkey'
 339              );
 340  
 341              return $this->loadObjectList();
 342          }
 343  
 344          return [];
 345      }
 346  
 347      /**
 348       * Get the list of column names this index indexes.
 349       *
 350       * @param   string  $table   The name of the table.
 351       * @param   string  $indKey  The list of column numbers for the table
 352       *
 353       * @return  string  A list of the column names for the table.
 354       *
 355       * @since   2.0.0
 356       * @throws  \RuntimeException
 357       */
 358  	public function getNamesKey($table, $indKey)
 359      {
 360          $this->connect();
 361  
 362          $tableSub = $this->replacePrefix($table);
 363  
 364          $tabInd   = explode(' ', $indKey);
 365          $colNames = [];
 366  
 367          foreach ($tabInd as $numCol)
 368          {
 369              $query = $this->getQuery(true)
 370                  ->select('attname')
 371                  ->from('pg_attribute')
 372                  ->join('LEFT', 'pg_class ON pg_class.relname=' . $this->quote($tableSub))
 373                  ->where('attnum=' . $numCol . ' AND attrelid=pg_class.oid');
 374              $this->setQuery($query);
 375              $colNames[] = $this->loadResult();
 376          }
 377  
 378          return implode(', ', $colNames);
 379      }
 380  
 381      /**
 382       * Method to get an array of all tables in the database.
 383       *
 384       * @return  array  An array of all the tables in the database.
 385       *
 386       * @since   1.5.0
 387       * @throws  \RuntimeException
 388       */
 389  	public function getTableList()
 390      {
 391          $query = $this->getQuery(true)
 392              ->select('table_name')
 393              ->from('information_schema.tables')
 394              ->where('table_type = ' . $this->quote('BASE TABLE'))
 395              ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ')')
 396              ->order('table_name ASC');
 397  
 398          $this->setQuery($query);
 399  
 400          return $this->loadColumn();
 401      }
 402  
 403      /**
 404       * Get the details list of sequences for a table.
 405       *
 406       * @param   string  $table  The name of the table.
 407       *
 408       * @return  array  An array of sequences specification for the table.
 409       *
 410       * @since   1.5.0
 411       * @throws  \RuntimeException
 412       */
 413  	public function getTableSequences($table)
 414      {
 415          // To check if table exists and prevent SQL injection
 416          $tableList = $this->getTableList();
 417          $tableSub  = $this->replacePrefix($table);
 418  
 419          if (\in_array($tableSub, $tableList, true))
 420          {
 421              $name = [
 422                  's.relname', 'n.nspname', 't.relname', 'a.attname', 'info.data_type',
 423                  'info.minimum_value', 'info.maximum_value', 'info.increment', 'info.cycle_option', 'info.start_value',
 424              ];
 425  
 426              $as = [
 427                  'sequence', 'schema', 'table', 'column', 'data_type', 'minimum_value', 'maximum_value', 'increment', 'cycle_option', 'start_value',
 428              ];
 429  
 430              // Get the details columns information.
 431              $query = $this->getQuery(true)
 432                  ->select($this->quoteName($name, $as))
 433                  ->from('pg_class AS s')
 434                  ->leftJoin("pg_depend d ON d.objid = s.oid AND d.classid = 'pg_class'::regclass AND d.refclassid = 'pg_class'::regclass")
 435                  ->leftJoin('pg_class t ON t.oid = d.refobjid')
 436                  ->leftJoin('pg_namespace n ON n.oid = t.relnamespace')
 437                  ->leftJoin('pg_attribute a ON a.attrelid = t.oid AND a.attnum = d.refobjsubid')
 438                  ->leftJoin('information_schema.sequences AS info ON info.sequence_name = s.relname')
 439                  ->where('s.relkind = ' . $this->quote('S') . ' AND d.deptype = ' . $this->quote('a') . ' AND t.relname = ' . $this->quote($tableSub));
 440              $this->setQuery($query);
 441  
 442              return $this->loadObjectList();
 443          }
 444  
 445          return [];
 446      }
 447  
 448      /**
 449       * Method to get the last value of a sequence in the database.
 450       *
 451       * @param   string  $sequence  The name of the sequence.
 452       *
 453       * @return  integer  The last value of the sequence.
 454       *
 455       * @since   2.0.0
 456       * @throws  \RuntimeException
 457       */
 458  	public function getSequenceLastValue($sequence)
 459      {
 460          $this->connect();
 461  
 462          $query = $this->getQuery(true)
 463              ->select($this->quoteName('last_value'))
 464              ->from($sequence);
 465  
 466          $this->setQuery($query);
 467  
 468          return $this->loadResult();
 469      }
 470  
 471      /**
 472       * Method to get the is_called attribute of a sequence.
 473       *
 474       * @param   string  $sequence  The name of the sequence.
 475       *
 476       * @return  boolean  The is_called attribute of the sequence.
 477       *
 478       * @since   2.0.0
 479       * @throws  \RuntimeException
 480       */
 481  	public function getSequenceIsCalled($sequence)
 482      {
 483          $this->connect();
 484  
 485          $query = $this->getQuery(true)
 486              ->select($this->quoteName('is_called'))
 487              ->from($sequence);
 488  
 489          $this->setQuery($query);
 490  
 491          return $this->loadResult();
 492      }
 493  
 494      /**
 495       * Locks a table in the database.
 496       *
 497       * @param   string  $tableName  The name of the table to unlock.
 498       *
 499       * @return  $this
 500       *
 501       * @since   1.5.0
 502       * @throws  \RuntimeException
 503       */
 504  	public function lockTable($tableName)
 505      {
 506          $this->transactionStart();
 507          $this->setQuery('LOCK TABLE ' . $this->quoteName($tableName) . ' IN ACCESS EXCLUSIVE MODE')->execute();
 508  
 509          return $this;
 510      }
 511  
 512      /**
 513       * Renames a table in the database.
 514       *
 515       * @param   string  $oldTable  The name of the table to be renamed
 516       * @param   string  $newTable  The new name for the table.
 517       * @param   string  $backup    Not used by PostgreSQL.
 518       * @param   string  $prefix    Not used by PostgreSQL.
 519       *
 520       * @return  $this
 521       *
 522       * @since   1.5.0
 523       * @throws  \RuntimeException
 524       */
 525  	public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
 526      {
 527          $this->connect();
 528  
 529          $oldTable = $this->replacePrefix($oldTable);
 530          $newTable = $this->replacePrefix($newTable);
 531  
 532          // To check if table exists and prevent SQL injection
 533          $tableList = $this->getTableList();
 534  
 535          // Origin Table does not exist
 536          if (!\in_array($oldTable, $tableList, true))
 537          {
 538              // Origin Table not found
 539              throw new \RuntimeException('Table not found in Postgresql database.');
 540          }
 541  
 542          // Rename indexes
 543          $subQuery = $this->getQuery(true)
 544              ->select('indexrelid')
 545              ->from('pg_index, pg_class')
 546              ->where('pg_class.relname = ' . $this->quote($oldTable))
 547              ->where('pg_class.oid = pg_index.indrelid');
 548  
 549          $this->setQuery(
 550              $this->getQuery(true)
 551                  ->select('relname')
 552                  ->from('pg_class')
 553                  ->where('oid IN (' . (string) $subQuery . ')')
 554          );
 555  
 556          $oldIndexes = $this->loadColumn();
 557  
 558          foreach ($oldIndexes as $oldIndex)
 559          {
 560              $changedIdxName = str_replace($oldTable, $newTable, $oldIndex);
 561              $this->setQuery('ALTER INDEX ' . $this->escape($oldIndex) . ' RENAME TO ' . $this->escape($changedIdxName))->execute();
 562          }
 563  
 564          // Rename sequences
 565          $subQuery = $this->getQuery(true)
 566              ->select('oid')
 567              ->from('pg_namespace')
 568              ->where('nspname NOT LIKE ' . $this->quote('pg_%'))
 569              ->where('nspname != ' . $this->quote('information_schema'));
 570  
 571          $this->setQuery(
 572              $this->getQuery(true)
 573                  ->select('relname')
 574                  ->from('pg_class')
 575                  ->where('relkind = ' . $this->quote('S'))
 576                  ->where('relnamespace IN (' . (string) $subQuery . ')')
 577                  ->where('relname LIKE ' . $this->quote("%$oldTable%"))
 578          );
 579  
 580          $oldSequences = $this->loadColumn();
 581  
 582          foreach ($oldSequences as $oldSequence)
 583          {
 584              $changedSequenceName = str_replace($oldTable, $newTable, $oldSequence);
 585              $this->setQuery('ALTER SEQUENCE ' . $this->escape($oldSequence) . ' RENAME TO ' . $this->escape($changedSequenceName))->execute();
 586          }
 587  
 588          // Rename table
 589          $this->setQuery('ALTER TABLE ' . $this->escape($oldTable) . ' RENAME TO ' . $this->escape($newTable))->execute();
 590  
 591          return $this;
 592      }
 593  
 594      /**
 595       * This function return a field value as a prepared string to be used in a SQL statement.
 596       *
 597       * @param   array   $columns     Array of table's column returned by ::getTableColumns.
 598       * @param   string  $fieldName   The table field's name.
 599       * @param   string  $fieldValue  The variable value to quote and return.
 600       *
 601       * @return  string  The quoted string.
 602       *
 603       * @since   1.5.0
 604       */
 605  	public function sqlValue($columns, $fieldName, $fieldValue)
 606      {
 607          switch ($columns[$fieldName])
 608          {
 609              case 'boolean':
 610                  $val = 'NULL';
 611  
 612                  if ($fieldValue === 't' || $fieldValue === true || $fieldValue === 1 || $fieldValue === '1')
 613                  {
 614                      $val = 'TRUE';
 615                  }
 616                  elseif ($fieldValue === 'f' || $fieldValue === false || $fieldValue === 0 || $fieldValue === '0')
 617                  {
 618                      $val = 'FALSE';
 619                  }
 620  
 621                  break;
 622  
 623              case 'bigint':
 624              case 'bigserial':
 625              case 'integer':
 626              case 'money':
 627              case 'numeric':
 628              case 'real':
 629              case 'smallint':
 630              case 'serial':
 631              case 'numeric,':
 632                  $val = $fieldValue === '' ? 'NULL' : $fieldValue;
 633  
 634                  break;
 635  
 636              case 'timestamp without time zone':
 637              case 'date':
 638                  if (empty($fieldValue))
 639                  {
 640                      $fieldValue = $this->getNullDate();
 641                  }
 642  
 643                  $val = $this->quote($fieldValue);
 644  
 645                  break;
 646  
 647              default:
 648                  $val = $this->quote($fieldValue);
 649  
 650                  break;
 651          }
 652  
 653          return $val;
 654      }
 655  
 656      /**
 657       * Method to commit a transaction.
 658       *
 659       * @param   boolean  $toSavepoint  If true, commit to the last savepoint.
 660       *
 661       * @return  void
 662       *
 663       * @since   1.0
 664       * @throws  \RuntimeException
 665       */
 666  	public function transactionCommit($toSavepoint = false)
 667      {
 668          $this->connect();
 669  
 670          if (!$toSavepoint || $this->transactionDepth <= 1)
 671          {
 672              parent::transactionCommit($toSavepoint);
 673          }
 674          else
 675          {
 676              $this->transactionDepth--;
 677          }
 678      }
 679  
 680      /**
 681       * Method to roll back a transaction.
 682       *
 683       * @param   boolean  $toSavepoint  If true, rollback to the last savepoint.
 684       *
 685       * @return  void
 686       *
 687       * @since   1.0
 688       * @throws  \RuntimeException
 689       */
 690  	public function transactionRollback($toSavepoint = false)
 691      {
 692          $this->connect();
 693  
 694          if (!$toSavepoint || $this->transactionDepth <= 1)
 695          {
 696              parent::transactionRollback($toSavepoint);
 697          }
 698          else
 699          {
 700              $savepoint = 'SP_' . ($this->transactionDepth - 1);
 701              $this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint));
 702  
 703              if ($this->execute())
 704              {
 705                  $this->transactionDepth--;
 706                  $this->setQuery('RELEASE SAVEPOINT ' . $this->quoteName($savepoint))->execute();
 707              }
 708          }
 709      }
 710  
 711      /**
 712       * Method to initialize a transaction.
 713       *
 714       * @param   boolean  $asSavepoint  If true and a transaction is already active, a savepoint will be created.
 715       *
 716       * @return  void
 717       *
 718       * @since   1.0
 719       * @throws  \RuntimeException
 720       */
 721  	public function transactionStart($asSavepoint = false)
 722      {
 723          $this->connect();
 724  
 725          if (!$asSavepoint || !$this->transactionDepth)
 726          {
 727              parent::transactionStart($asSavepoint);
 728          }
 729          else
 730          {
 731              $savepoint = 'SP_' . $this->transactionDepth;
 732              $this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint));
 733  
 734              if ($this->execute())
 735              {
 736                  $this->transactionDepth++;
 737              }
 738          }
 739      }
 740  
 741      /**
 742       * Inserts a row into a table based on an object's properties.
 743       *
 744       * @param   string  $table   The name of the database table to insert into.
 745       * @param   object  $object  A reference to an object whose public properties match the table fields.
 746       * @param   string  $key     The name of the primary key. If provided the object property is updated.
 747       *
 748       * @return  boolean    True on success.
 749       *
 750       * @since   1.5.0
 751       * @throws  \RuntimeException
 752       */
 753  	public function insertObject($table, &$object, $key = null)
 754      {
 755          $columns = $this->getTableColumns($table);
 756  
 757          $fields = [];
 758          $values = [];
 759  
 760          // Iterate over the object variables to build the query fields and values.
 761          foreach (get_object_vars($object) as $k => $v)
 762          {
 763              // Skip columns that don't exist in the table.
 764              if (!\array_key_exists($k, $columns))
 765              {
 766                  continue;
 767              }
 768  
 769              // Only process non-null scalars.
 770              if (\is_array($v) || \is_object($v) || $v === null)
 771              {
 772                  continue;
 773              }
 774  
 775              // Ignore any internal fields or primary keys with value 0.
 776              if (($k[0] === '_') || ($k == $key && (($v === 0) || ($v === '0'))))
 777              {
 778                  continue;
 779              }
 780  
 781              // Ignore null timestamp fields.
 782              if ($columns[$k] === 'timestamp without time zone' && empty($v))
 783              {
 784                  continue;
 785              }
 786  
 787              // Prepare and sanitize the fields and values for the database query.
 788              $fields[] = $this->quoteName($k);
 789              $values[] = $this->sqlValue($columns, $k, $v);
 790          }
 791  
 792          // Create the base insert statement.
 793          $query = $this->getQuery(true);
 794  
 795          $query->insert($this->quoteName($table))
 796              ->columns($fields)
 797              ->values(implode(',', $values));
 798  
 799          if ($key)
 800          {
 801              $query->returning($key);
 802  
 803              // Set the query and execute the insert.
 804              $object->$key = $this->setQuery($query)->loadResult();
 805          }
 806          else
 807          {
 808              // Set the query and execute the insert.
 809              $this->setQuery($query)->execute();
 810          }
 811  
 812          return true;
 813      }
 814  
 815      /**
 816       * Test to see if the PostgreSQL connector is available.
 817       *
 818       * @return  boolean  True on success, false otherwise.
 819       *
 820       * @since   1.5.0
 821       */
 822  	public static function isSupported()
 823      {
 824          return class_exists('\\PDO') && \in_array('pgsql', \PDO::getAvailableDrivers(), true);
 825      }
 826  
 827      /**
 828       * Returns an array containing database's table list.
 829       *
 830       * @return  array  The database's table list.
 831       *
 832       * @since   1.5.0
 833       */
 834  	public function showTables()
 835      {
 836          $query = $this->getQuery(true)
 837              ->select('table_name')
 838              ->from('information_schema.tables')
 839              ->where('table_type=' . $this->quote('BASE TABLE'))
 840              ->where('table_schema NOT IN (' . $this->quote('pg_catalog') . ', ' . $this->quote('information_schema') . ' )');
 841  
 842          $this->setQuery($query);
 843  
 844          return $this->loadColumn();
 845      }
 846  
 847      /**
 848       * Get the substring position inside a string
 849       *
 850       * @param   string  $substring  The string being sought
 851       * @param   string  $string     The string/column being searched
 852       *
 853       * @return  integer  The position of $substring in $string
 854       *
 855       * @since   1.5.0
 856       */
 857  	public function getStringPositionSql($substring, $string)
 858      {
 859          $this->setQuery("SELECT POSITION($substring IN $string)");
 860          $position = $this->loadRow();
 861  
 862          return $position['position'];
 863      }
 864  
 865      /**
 866       * Generate a random value
 867       *
 868       * @return  float  The random generated number
 869       *
 870       * @since   1.5.0
 871       */
 872  	public function getRandom()
 873      {
 874          $this->setQuery('SELECT RANDOM()');
 875          $random = $this->loadAssoc();
 876  
 877          return $random['random'];
 878      }
 879  
 880      /**
 881       * Get the query string to alter the database character set.
 882       *
 883       * @param   string  $dbName  The database name
 884       *
 885       * @return  string  The query that alter the database query string
 886       *
 887       * @since   1.5.0
 888       */
 889  	public function getAlterDbCharacterSet($dbName)
 890      {
 891          return 'ALTER DATABASE ' . $this->quoteName($dbName) . ' SET CLIENT_ENCODING TO ' . $this->quote('UTF8');
 892      }
 893  
 894      /**
 895       * Get the query string to create new Database in correct PostgreSQL syntax.
 896       *
 897       * @param   object   $options  object coming from "initialise" function to pass user and database name to database driver.
 898       * @param   boolean  $utf      True if the database supports the UTF-8 character set, not used in PostgreSQL "CREATE DATABASE" query.
 899       *
 900       * @return  string    The query that creates database, owned by $options['user']
 901       *
 902       * @since   1.5.0
 903       */
 904  	public function getCreateDbQuery($options, $utf)
 905      {
 906          $query = 'CREATE DATABASE ' . $this->quoteName($options->db_name) . ' OWNER ' . $this->quoteName($options->db_user);
 907  
 908          if ($utf)
 909          {
 910              $query .= ' ENCODING ' . $this->quote('UTF-8');
 911          }
 912  
 913          return $query;
 914      }
 915  
 916      /**
 917       * This function replaces a string identifier with the configured table prefix.
 918       *
 919       * @param   string  $sql     The SQL statement to prepare.
 920       * @param   string  $prefix  The table prefix.
 921       *
 922       * @return  string  The processed SQL statement.
 923       *
 924       * @since   1.5.0
 925       */
 926  	public function replacePrefix($sql, $prefix = '#__')
 927      {
 928          $sql = trim($sql);
 929  
 930          if (strpos($sql, '\''))
 931          {
 932              // Sequence name quoted with ' ' but need to be replaced
 933              if (strpos($sql, 'currval'))
 934              {
 935                  $sql = explode('currval', $sql);
 936  
 937                  for ($nIndex = 1, $nIndexMax = \count($sql); $nIndex < $nIndexMax; $nIndex += 2)
 938                  {
 939                      $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]);
 940                  }
 941  
 942                  $sql = implode('currval', $sql);
 943              }
 944  
 945              // Sequence name quoted with ' ' but need to be replaced
 946              if (strpos($sql, 'nextval'))
 947              {
 948                  $sql = explode('nextval', $sql);
 949  
 950                  for ($nIndex = 1, $nIndexMax = \count($sql); $nIndex < $nIndexMax; $nIndex += 2)
 951                  {
 952                      $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]);
 953                  }
 954  
 955                  $sql = implode('nextval', $sql);
 956              }
 957  
 958              // Sequence name quoted with ' ' but need to be replaced
 959              if (strpos($sql, 'setval'))
 960              {
 961                  $sql = explode('setval', $sql);
 962  
 963                  for ($nIndex = 1, $nIndexMax = \count($sql); $nIndex < $nIndexMax; $nIndex += 2)
 964                  {
 965                      $sql[$nIndex] = str_replace($prefix, $this->tablePrefix, $sql[$nIndex]);
 966                  }
 967  
 968                  $sql = implode('setval', $sql);
 969              }
 970  
 971              $explodedQuery = explode('\'', $sql);
 972  
 973              for ($nIndex = 0, $nIndexMax = \count($explodedQuery); $nIndex < $nIndexMax; $nIndex += 2)
 974              {
 975                  if (strpos($explodedQuery[$nIndex], $prefix))
 976                  {
 977                      $explodedQuery[$nIndex] = str_replace($prefix, $this->tablePrefix, $explodedQuery[$nIndex]);
 978                  }
 979              }
 980  
 981              $replacedQuery = implode('\'', $explodedQuery);
 982          }
 983          else
 984          {
 985              $replacedQuery = str_replace($prefix, $this->tablePrefix, $sql);
 986          }
 987  
 988          return $replacedQuery;
 989      }
 990  
 991      /**
 992       * Unlocks tables in the database, this command does not exist in PostgreSQL, it is automatically done on commit or rollback.
 993       *
 994       * @return  $this
 995       *
 996       * @since   1.5.0
 997       * @throws  \RuntimeException
 998       */
 999  	public function unlockTables()
1000      {
1001          $this->transactionCommit();
1002  
1003          return $this;
1004      }
1005  
1006      /**
1007       * Updates a row in a table based on an object's properties.
1008       *
1009       * @param   string        $table   The name of the database table to update.
1010       * @param   object        $object  A reference to an object whose public properties match the table fields.
1011       * @param   array|string  $key     The name of the primary key.
1012       * @param   boolean       $nulls   True to update null fields or false to ignore them.
1013       *
1014       * @return  boolean
1015       *
1016       * @since   1.5.0
1017       * @throws  \RuntimeException
1018       */
1019  	public function updateObject($table, &$object, $key, $nulls = false)
1020      {
1021          $columns = $this->getTableColumns($table);
1022          $fields  = [];
1023          $where   = [];
1024  
1025          if (\is_string($key))
1026          {
1027              $key = [$key];
1028          }
1029  
1030          if (\is_object($key))
1031          {
1032              $key = (array) $key;
1033          }
1034  
1035          // Create the base update statement.
1036          $statement = 'UPDATE ' . $this->quoteName($table) . ' SET %s WHERE %s';
1037  
1038          // Iterate over the object variables to build the query fields/value pairs.
1039          foreach (get_object_vars($object) as $k => $v)
1040          {
1041              // Skip columns that don't exist in the table.
1042              if (!\array_key_exists($k, $columns))
1043              {
1044                  continue;
1045              }
1046  
1047              // Only process scalars that are not internal fields.
1048              if (\is_array($v) || \is_object($v) || $k[0] === '_')
1049              {
1050                  continue;
1051              }
1052  
1053              // Set the primary key to the WHERE clause instead of a field to update.
1054              if (\in_array($k, $key, true))
1055              {
1056                  $key_val = $this->sqlValue($columns, $k, $v);
1057                  $where[] = $this->quoteName($k) . '=' . $key_val;
1058  
1059                  continue;
1060              }
1061  
1062              // Prepare and sanitize the fields and values for the database query.
1063              if ($v === null)
1064              {
1065                  // If the value is null and we do not want to update nulls then ignore this field.
1066                  if (!$nulls)
1067                  {
1068                      continue;
1069                  }
1070  
1071                  // If the value is null and we want to update nulls then set it.
1072                  $val = 'NULL';
1073              }
1074              else
1075              {
1076                  // The field is not null so we prep it for update.
1077                  $val = $this->sqlValue($columns, $k, $v);
1078              }
1079  
1080              // Add the field to be updated.
1081              $fields[] = $this->quoteName($k) . '=' . $val;
1082          }
1083  
1084          // We don't have any fields to update.
1085          if (empty($fields))
1086          {
1087              return true;
1088          }
1089  
1090          // Set the query and execute the update.
1091          $this->setQuery(sprintf($statement, implode(',', $fields), implode(' AND ', $where)));
1092  
1093          return $this->execute();
1094      }
1095  
1096      /**
1097       * Quotes a binary string to database requirements for use in database queries.
1098       *
1099       * @param   string  $data  A binary string to quote.
1100       *
1101       * @return  string  The binary quoted input string.
1102       *
1103       * @since   1.7.0
1104       */
1105  	public function quoteBinary($data)
1106      {
1107          return "decode('" . bin2hex($data) . "', 'hex')";
1108      }
1109  
1110      /**
1111       * Replace special placeholder representing binary field with the original string.
1112       *
1113       * @param   string|resource  $data  Encoded string or resource.
1114       *
1115       * @return  string  The original string.
1116       *
1117       * @since   1.7.0
1118       */
1119  	public function decodeBinary($data)
1120      {
1121          if (\is_resource($data))
1122          {
1123              return stream_get_contents($data);
1124          }
1125  
1126          return $data;
1127      }
1128  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer