[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/adodb/datadict/ -> datadict-postgres.inc.php (source)

   1  <?php
   2  
   3  /**
   4    V5.19  23-Apr-2014  (c) 2000-2014 John Lim (jlim#natsoft.com). All rights reserved.
   5    Released under both BSD license and Lesser GPL library license.
   6    Whenever there is any discrepancy between the two licenses,
   7    the BSD license will take precedence.
   8  
   9    Set tabs to 4 for best viewing.
  10  
  11  */
  12  
  13  // security - hide paths
  14  if (!defined('ADODB_DIR')) die();
  15  
  16  class ADODB2_postgres extends ADODB_DataDict {
  17  
  18      var $databaseType = 'postgres';
  19      var $seqField = false;
  20      var $seqPrefix = 'SEQ_';
  21      var $addCol = ' ADD COLUMN';
  22      var $quote = '"';
  23      var $renameTable = 'ALTER TABLE %s RENAME TO %s'; // at least since 7.1
  24      var $dropTable = 'DROP TABLE %s CASCADE';
  25  
  26  	function MetaType($t,$len=-1,$fieldobj=false)
  27      {
  28          if (is_object($t)) {
  29              $fieldobj = $t;
  30              $t = $fieldobj->type;
  31              $len = $fieldobj->max_length;
  32          }
  33          $is_serial = is_object($fieldobj) && !empty($fieldobj->primary_key) && !empty($fieldobj->unique) &&
  34              !empty($fieldobj->has_default) && substr($fieldobj->default_value,0,8) == 'nextval(';
  35  
  36          switch (strtoupper($t)) {
  37              case 'INTERVAL':
  38              case 'CHAR':
  39              case 'CHARACTER':
  40              case 'VARCHAR':
  41              case 'NAME':
  42                 case 'BPCHAR':
  43                  if ($len <= $this->blobSize) return 'C';
  44  
  45              case 'TEXT':
  46                  return 'X';
  47  
  48              case 'IMAGE': // user defined type
  49              case 'BLOB': // user defined type
  50              case 'BIT':    // This is a bit string, not a single bit, so don't return 'L'
  51              case 'VARBIT':
  52              case 'BYTEA':
  53                  return 'B';
  54  
  55              case 'BOOL':
  56              case 'BOOLEAN':
  57                  return 'L';
  58  
  59              case 'DATE':
  60                  return 'D';
  61  
  62              case 'TIME':
  63              case 'DATETIME':
  64              case 'TIMESTAMP':
  65              case 'TIMESTAMPTZ':
  66                  return 'T';
  67  
  68              case 'INTEGER': return !$is_serial ? 'I' : 'R';
  69              case 'SMALLINT':
  70              case 'INT2': return !$is_serial ? 'I2' : 'R';
  71              case 'INT4': return !$is_serial ? 'I4' : 'R';
  72              case 'BIGINT':
  73              case 'INT8': return !$is_serial ? 'I8' : 'R';
  74  
  75              case 'OID':
  76              case 'SERIAL':
  77                  return 'R';
  78  
  79              case 'FLOAT4':
  80              case 'FLOAT8':
  81              case 'DOUBLE PRECISION':
  82              case 'REAL':
  83                  return 'F';
  84  
  85               default:
  86                   return 'N';
  87          }
  88      }
  89  
  90   	function ActualType($meta)
  91      {
  92          switch($meta) {
  93          case 'C': return 'VARCHAR';
  94          case 'XL':
  95          case 'X': return 'TEXT';
  96  
  97          case 'C2': return 'VARCHAR';
  98          case 'X2': return 'TEXT';
  99  
 100          case 'B': return 'BYTEA';
 101  
 102          case 'D': return 'DATE';
 103          case 'TS':
 104          case 'T': return 'TIMESTAMP';
 105  
 106          case 'L': return 'BOOLEAN';
 107          case 'I': return 'INTEGER';
 108          case 'I1': return 'SMALLINT';
 109          case 'I2': return 'INT2';
 110          case 'I4': return 'INT4';
 111          case 'I8': return 'INT8';
 112  
 113          case 'F': return 'FLOAT8';
 114          case 'N': return 'NUMERIC';
 115          default:
 116              return $meta;
 117          }
 118      }
 119  
 120      /**
 121       * Adding a new Column
 122       *
 123       * reimplementation of the default function as postgres does NOT allow to set the default in the same statement
 124       *
 125       * @param string $tabname table-name
 126       * @param string $flds column-names and types for the changed columns
 127       * @return array with SQL strings
 128       */
 129  	function AddColumnSQL($tabname, $flds)
 130      {
 131          $tabname = $this->TableName ($tabname);
 132          $sql = array();
 133          $not_null = false;
 134          list($lines,$pkey) = $this->_GenFields($flds);
 135          $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';
 136          foreach($lines as $v) {
 137              if (($not_null = preg_match('/NOT NULL/i',$v))) {
 138                  $v = preg_replace('/NOT NULL/i','',$v);
 139              }
 140              if (preg_match('/^([^ ]+) .*DEFAULT (\'[^\']+\'|\"[^\"]+\"|[^ ]+)/',$v,$matches)) {
 141                  list(,$colname,$default) = $matches;
 142                  $sql[] = $alter . str_replace('DEFAULT '.$default,'',$v);
 143                  $sql[] = 'UPDATE '.$tabname.' SET '.$colname.'='.$default;
 144                  $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET DEFAULT ' . $default;
 145              } else {
 146                  $sql[] = $alter . $v;
 147              }
 148              if ($not_null) {
 149                  list($colname) = explode(' ',$v);
 150                  $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL';
 151              }
 152          }
 153          return $sql;
 154      }
 155  
 156  
 157  	function DropIndexSQL ($idxname, $tabname = NULL)
 158      {
 159         return array(sprintf($this->dropIndex, $this->TableName($idxname), $this->TableName($tabname)));
 160      }
 161  
 162      /**
 163       * Change the definition of one column
 164       *
 165       * Postgres can't do that on it's own, you need to supply the complete defintion of the new table,
 166       * to allow, recreating the table and copying the content over to the new table
 167       * @param string $tabname table-name
 168       * @param string $flds column-name and type for the changed column
 169       * @param string $tableflds complete defintion of the new table, eg. for postgres, default ''
 170       * @param array/ $tableoptions options for the new table see CreateTableSQL, default ''
 171       * @return array with SQL strings
 172       */
 173       /*
 174      function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 175      {
 176          if (!$tableflds) {
 177              if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL");
 178              return array();
 179          }
 180          return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions);
 181      }*/
 182  
 183  	function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 184      {
 185          // Check if alter single column datatype available - works with 8.0+
 186          $has_alter_column = 8.0 <= (float) @$this->serverInfo['version'];
 187  
 188          if ($has_alter_column) {
 189              $tabname = $this->TableName($tabname);
 190              $sql = array();
 191              list($lines,$pkey) = $this->_GenFields($flds);
 192              $set_null = false;
 193              $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
 194              foreach($lines as $v) {
 195                  if ($not_null = preg_match('/NOT NULL/i',$v)) {
 196                      $v = preg_replace('/NOT NULL/i','',$v);
 197                  }
 198                   // this next block doesn't work - there is no way that I can see to
 199                   // explicitly ask a column to be null using $flds
 200                  else if ($set_null = preg_match('/NULL/i',$v)) {
 201                      // if they didn't specify not null, see if they explicitely asked for null
 202                      // Lookbehind pattern covers the case 'fieldname NULL datatype DEFAULT NULL'
 203                      // only the first NULL should be removed, not the one specifying
 204                      // the default value
 205                      $v = preg_replace('/(?<!DEFAULT)\sNULL/i','',$v);
 206                  }
 207  
 208                  if (preg_match('/^([^ ]+) .*DEFAULT (\'[^\']+\'|\"[^\"]+\"|[^ ]+)/',$v,$matches)) {
 209                      $existing = $this->MetaColumns($tabname);
 210                      list(,$colname,$default) = $matches;
 211                      $alter .= $colname;
 212                      if ($this->connection) {
 213                          $old_coltype = $this->connection->MetaType($existing[strtoupper($colname)]);
 214                      }
 215                      else {
 216                          $old_coltype = $t;
 217                      }
 218                      $v = preg_replace('/^' . preg_quote($colname) . '\s/', '', $v);
 219                      $t = trim(str_replace('DEFAULT '.$default,'',$v));
 220  
 221                      // Type change from bool to int
 222                      if ( $old_coltype == 'L' && $t == 'INTEGER' ) {
 223                          $sql[] = $alter . ' DROP DEFAULT';
 224                          $sql[] = $alter . " TYPE $t USING ($colname::BOOL)::INT";
 225                          $sql[] = $alter . " SET DEFAULT $default";
 226                      }
 227                      // Type change from int to bool
 228                      else if ( $old_coltype == 'I' && $t == 'BOOLEAN' ) {
 229                          if( strcasecmp('NULL', trim($default)) != 0 ) {
 230                              $default = $this->connection->qstr($default);
 231                          }
 232                          $sql[] = $alter . ' DROP DEFAULT';
 233                          $sql[] = $alter . " TYPE $t USING CASE WHEN $colname = 0 THEN false ELSE true END";
 234                          $sql[] = $alter . " SET DEFAULT $default";
 235                      }
 236                      // Any other column types conversion
 237                      else {
 238                          $sql[] = $alter . " TYPE $t";
 239                          $sql[] = $alter . " SET DEFAULT $default";
 240                      }
 241  
 242                  }
 243                  else {
 244                      // drop default?
 245                      preg_match ('/^\s*(\S+)\s+(.*)$/',$v,$matches);
 246                      list (,$colname,$rest) = $matches;
 247                      $alter .= $colname;
 248                      $sql[] = $alter . ' TYPE ' . $rest;
 249                  }
 250  
 251  #                list($colname) = explode(' ',$v);
 252                  if ($not_null) {
 253                      // this does not error out if the column is already not null
 254                      $sql[] = $alter . ' SET NOT NULL';
 255                  }
 256                  if ($set_null) {
 257                      // this does not error out if the column is already null
 258                      $sql[] = $alter . ' DROP NOT NULL';
 259                  }
 260              }
 261              return $sql;
 262          }
 263  
 264          // does not have alter column
 265          if (!$tableflds) {
 266              if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL");
 267              return array();
 268          }
 269          return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions);
 270      }
 271  
 272      /**
 273       * Drop one column
 274       *
 275       * Postgres < 7.3 can't do that on it's own, you need to supply the complete defintion of the new table,
 276       * to allow, recreating the table and copying the content over to the new table
 277       * @param string $tabname table-name
 278       * @param string $flds column-name and type for the changed column
 279       * @param string $tableflds complete defintion of the new table, eg. for postgres, default ''
 280       * @param array/ $tableoptions options for the new table see CreateTableSQL, default ''
 281       * @return array with SQL strings
 282       */
 283  	function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 284      {
 285          $has_drop_column = 7.3 <= (float) @$this->serverInfo['version'];
 286          if (!$has_drop_column && !$tableflds) {
 287              if ($this->debug) ADOConnection::outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3");
 288          return array();
 289      }
 290          if ($has_drop_column) {
 291              return ADODB_DataDict::DropColumnSQL($tabname, $flds);
 292          }
 293          return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions);
 294      }
 295  
 296      /**
 297       * Save the content into a temp. table, drop and recreate the original table and copy the content back in
 298       *
 299       * We also take care to set the values of the sequenz and recreate the indexes.
 300       * All this is done in a transaction, to not loose the content of the table, if something went wrong!
 301       * @internal
 302       * @param string $tabname table-name
 303       * @param string $dropflds column-names to drop
 304       * @param string $tableflds complete defintion of the new table, eg. for postgres
 305       * @param array/string $tableoptions options for the new table see CreateTableSQL, default ''
 306       * @return array with SQL strings
 307       */
 308  	function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='')
 309      {
 310          if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds);
 311          $copyflds = array();
 312          foreach($this->MetaColumns($tabname) as $fld) {
 313              if (!$dropflds || !in_array($fld->name,$dropflds)) {
 314                  // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one
 315                  if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) &&
 316                      in_array($fld->type,array('varchar','char','text','bytea'))) {
 317                      $copyflds[] = "to_number($fld->name,'S9999999999999D99')";
 318                  } else {
 319                      $copyflds[] = $fld->name;
 320                  }
 321                  // identify the sequence name and the fld its on
 322                  if ($fld->primary_key && $fld->has_default &&
 323                      preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) {
 324                      $seq_name = $matches[1];
 325                      $seq_fld = $fld->name;
 326                  }
 327              }
 328          }
 329          $copyflds = implode(', ',$copyflds);
 330  
 331          $tempname = $tabname.'_tmp';
 332          $aSql[] = 'BEGIN';        // we use a transaction, to make sure not to loose the content of the table
 333          $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname";
 334          $aSql = array_merge($aSql,$this->DropTableSQL($tabname));
 335          $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions));
 336          $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname";
 337          if ($seq_name && $seq_fld) {    // if we have a sequence we need to set it again
 338              $seq_name = $tabname.'_'.$seq_fld.'_seq';    // has to be the name of the new implicit sequence
 339              $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname";
 340          }
 341          $aSql[] = "DROP TABLE $tempname";
 342          // recreate the indexes, if they not contain one of the droped columns
 343          foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data)
 344          {
 345              if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) {
 346                  $aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'],
 347                      $idx_data['unique'] ? array('UNIQUE') : False));
 348              }
 349          }
 350          $aSql[] = 'COMMIT';
 351          return $aSql;
 352      }
 353  
 354  	function DropTableSQL($tabname)
 355      {
 356          $sql = ADODB_DataDict::DropTableSQL($tabname);
 357  
 358          $drop_seq = $this->_DropAutoIncrement($tabname);
 359          if ($drop_seq) $sql[] = $drop_seq;
 360  
 361          return $sql;
 362      }
 363  
 364      // return string must begin with space
 365  	function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
 366      {
 367          if ($fautoinc) {
 368              $ftype = 'SERIAL';
 369              return '';
 370          }
 371          $suffix = '';
 372          if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
 373          if ($fnotnull) $suffix .= ' NOT NULL';
 374          if ($fconstraint) $suffix .= ' '.$fconstraint;
 375          return $suffix;
 376      }
 377  
 378      // search for a sequece for the given table (asumes the seqence-name contains the table-name!)
 379      // if yes return sql to drop it
 380      // this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!!
 381  	function _DropAutoIncrement($tabname)
 382      {
 383          $tabname = $this->connection->quote('%'.$tabname.'%');
 384  
 385          $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'");
 386  
 387          // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly
 388          if (!$seq || $this->connection->GetOne("SELECT relname FROM pg_class JOIN pg_depend ON pg_class.relfilenode=pg_depend.objid WHERE relname='$seq' AND relkind='S' AND deptype='i'")) {
 389              return False;
 390          }
 391          return "DROP SEQUENCE ".$seq;
 392      }
 393  
 394  	function RenameTableSQL($tabname,$newname)
 395      {
 396          if (!empty($this->schema)) {
 397              $rename_from = $this->TableName($tabname);
 398              $schema_save = $this->schema;
 399              $this->schema = false;
 400              $rename_to = $this->TableName($newname);
 401              $this->schema = $schema_save;
 402              return array (sprintf($this->renameTable, $rename_from, $rename_to));
 403          }
 404  
 405          return array (sprintf($this->renameTable, $this->TableName($tabname),$this->TableName($newname)));
 406      }
 407  
 408      /*
 409      CREATE [ [ LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name (
 410      { column_name data_type [ DEFAULT default_expr ] [ column_constraint [, ... ] ]
 411      | table_constraint } [, ... ]
 412      )
 413      [ INHERITS ( parent_table [, ... ] ) ]
 414      [ WITH OIDS | WITHOUT OIDS ]
 415      where column_constraint is:
 416      [ CONSTRAINT constraint_name ]
 417      { NOT NULL | NULL | UNIQUE | PRIMARY KEY |
 418      CHECK (expression) |
 419      REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL ]
 420      [ ON DELETE action ] [ ON UPDATE action ] }
 421      [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 422      and table_constraint is:
 423      [ CONSTRAINT constraint_name ]
 424      { UNIQUE ( column_name [, ... ] ) |
 425      PRIMARY KEY ( column_name [, ... ] ) |
 426      CHECK ( expression ) |
 427      FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
 428      [ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] }
 429      [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 430      */
 431  
 432  
 433      /*
 434      CREATE [ UNIQUE ] INDEX index_name ON table
 435  [ USING acc_method ] ( column [ ops_name ] [, ...] )
 436  [ WHERE predicate ]
 437  CREATE [ UNIQUE ] INDEX index_name ON table
 438  [ USING acc_method ] ( func_name( column [, ... ]) [ ops_name ] )
 439  [ WHERE predicate ]
 440      */
 441  	function _IndexSQL($idxname, $tabname, $flds, $idxoptions)
 442      {
 443          $sql = array();
 444  
 445          if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) {
 446              $sql[] = sprintf ($this->dropIndex, $idxname, $tabname);
 447              if ( isset($idxoptions['DROP']) )
 448                  return $sql;
 449          }
 450  
 451          if ( empty ($flds) ) {
 452              return $sql;
 453          }
 454  
 455          $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : '';
 456  
 457          $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' ';
 458  
 459          if (isset($idxoptions['HASH']))
 460              $s .= 'USING HASH ';
 461  
 462          if ( isset($idxoptions[$this->upperName]) )
 463              $s .= $idxoptions[$this->upperName];
 464  
 465          if ( is_array($flds) )
 466              $flds = implode(', ',$flds);
 467          $s .= '(' . $flds . ')';
 468          $sql[] = $s;
 469  
 470          return $sql;
 471      }
 472  
 473  	function _GetSize($ftype, $ty, $fsize, $fprec)
 474      {
 475          if (strlen($fsize) && $ty != 'X' && $ty != 'B' && $ty  != 'I' && strpos($ftype,'(') === false) {
 476              $ftype .= "(".$fsize;
 477              if (strlen($fprec)) $ftype .= ",".$fprec;
 478              $ftype .= ')';
 479          }
 480          return $ftype;
 481      }
 482  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1