You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							359 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							359 lines
						
					
					
						
							12 KiB
						
					
					
				| <?php | |
| 
 | |
| /** | |
|   V4.55 3 Jan 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved. | |
|   Released under both BSD license and Lesser GPL library license.  | |
|   Whenever there is any discrepancy between the two licenses,  | |
|   the BSD license will take precedence. | |
| 	 | |
|   Set tabs to 4 for best viewing. | |
|   | |
| */ | |
| 
 | |
| // security - hide paths | |
| if (!defined('ADODB_DIR')) die(); | |
| 
 | |
| class ADODB2_postgres extends ADODB_DataDict { | |
| 	 | |
| 	var $databaseType = 'postgres'; | |
| 	var $seqField = false; | |
| 	var $seqPrefix = 'SEQ_'; | |
| 	var $addCol = ' ADD COLUMN'; | |
| 	var $quote = '"'; | |
| 	var $renameTable = 'ALTER TABLE %s RENAME TO %s'; // at least since 7.1 | |
| 	 | |
| 	function MetaType($t,$len=-1,$fieldobj=false) | |
| 	{ | |
| 		if (is_object($t)) { | |
| 			$fieldobj = $t; | |
| 			$t = $fieldobj->type; | |
| 			$len = $fieldobj->max_length; | |
| 		} | |
| 		$is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->unique &&  | |
| 			$fieldobj->has_default && substr($fieldobj->default_value,0,8) == 'nextval('; | |
| 		 | |
| 		switch (strtoupper($t)) { | |
| 			case 'INTERVAL': | |
| 			case 'CHAR': | |
| 			case 'CHARACTER': | |
| 			case 'VARCHAR': | |
| 			case 'NAME': | |
| 	   		case 'BPCHAR': | |
| 				if ($len <= $this->blobSize) return 'C'; | |
| 			 | |
| 			case 'TEXT': | |
| 				return 'X'; | |
| 	 | |
| 			case 'IMAGE': // user defined type | |
| 			case 'BLOB': // user defined type | |
| 			case 'BIT':	// This is a bit string, not a single bit, so don't return 'L' | |
| 			case 'VARBIT': | |
| 			case 'BYTEA': | |
| 				return 'B'; | |
| 			 | |
| 			case 'BOOL': | |
| 			case 'BOOLEAN': | |
| 				return 'L'; | |
| 			 | |
| 			case 'DATE': | |
| 				return 'D'; | |
| 			 | |
| 			case 'TIME': | |
| 			case 'DATETIME': | |
| 			case 'TIMESTAMP': | |
| 			case 'TIMESTAMPTZ': | |
| 				return 'T'; | |
| 			 | |
| 			case 'INTEGER': return !$is_serial ? 'I' : 'R'; | |
| 			case 'SMALLINT':  | |
| 			case 'INT2': return !$is_serial ? 'I2' : 'R'; | |
| 			case 'INT4': return !$is_serial ? 'I4' : 'R'; | |
| 			case 'BIGINT':  | |
| 			case 'INT8': return !$is_serial ? 'I8' : 'R'; | |
| 				 | |
| 			case 'OID': | |
| 			case 'SERIAL': | |
| 				return 'R'; | |
| 			 | |
| 			case 'FLOAT4': | |
| 			case 'FLOAT8': | |
| 			case 'DOUBLE PRECISION': | |
| 			case 'REAL': | |
| 				return 'F'; | |
| 				 | |
| 			 default: | |
| 			 	return 'N'; | |
| 		} | |
| 	} | |
|  	 | |
|  	function ActualType($meta) | |
| 	{ | |
| 		switch($meta) { | |
| 		case 'C': return 'VARCHAR'; | |
| 		case 'XL': | |
| 		case 'X': return 'TEXT'; | |
| 		 | |
| 		case 'C2': return 'VARCHAR'; | |
| 		case 'X2': return 'TEXT'; | |
| 		 | |
| 		case 'B': return 'BYTEA'; | |
| 			 | |
| 		case 'D': return 'DATE'; | |
| 		case 'T': return 'TIMESTAMP'; | |
| 		 | |
| 		case 'L': return 'SMALLINT'; | |
| 		case 'I': return 'INTEGER'; | |
| 		case 'I1': return 'SMALLINT'; | |
| 		case 'I2': return 'INT2'; | |
| 		case 'I4': return 'INT4'; | |
| 		case 'I8': return 'INT8'; | |
| 		 | |
| 		case 'F': return 'FLOAT8'; | |
| 		case 'N': return 'NUMERIC'; | |
| 		default: | |
| 			return $meta; | |
| 		} | |
| 	} | |
| 	 | |
| 	/** | |
| 	 * Adding a new Column  | |
| 	 * | |
| 	 * reimplementation of the default function as postgres does NOT allow to set the default in the same statement | |
| 	 * | |
| 	 * @param string $tabname table-name | |
| 	 * @param string $flds column-names and types for the changed columns | |
| 	 * @return array with SQL strings | |
| 	 */ | |
| 	function AddColumnSQL($tabname, $flds) | |
| 	{ | |
| 		$tabname = $this->TableName ($tabname); | |
| 		$sql = array(); | |
| 		list($lines,$pkey) = $this->_GenFields($flds); | |
| 		$alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' '; | |
| 		foreach($lines as $v) { | |
| 			if (($not_null = preg_match('/NOT NULL/i',$v))) { | |
| 				$v = preg_replace('/NOT NULL/i','',$v); | |
| 			} | |
| 			if (preg_match('/^([^ ]+) .*(DEFAULT [^ ]+)/',$v,$matches)) { | |
| 				list(,$colname,$default) = $matches; | |
| 				$sql[] = $alter . str_replace($default,'',$v); | |
| 				$sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET ' . $default; | |
| 			} else {				 | |
| 				$sql[] = $alter . $v; | |
| 			} | |
| 			if ($not_null) { | |
| 				list($colname) = explode(' ',$v); | |
| 				$sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL'; | |
| 			} | |
| 		} | |
| 		return $sql; | |
| 	} | |
| 	 | |
| 	/** | |
| 	 * Change the definition of one column | |
| 	 * | |
| 	 * Postgres can't do that on it's own, you need to supply the complete defintion of the new table, | |
| 	 * to allow, recreating the table and copying the content over to the new table | |
| 	 * @param string $tabname table-name | |
| 	 * @param string $flds column-name and type for the changed column | |
| 	 * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' | |
| 	 * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' | |
| 	 * @return array with SQL strings | |
| 	 */ | |
| 	function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') | |
| 	{ | |
| 		if (!$tableflds) { | |
| 			if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL"); | |
| 			return array(); | |
| 		} | |
| 		return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions); | |
| 	} | |
| 	 | |
| 	/** | |
| 	 * Drop one column | |
| 	 * | |
| 	 * Postgres < 7.3 can't do that on it's own, you need to supply the complete defintion of the new table, | |
| 	 * to allow, recreating the table and copying the content over to the new table | |
| 	 * @param string $tabname table-name | |
| 	 * @param string $flds column-name and type for the changed column | |
| 	 * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' | |
| 	 * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' | |
| 	 * @return array with SQL strings | |
| 	 */ | |
| 	function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') | |
| 	{ | |
| 		$has_drop_column = 7.3 <= (float) @$this->serverInfo['version']; | |
| 		if (!$has_drop_column && !$tableflds) { | |
| 			if ($this->debug) ADOConnection::outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3"); | |
| 		return array(); | |
| 	} | |
| 		if ($has_drop_column) { | |
| 			return ADODB_DataDict::DropColumnSQL($tabname, $flds); | |
| 		} | |
| 		return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions); | |
| 	} | |
| 	 | |
| 	/** | |
| 	 * Save the content into a temp. table, drop and recreate the original table and copy the content back in | |
| 	 * | |
| 	 * We also take care to set the values of the sequenz and recreate the indexes. | |
| 	 * All this is done in a transaction, to not loose the content of the table, if something went wrong! | |
| 	 * @internal | |
| 	 * @param string $tabname table-name | |
| 	 * @param string $dropflds column-names to drop | |
| 	 * @param string $tableflds complete defintion of the new table, eg. for postgres | |
| 	 * @param array/string $tableoptions options for the new table see CreateTableSQL, default '' | |
| 	 * @return array with SQL strings | |
| 	 */ | |
| 	function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='') | |
| 	{ | |
| 		if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); | |
| 		$copyflds = array(); | |
| 		foreach($this->MetaColumns($tabname) as $fld) { | |
| 			if (!$dropflds || !in_array($fld->name,$dropflds)) { | |
| 				// 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) &&  | |
| 					in_array($fld->type,array('varchar','char','text','bytea'))) { | |
| 					$copyflds[] = "to_number($fld->name,'S99D99')"; | |
| 				} else { | |
| 					$copyflds[] = $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)) { | |
| 					$seq_name = $matches[1]; | |
| 					$seq_fld = $fld->name; | |
| 				} | |
| 			} | |
| 		} | |
| 		$copyflds = implode(', ',$copyflds); | |
| 		 | |
| 		$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[] = "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 | |
| 		foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) | |
| 		{ | |
| 			if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { | |
| 				$aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'], | |
| 					$idx_data['unique'] ? array('UNIQUE') : False)); | |
| 			} | |
| 		} | |
| 		$aSql[] = 'COMMIT'; | |
| 		return $aSql; | |
| 	} | |
| 	 | |
| 	function DropTableSQL($tabname) | |
| 	{ | |
| 		$sql = ADODB_DataDict::DropTableSQL($tabname); | |
| 		 | |
| 		$drop_seq = $this->_DropAutoIncrement($tabname); | |
| 		if ($drop_seq) $sql[] = $drop_seq; | |
| 		 | |
| 		return $sql; | |
| 	} | |
| 
 | |
| 	// return string must begin with space | |
| 	function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) | |
| 	{ | |
| 		if ($fautoinc) { | |
| 			$ftype = 'SERIAL'; | |
| 			return ''; | |
| 		} | |
| 		$suffix = ''; | |
| 		if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault"; | |
| 		if ($fnotnull) $suffix .= ' NOT NULL'; | |
| 		if ($fconstraint) $suffix .= ' '.$fconstraint; | |
| 		return $suffix; | |
| 	} | |
| 	 | |
| 	// search for a sequece for the given table (asumes the seqence-name contains the table-name!) | |
| 	// if yes return sql to drop it | |
| 	// 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.'%'); | |
| 
 | |
| 		$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'")) { | |
| 			return False; | |
| 		} | |
| 		return "DROP SEQUENCE ".$seq; | |
| 	} | |
| 	 | |
| 	/* | |
| 	CREATE [ [ LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( | |
| 	{ column_name data_type [ DEFAULT default_expr ] [ column_constraint [, ... ] ] | |
| 	| table_constraint } [, ... ] | |
| 	) | |
| 	[ INHERITS ( parent_table [, ... ] ) ] | |
| 	[ WITH OIDS | WITHOUT OIDS ] | |
| 	where column_constraint is: | |
| 	[ CONSTRAINT constraint_name ] | |
| 	{ NOT NULL | NULL | UNIQUE | PRIMARY KEY | | |
| 	CHECK (expression) | | |
| 	REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL ] | |
| 	[ ON DELETE action ] [ ON UPDATE action ] } | |
| 	[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] | |
| 	and table_constraint is: | |
| 	[ CONSTRAINT constraint_name ] | |
| 	{ UNIQUE ( column_name [, ... ] ) | | |
| 	PRIMARY KEY ( column_name [, ... ] ) | | |
| 	CHECK ( expression ) | | |
| 	FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ] | |
| 	[ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] } | |
| 	[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] | |
| 	*/ | |
| 	 | |
| 	 | |
| 	/* | |
| 	CREATE [ UNIQUE ] INDEX index_name ON table | |
| [ USING acc_method ] ( column [ ops_name ] [, ...] ) | |
| [ WHERE predicate ] | |
| CREATE [ UNIQUE ] INDEX index_name ON table | |
| [ USING acc_method ] ( func_name( column [, ... ]) [ ops_name ] ) | |
| [ WHERE predicate ] | |
| 	*/ | |
| 	function _IndexSQL($idxname, $tabname, $flds, $idxoptions) | |
| 	{ | |
| 		$sql = array(); | |
| 		 | |
| 		if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) { | |
| 			$sql[] = sprintf ($this->dropIndex, $idxname, $tabname); | |
| 			if ( isset($idxoptions['DROP']) ) | |
| 				return $sql; | |
| 		} | |
| 		 | |
| 		if ( empty ($flds) ) { | |
| 			return $sql; | |
| 		} | |
| 		 | |
| 		$unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : ''; | |
| 		 | |
| 		$s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' '; | |
| 		 | |
| 		if (isset($idxoptions['HASH'])) | |
| 			$s .= 'USING HASH '; | |
| 		 | |
| 		if ( isset($idxoptions[$this->upperName]) ) | |
| 			$s .= $idxoptions[$this->upperName]; | |
| 		 | |
| 		if ( is_array($flds) ) | |
| 			$flds = implode(', ',$flds); | |
| 		$s .= '(' . $flds . ')'; | |
| 		$sql[] = $s; | |
| 		 | |
| 		return $sql; | |
| 	} | |
| } | |
| ?>
 |