diff -u -r adodb-new-tmp/datadict/datadict-postgres.inc.php adodb/datadict/datadict-postgres.inc.php --- adodb-new-tmp/datadict/datadict-postgres.inc.php 2006-06-07 23:19:57.000000000 -0700 +++ adodb/datadict/datadict-postgres.inc.php 2006-07-25 12:30:05.000000000 -0700 @@ -39,7 +39,7 @@ case 'CHARACTER': case 'VARCHAR': case 'NAME': - case 'BPCHAR': + case 'BPCHAR': if ($len <= $this->blobSize) return 'C'; case 'TEXT': @@ -144,6 +144,10 @@ $sql[] = $alter . $v; } if ($not_null) { + if (isset($default)) { + list(,$defaultv) = preg_split('/[\t ]+/', $default, 2); + $sql[] = 'UPDATE '.$tabname.' SET '.$colname.' = '.$defaultv.' WHERE '.$colname.' IS NULL'; + } list($colname) = explode(' ',$v); $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL'; } @@ -211,37 +215,44 @@ { if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); $copyflds = array(); + $insertflds = array(); foreach($this->MetaColumns($tabname) as $fld) { - if (!$dropflds || !in_array($fld->name,$dropflds)) { + if ((!$dropflds || !in_array($fld->name,$dropflds)) && isset($tableflds[strtoupper($fld->name)])) { // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one - if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) && + if (((is_array($tableflds) + && in_array($tableflds[strtoupper($fld->name)]['TYPE'], array('I', 'I2', 'I4', 'I8', 'N', 'F'))) + || (!is_array($tableflds) + && preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches))) && in_array($fld->type,array('varchar','char','text','bytea'))) { $copyflds[] = "to_number($fld->name,'S9999999999999D99')"; } else { $copyflds[] = $fld->name; } + $insertflds[] = $fld->name; // identify the sequence name and the fld its on - if ($fld->primary_key && $fld->has_default && - preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) { + if (isset($fld->primary_key) && $fld->primary_key && $fld->has_default && + preg_match("/nextval\('(?:[^']+\.)*([^']+)'::text\)/",$fld->default_value,$matches)) { $seq_name = $matches[1]; $seq_fld = $fld->name; } } } $copyflds = implode(', ',$copyflds); + $insertflds = implode(', ',$insertflds); $tempname = $tabname.'_tmp'; $aSql[] = 'BEGIN'; // we use a transaction, to make sure not to loose the content of the table $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname"; $aSql = array_merge($aSql,$this->DropTableSQL($tabname)); $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions)); - $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname"; - if ($seq_name && $seq_fld) { // if we have a sequence we need to set it again - $seq_name = $tabname.'_'.$seq_fld.'_seq'; // has to be the name of the new implicit sequence + $aSql[] = "INSERT INTO $tabname ($insertflds) SELECT $copyflds FROM $tempname"; + if (isset($seq_name) && $seq_name && $seq_fld) { // if we have a sequence we need to set it again + //$seq_name = $tabname.'_'.$seq_fld.'_seq'; // has to be the name of the new implicit sequence $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname"; } $aSql[] = "DROP TABLE $tempname"; // recreate the indexes, if they not contain one of the droped columns + /* FIXME 2005-08-01 KJ - Temporarily disabled for XML schema upgrades foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) { if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { @@ -249,10 +260,87 @@ $idx_data['unique'] ? array('UNIQUE') : False)); } } + */ $aSql[] = 'COMMIT'; return $aSql; } + /* --- Added by Alec 2005-09-14: + In PostgreSQL <7.3, SERIAL columns can't be used because they + impose UNIQUE constraints on the column. In the best case (when + we want a UNIQUE constraint), this means that the index is + created twice -- once by ADODB, once by PostgreSQL -- and in + the worst case, an unwanted UNIQUE condition is imposed. + + The makeObjectName function was ported from PostgreSQL 7.1's + analyse.c. + --- */ + + function makeObjectName($name1, $name2, $typename) { + $overhead = 0; + + $name1chars = strlen($name1); + if ($name2) { + $name2chars = strlen($name2); + $overhead++; /* allow for separating underscore */ + } + else $name2chars = 0; + + if ($typename) $overhead += strlen($typename) + 1; + + $availchars = 32 - 1 - $overhead; /* --- 32 = default NAMEDATALEN in PostgreSQL --- */ + + /* + * If we must truncate, preferentially truncate the longer name. This + * logic could be expressed without a loop, but it's simple and + * obvious as a loop. + */ + while ($name1chars + $name2chars > $availchars) { + if ($name1chars > $name2chars) $name1chars--; + else $name2chars--; + } + + /* Now construct the string using the chosen lengths */ + $name = substr($name1, 0, $name1chars); + + if ($name2) $name .= '_' . substr($name2, 0, $name2chars); + if ($typename) $name .= '_' . $typename; + + return $name; + } + + function CreateTableSQL($tabname, $flds, $tableoptions=false) { + $sql = ADODB_DataDict::CreateTableSQL($tabname, $flds, $tableoptions); + + if (7.3 > (float) @$this->serverInfo['version']) { + foreach ($flds as $fld) { + $fld = _array_change_key_case($fld); + + $isAutoInc = false; + foreach($fld as $attr => $v) switch ($attr) { + case 'AUTOINCREMENT': + case 'AUTO': + $isAutoInc = true; + break; + case 'NAME': + $fname = $v; + break; + } + + if (isset($fname) && $isAutoInc) { + // This field is an AUTOINCREMENT. Create a sequence + // for it. + $sequenceName = $this->makeObjectName($tabname, $fname, 'seq'); + array_unshift($sql, "CREATE SEQUENCE $sequenceName"); + array_push($sql, "ALTER TABLE $tabname ALTER COLUMN $fname SET DEFAULT nextval('$sequenceName')"); + } + } + } + return $sql; + } + + /* --- End additions by Alec --- */ + function DropTableSQL($tabname) { $sql = ADODB_DataDict::DropTableSQL($tabname); @@ -267,6 +355,19 @@ function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) { if ($fautoinc) { + // Added by Alec 2005-09-14: With PostgreSQL < 7.3, we cannot + // use the SERIAL type because it forces the use of a unique + // index on that column; at best, this causes duplicate indexes + // to be created. At worst, it causes UNIQUE constraints to be + // put on columns that shouldn't have them. + + if (7.3 > (float) @$this->serverInfo['version']) { + $ftype = 'INTEGER'; + return ''; + } + + // --- + $ftype = 'SERIAL'; return ''; } @@ -282,9 +383,18 @@ // this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!! function _DropAutoIncrement($tabname) { - $tabname = $this->connection->quote('%'.$tabname.'%'); + // FIXME This Code + $seq = false; + foreach($this->MetaColumns($tabname) as $fld) { + if (isset($fld->primary_key) && $fld->primary_key && $fld->has_default && + preg_match("/nextval\('[\"]?(?:[^'\"]+\.)*([^'\"]+)[\"]?'::text\)/",$fld->default_value,$matches)) { + $seq = $matches[1]; + } + } + + //$tabname = $this->connection->quote('%'.$tabname.'%'); - $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'"); + //$seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'"); // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly 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'")) { @@ -367,5 +477,25 @@ } return $ftype; } + + // Functions for managing the database character encoding + // (for CREATE DATABASE, CREATE TABLE, etc.) + // Added 2004-06-20 by Kevin Jamieson (http://pkp.sfu.ca/) + // NOTE: If a character set is specified, assumes the database server supports this + function CreateDatabase($dbname,$options=false) + { + $options = $this->_Options($options); + $sql = array(); + + $s = 'CREATE DATABASE ' . $this->NameQuote($dbname); + if (isset($options[$this->upperName])) + $s .= ' '.$options[$this->upperName]; + if ($this->charSet) + $s .= sprintf(' WITH ENCODING \'%s\'', $this->charSet); + if (7.3 <= (float) @$this->serverInfo['version']) + $s .= ' TEMPLATE template0'; // Deal with "template1 is being accessed by other users" errors (FIXME?) + $sql[] = $s; + return $sql; + } }