Skip to content
Snippets Groups Projects
adodb-datadict.inc.php 27.5 KiB
Newer Older
Prasad's avatar
Prasad committed
<?php

/**
Satish's avatar
Satish committed
  @version   v5.20.9  21-Dec-2016
  @copyright (c) 2000-2013 John Lim (jlim#natsoft.com). All rights reserved.
  @copyright (c) 2014      Damien Regad, Mark Newnham and the ADOdb community
  Released under both BSD license and Lesser GPL library license.
  Whenever there is any discrepancy between the two licenses,
Prasad's avatar
Prasad committed
  the BSD license will take precedence.
Prasad's avatar
Prasad committed
  Set tabs to 4 for best viewing.
Prasad's avatar
Prasad committed
 	DOCUMENTATION:
Prasad's avatar
Prasad committed
		See adodb/tests/test-datadict.php for docs and examples.
*/

/*
	Test script for parser
*/

// security - hide paths
if (!defined('ADODB_DIR')) die();

function Lens_ParseTest()
{
$str = "`zcol ACOL` NUMBER(32,2) DEFAULT 'The \"cow\" (and Jim''s dog) jumps over the moon' PRIMARY, INTI INT AUTO DEFAULT 0, zcol2\"afs ds";
print "<p>$str</p>";
$a= Lens_ParseArgs($str);
print "<pre>";
print_r($a);
print "</pre>";
}


if (!function_exists('ctype_alnum')) {
	function ctype_alnum($text) {
		return preg_match('/^[a-z0-9]*$/i', $text);
	}
}

//Lens_ParseTest();

/**
	Parse arguments, treat "text" (text) and 'text' as quotation marks.
	To escape, use "" or '' or ))
Prasad's avatar
Prasad committed
	Will read in "abc def" sans quotes, as: abc def
	Same with 'abc def'.
	However if `abc def`, then will read in as `abc def`
Prasad's avatar
Prasad committed
	@param endstmtchar    Character that indicates end of statement
Satish's avatar
Satish committed
	@param tokenchars     Include the following characters in tokens apart from A-Z and 0-9
Prasad's avatar
Prasad committed
	@returns 2 dimensional array containing parsed tokens.
*/
function Lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-')
{
	$pos = 0;
	$intoken = false;
	$stmtno = 0;
	$endquote = false;
	$tokens = array();
	$tokens[$stmtno] = array();
	$max = strlen($args);
	$quoted = false;
	$tokarr = array();
Prasad's avatar
Prasad committed
	while ($pos < $max) {
		$ch = substr($args,$pos,1);
		switch($ch) {
		case ' ':
		case "\t":
		case "\n":
		case "\r":
			if (!$quoted) {
				if ($intoken) {
					$intoken = false;
					$tokens[$stmtno][] = implode('',$tokarr);
				}
				break;
			}
Prasad's avatar
Prasad committed
			$tokarr[] = $ch;
			break;
Prasad's avatar
Prasad committed
		case '`':
			if ($intoken) $tokarr[] = $ch;
		case '(':
Satish's avatar
Satish committed
		case ')':
Prasad's avatar
Prasad committed
		case '"':
		case "'":
Prasad's avatar
Prasad committed
			if ($intoken) {
				if (empty($endquote)) {
					$tokens[$stmtno][] = implode('',$tokarr);
					if ($ch == '(') $endquote = ')';
					else $endquote = $ch;
					$quoted = true;
					$intoken = true;
					$tokarr = array();
				} else if ($endquote == $ch) {
					$ch2 = substr($args,$pos+1,1);
					if ($ch2 == $endquote) {
						$pos += 1;
						$tokarr[] = $ch2;
					} else {
						$quoted = false;
						$intoken = false;
						$tokens[$stmtno][] = implode('',$tokarr);
						$endquote = '';
					}
				} else
					$tokarr[] = $ch;
Prasad's avatar
Prasad committed
			}else {
Prasad's avatar
Prasad committed
				if ($ch == '(') $endquote = ')';
				else $endquote = $ch;
				$quoted = true;
				$intoken = true;
				$tokarr = array();
				if ($ch == '`') $tokarr[] = '`';
			}
			break;
Prasad's avatar
Prasad committed
		default:
Prasad's avatar
Prasad committed
			if (!$intoken) {
				if ($ch == $endstmtchar) {
					$stmtno += 1;
					$tokens[$stmtno] = array();
					break;
				}
Prasad's avatar
Prasad committed
				$intoken = true;
				$quoted = false;
				$endquote = false;
				$tokarr = array();
Prasad's avatar
Prasad committed
			}
Prasad's avatar
Prasad committed
			if ($quoted) $tokarr[] = $ch;
			else if (ctype_alnum($ch) || strpos($tokenchars,$ch) !== false) $tokarr[] = $ch;
			else {
Satish's avatar
Satish committed
				if ($ch == $endstmtchar) {
Prasad's avatar
Prasad committed
					$tokens[$stmtno][] = implode('',$tokarr);
					$stmtno += 1;
					$tokens[$stmtno] = array();
					$intoken = false;
					$tokarr = array();
					break;
				}
				$tokens[$stmtno][] = implode('',$tokarr);
				$tokens[$stmtno][] = $ch;
				$intoken = false;
			}
		}
		$pos += 1;
	}
	if ($intoken) $tokens[$stmtno][] = implode('',$tokarr);
Prasad's avatar
Prasad committed
	return $tokens;
}


class ADODB_DataDict {
	var $connection;
	var $debug = false;
	var $dropTable = 'DROP TABLE %s';
Satish's avatar
Satish committed
	var $renameTable = 'RENAME TABLE %s TO %s';
Prasad's avatar
Prasad committed
	var $dropIndex = 'DROP INDEX %s';
	var $addCol = ' ADD';
	var $alterCol = ' ALTER COLUMN';
	var $dropCol = ' DROP COLUMN';
	var $renameColumn = 'ALTER TABLE %s RENAME COLUMN %s TO %s';	// table, old-column, new-column, column-definitions (not used by default)
	var $nameRegex = '\w';
	var $nameRegexBrackets = 'a-zA-Z0-9_\(\)';
	var $schema = false;
	var $serverInfo = array();
	var $autoIncrement = false;
	var $dataProvider;
	var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changetablesql
	var $blobSize = 100; 	/// any varchar/char field this size or greater is treated as a blob
							/// in other words, we use a text area for editting.
Prasad's avatar
Prasad committed
	function GetCommentSQL($table,$col)
	{
		return false;
	}
Prasad's avatar
Prasad committed
	function SetCommentSQL($table,$col,$cmt)
	{
		return false;
	}
Prasad's avatar
Prasad committed
	function MetaTables()
	{
		if (!$this->connection->IsConnected()) return array();
		return $this->connection->MetaTables();
	}
Prasad's avatar
Prasad committed
	function MetaColumns($tab, $upper=true, $schema=false)
	{
		if (!$this->connection->IsConnected()) return array();
		return $this->connection->MetaColumns($this->TableName($tab), $upper, $schema);
	}
Prasad's avatar
Prasad committed
	function MetaPrimaryKeys($tab,$owner=false,$intkey=false)
	{
		if (!$this->connection->IsConnected()) return array();
		return $this->connection->MetaPrimaryKeys($this->TableName($tab), $owner, $intkey);
	}
Prasad's avatar
Prasad committed
	function MetaIndexes($table, $primary = false, $owner = false)
	{
		if (!$this->connection->IsConnected()) return array();
		return $this->connection->MetaIndexes($this->TableName($table), $primary, $owner);
	}
Prasad's avatar
Prasad committed
	function MetaType($t,$len=-1,$fieldobj=false)
	{
Satish's avatar
Satish committed
		static $typeMap = array(
		'VARCHAR' => 'C',
		'VARCHAR2' => 'C',
		'CHAR' => 'C',
		'C' => 'C',
		'STRING' => 'C',
		'NCHAR' => 'C',
		'NVARCHAR' => 'C',
		'VARYING' => 'C',
		'BPCHAR' => 'C',
		'CHARACTER' => 'C',
		'INTERVAL' => 'C',  # Postgres
		'MACADDR' => 'C', # postgres
		'VAR_STRING' => 'C', # mysql
		##
		'LONGCHAR' => 'X',
		'TEXT' => 'X',
		'NTEXT' => 'X',
		'M' => 'X',
		'X' => 'X',
		'CLOB' => 'X',
		'NCLOB' => 'X',
		'LVARCHAR' => 'X',
		##
		'BLOB' => 'B',
		'IMAGE' => 'B',
		'BINARY' => 'B',
		'VARBINARY' => 'B',
		'LONGBINARY' => 'B',
		'B' => 'B',
		##
		'YEAR' => 'D', // mysql
		'DATE' => 'D',
		'D' => 'D',
		##
		'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
		##
		'TIME' => 'T',
		'TIMESTAMP' => 'T',
		'DATETIME' => 'T',
		'TIMESTAMPTZ' => 'T',
		'SMALLDATETIME' => 'T',
		'T' => 'T',
		'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
		##
		'BOOL' => 'L',
		'BOOLEAN' => 'L',
		'BIT' => 'L',
		'L' => 'L',
		##
		'COUNTER' => 'R',
		'R' => 'R',
		'SERIAL' => 'R', // ifx
		'INT IDENTITY' => 'R',
		##
		'INT' => 'I',
		'INT2' => 'I',
		'INT4' => 'I',
		'INT8' => 'I',
		'INTEGER' => 'I',
		'INTEGER UNSIGNED' => 'I',
		'SHORT' => 'I',
		'TINYINT' => 'I',
		'SMALLINT' => 'I',
		'I' => 'I',
		##
		'LONG' => 'N', // interbase is numeric, oci8 is blob
		'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
		'DECIMAL' => 'N',
		'DEC' => 'N',
		'REAL' => 'N',
		'DOUBLE' => 'N',
		'DOUBLE PRECISION' => 'N',
		'SMALLFLOAT' => 'N',
		'FLOAT' => 'N',
		'NUMBER' => 'N',
		'NUM' => 'N',
		'NUMERIC' => 'N',
		'MONEY' => 'N',

		## informix 9.2
		'SQLINT' => 'I',
		'SQLSERIAL' => 'I',
		'SQLSMINT' => 'I',
		'SQLSMFLOAT' => 'N',
		'SQLFLOAT' => 'N',
		'SQLMONEY' => 'N',
		'SQLDECIMAL' => 'N',
		'SQLDATE' => 'D',
		'SQLVCHAR' => 'C',
		'SQLCHAR' => 'C',
		'SQLDTIME' => 'T',
		'SQLINTERVAL' => 'N',
		'SQLBYTES' => 'B',
		'SQLTEXT' => 'X',
		 ## informix 10
		"SQLINT8" => 'I8',
		"SQLSERIAL8" => 'I8',
		"SQLNCHAR" => 'C',
		"SQLNVCHAR" => 'C',
		"SQLLVARCHAR" => 'X',
		"SQLBOOL" => 'L'
		);

		if (!$this->connection->IsConnected()) {
			$t = strtoupper($t);
			if (isset($typeMap[$t])) return $typeMap[$t];
			return 'N';
		}
		return $this->connection->MetaType($t,$len,$fieldobj);
Prasad's avatar
Prasad committed
	}
Prasad's avatar
Prasad committed
	function NameQuote($name = NULL,$allowBrackets=false)
	{
		if (!is_string($name)) {
			return FALSE;
		}
Prasad's avatar
Prasad committed
		$name = trim($name);
Prasad's avatar
Prasad committed
		if ( !is_object($this->connection) ) {
			return $name;
		}
Prasad's avatar
Prasad committed
		$quote = $this->connection->nameQuote;
Prasad's avatar
Prasad committed
		// if name is of the form `name`, quote it
		if ( preg_match('/^`(.+)`$/', $name, $matches) ) {
			return $quote . $matches[1] . $quote;
		}
Prasad's avatar
Prasad committed
		// if name contains special characters, quote it
		$regex = ($allowBrackets) ? $this->nameRegexBrackets : $this->nameRegex;
Prasad's avatar
Prasad committed
		if ( !preg_match('/^[' . $regex . ']+$/', $name) ) {
			return $quote . $name . $quote;
		}
Prasad's avatar
Prasad committed
		return $name;
	}
Prasad's avatar
Prasad committed
	function TableName($name)
	{
		if ( $this->schema ) {
			return $this->NameQuote($this->schema) .'.'. $this->NameQuote($name);
		}
		return $this->NameQuote($name);
	}
Prasad's avatar
Prasad committed
	// Executes the sql array returned by GetTableSQL and GetIndexSQL
	function ExecuteSQLArray($sql, $continueOnError = true)
	{
		$rez = 2;
Satish's avatar
Satish committed
		$conn = $this->connection;
Prasad's avatar
Prasad committed
		$saved = $conn->debug;
		foreach($sql as $line) {
Prasad's avatar
Prasad committed
			if ($this->debug) $conn->debug = true;
			$ok = $conn->Execute($line);
			$conn->debug = $saved;
			if (!$ok) {
Satish's avatar
Satish committed
				if ($this->debug) ADOConnection::outp($conn->ErrorMsg());
Prasad's avatar
Prasad committed
				if (!$continueOnError) return 0;
				$rez = 1;
			}
		}
		return $rez;
	}
Prasad's avatar
Prasad committed
	 	Returns the actual type given a character code.
Prasad's avatar
Prasad committed
		C:  varchar
		X:  CLOB (character large object) or largest varchar size if CLOB is not supported
		C2: Multibyte varchar
		X2: Multibyte CLOB
Prasad's avatar
Prasad committed
		B:  BLOB (binary large object)
Prasad's avatar
Prasad committed
		D:  Date
Satish's avatar
Satish committed
		T:  Date-time
Prasad's avatar
Prasad committed
		L:  Integer field suitable for storing booleans (0 or 1)
		I:  Integer
		F:  Floating point number
		N:  Numeric or decimal number
	*/
Prasad's avatar
Prasad committed
	function ActualType($meta)
	{
		return $meta;
	}
Prasad's avatar
Prasad committed
	function CreateDatabase($dbname,$options=false)
	{
		$options = $this->_Options($options);
		$sql = array();
Prasad's avatar
Prasad committed
		$s = 'CREATE DATABASE ' . $this->NameQuote($dbname);
		if (isset($options[$this->upperName]))
			$s .= ' '.$options[$this->upperName];
Prasad's avatar
Prasad committed
		$sql[] = $s;
		return $sql;
	}
Prasad's avatar
Prasad committed
	/*
	 Generates the SQL to create index. Returns an array of sql strings.
	*/
	function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false)
	{
		if (!is_array($flds)) {
			$flds = explode(',',$flds);
		}
Prasad's avatar
Prasad committed
		foreach($flds as $key => $fld) {
			# some indexes can use partial fields, eg. index first 32 chars of "name" with NAME(32)
			$flds[$key] = $this->NameQuote($fld,$allowBrackets=true);
		}
Prasad's avatar
Prasad committed
		return $this->_IndexSQL($this->NameQuote($idxname), $this->TableName($tabname), $flds, $this->_Options($idxoptions));
	}
Prasad's avatar
Prasad committed
	function DropIndexSQL ($idxname, $tabname = NULL)
	{
		return array(sprintf($this->dropIndex, $this->NameQuote($idxname), $this->TableName($tabname)));
	}
Prasad's avatar
Prasad committed
	function SetSchema($schema)
	{
		$this->schema = $schema;
	}
Prasad's avatar
Prasad committed
	function AddColumnSQL($tabname, $flds)
	{
		$tabname = $this->TableName ($tabname);
		$sql = array();
Satish's avatar
Satish committed
		list($lines,$pkey,$idxs) = $this->_GenFields($flds);
		// genfields can return FALSE at times
		if ($lines  == null) $lines = array();
Prasad's avatar
Prasad committed
		$alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';
		foreach($lines as $v) {
			$sql[] = $alter . $v;
		}
Satish's avatar
Satish committed
		if (is_array($idxs)) {
			foreach($idxs as $idx => $idxdef) {
				$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
				$sql = array_merge($sql, $sql_idxs);
			}
		}
Prasad's avatar
Prasad committed
		return $sql;
	}
Prasad's avatar
Prasad committed
	/**
	 * Change the definition of one column
	 *
	 * As some DBM's can't do that on there 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/string $tableoptions='' options for the new table see CreateTableSQL, default ''
	 * @return array with SQL strings
	 */
	function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
	{
		$tabname = $this->TableName ($tabname);
		$sql = array();
Satish's avatar
Satish committed
		list($lines,$pkey,$idxs) = $this->_GenFields($flds);
		// genfields can return FALSE at times
		if ($lines == null) $lines = array();
Prasad's avatar
Prasad committed
		$alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
		foreach($lines as $v) {
			$sql[] = $alter . $v;
		}
Satish's avatar
Satish committed
		if (is_array($idxs)) {
			foreach($idxs as $idx => $idxdef) {
				$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
				$sql = array_merge($sql, $sql_idxs);
			}

		}
Prasad's avatar
Prasad committed
		return $sql;
	}
Prasad's avatar
Prasad committed
	/**
	 * Rename one column
	 *
	 * Some DBM's can only do this together with changeing the type of the column (even if that stays the same, eg. mysql)
	 * @param string $tabname table-name
	 * @param string $oldcolumn column-name to be renamed
	 * @param string $newcolumn new column-name
	 * @param string $flds='' complete column-defintion-string like for AddColumnSQL, only used by mysql atm., default=''
	 * @return array with SQL strings
	 */
	function RenameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='')
	{
		$tabname = $this->TableName ($tabname);
		if ($flds) {
Satish's avatar
Satish committed
			list($lines,$pkey,$idxs) = $this->_GenFields($flds);
			// genfields can return FALSE at times
			if ($lines == null) $lines = array();
Prasad's avatar
Prasad committed
			list(,$first) = each($lines);
Satish's avatar
Satish committed
			list(,$column_def) = preg_split("/[\t ]+/",$first,2);
Prasad's avatar
Prasad committed
		}
		return array(sprintf($this->renameColumn,$tabname,$this->NameQuote($oldcolumn),$this->NameQuote($newcolumn),$column_def));
	}
Prasad's avatar
Prasad committed
	/**
	 * Drop one column
	 *
	 * Some DBM's can't do that on there 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/string $tableoptions='' options for the new table see CreateTableSQL, default ''
	 * @return array with SQL strings
	 */
	function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
	{
		$tabname = $this->TableName ($tabname);
		if (!is_array($flds)) $flds = explode(',',$flds);
		$sql = array();
		$alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' ';
		foreach($flds as $v) {
			$sql[] = $alter . $this->NameQuote($v);
		}
		return $sql;
	}
Prasad's avatar
Prasad committed
	function DropTableSQL($tabname)
	{
		return array (sprintf($this->dropTable, $this->TableName($tabname)));
	}
Prasad's avatar
Prasad committed
	function RenameTableSQL($tabname,$newname)
	{
		return array (sprintf($this->renameTable, $this->TableName($tabname),$this->TableName($newname)));
Prasad's avatar
Prasad committed
	 Generate the SQL to create table. Returns an array of sql strings.
	*/
Satish's avatar
Satish committed
	function CreateTableSQL($tabname, $flds, $tableoptions=array())
Prasad's avatar
Prasad committed
	{
Satish's avatar
Satish committed
		list($lines,$pkey,$idxs) = $this->_GenFields($flds, true);
		// genfields can return FALSE at times
		if ($lines == null) $lines = array();

Prasad's avatar
Prasad committed
		$taboptions = $this->_Options($tableoptions);
		$tabname = $this->TableName ($tabname);
		$sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions);
Satish's avatar
Satish committed

		// ggiunta - 2006/10/12 - KLUDGE:
        // if we are on autoincrement, and table options includes REPLACE, the
        // autoincrement sequence has already been dropped on table creation sql, so
        // we avoid passing REPLACE to trigger creation code. This prevents
        // creating sql that double-drops the sequence
        if ($this->autoIncrement && isset($taboptions['REPLACE']))
        	unset($taboptions['REPLACE']);
Prasad's avatar
Prasad committed
		$tsql = $this->_Triggers($tabname,$taboptions);
		foreach($tsql as $s) $sql[] = $s;
Satish's avatar
Satish committed

		if (is_array($idxs)) {
			foreach($idxs as $idx => $idxdef) {
				$sql_idxs = $this->CreateIndexSql($idx, $tabname,  $idxdef['cols'], $idxdef['opts']);
				$sql = array_merge($sql, $sql_idxs);
			}
		}

Prasad's avatar
Prasad committed
		return $sql;
	}
Prasad's avatar
Prasad committed
	function _GenFields($flds,$widespacing=false)
	{
		if (is_string($flds)) {
			$padding = '     ';
			$txt = $flds.$padding;
			$flds = array();
			$flds0 = Lens_ParseArgs($txt,',');
			$hasparam = false;
			foreach($flds0 as $f0) {
				$f1 = array();
				foreach($f0 as $token) {
					switch (strtoupper($token)) {
Satish's avatar
Satish committed
					case 'INDEX':
						$f1['INDEX'] = '';
						// fall through intentionally
Prasad's avatar
Prasad committed
					case 'CONSTRAINT':
Satish's avatar
Satish committed
					case 'DEFAULT':
Prasad's avatar
Prasad committed
						$hasparam = $token;
						break;
					default:
						if ($hasparam) $f1[$hasparam] = $token;
						else $f1[] = $token;
						$hasparam = false;
						break;
					}
				}
Satish's avatar
Satish committed
				// 'index' token without a name means single column index: name it after column
				if (array_key_exists('INDEX', $f1) && $f1['INDEX'] == '') {
					$f1['INDEX'] = isset($f0['NAME']) ? $f0['NAME'] : $f0[0];
					// check if column name used to create an index name was quoted
					if (($f1['INDEX'][0] == '"' || $f1['INDEX'][0] == "'" || $f1['INDEX'][0] == "`") &&
						($f1['INDEX'][0] == substr($f1['INDEX'], -1))) {
						$f1['INDEX'] = $f1['INDEX'][0].'idx_'.substr($f1['INDEX'], 1, -1).$f1['INDEX'][0];
					}
					else
						$f1['INDEX'] = 'idx_'.$f1['INDEX'];
				}
				// reset it, so we don't get next field 1st token as INDEX...
				$hasparam = false;

Prasad's avatar
Prasad committed
				$flds[] = $f1;
Prasad's avatar
Prasad committed
			}
		}
		$this->autoIncrement = false;
		$lines = array();
		$pkey = array();
Satish's avatar
Satish committed
		$idxs = array();
Prasad's avatar
Prasad committed
		foreach($flds as $fld) {
			$fld = _array_change_key_case($fld);
Prasad's avatar
Prasad committed
			$fname = false;
			$fdefault = false;
			$fautoinc = false;
			$ftype = false;
			$fsize = false;
			$fprec = false;
			$fprimary = false;
			$fnoquote = false;
			$fdefts = false;
			$fdefdate = false;
			$fconstraint = false;
			$fnotnull = false;
			$funsigned = false;
Satish's avatar
Satish committed
			$findex = '';
			$funiqueindex = false;

Prasad's avatar
Prasad committed
			//-----------------
			// Parse attributes
			foreach($fld as $attr => $v) {
				if ($attr == 2 && is_numeric($v)) $attr = 'SIZE';
				else if (is_numeric($attr) && $attr > 1 && !is_numeric($v)) $attr = strtoupper($v);
Prasad's avatar
Prasad committed
				switch($attr) {
				case '0':
				case 'NAME': 	$fname = $v; break;
				case '1':
				case 'TYPE': 	$ty = $v; $ftype = $this->ActualType(strtoupper($v)); break;
Satish's avatar
Satish committed

				case 'SIZE':
Prasad's avatar
Prasad committed
								$dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,',');
								if ($dotat === false) $fsize = $v;
								else {
									$fsize = substr($v,0,$dotat);
									$fprec = substr($v,$dotat+1);
								}
								break;
				case 'UNSIGNED': $funsigned = true; break;
				case 'AUTOINCREMENT':
				case 'AUTO':	$fautoinc = true; $fnotnull = true; break;
				case 'KEY':
Satish's avatar
Satish committed
                // a primary key col can be non unique in itself (if key spans many cols...)
				case 'PRIMARY':	$fprimary = $v; $fnotnull = true; /*$funiqueindex = true;*/ break;
Prasad's avatar
Prasad committed
				case 'DEF':
				case 'DEFAULT': $fdefault = $v; break;
				case 'NOTNULL': $fnotnull = $v; break;
				case 'NOQUOTE': $fnoquote = $v; break;
				case 'DEFDATE': $fdefdate = $v; break;
				case 'DEFTIMESTAMP': $fdefts = $v; break;
				case 'CONSTRAINT': $fconstraint = $v; break;
Satish's avatar
Satish committed
				// let INDEX keyword create a 'very standard' index on column
				case 'INDEX': $findex = $v; break;
				case 'UNIQUE': $funiqueindex = true; break;
Prasad's avatar
Prasad committed
				} //switch
			} // foreach $fld
Prasad's avatar
Prasad committed
			//--------------------
			// VALIDATE FIELD INFO
			if (!strlen($fname)) {
				if ($this->debug) ADOConnection::outp("Undefined NAME");
				return false;
			}
Prasad's avatar
Prasad committed
			$fid = strtoupper(preg_replace('/^`(.+)`$/', '$1', $fname));
			$fname = $this->NameQuote($fname);
Prasad's avatar
Prasad committed
			if (!strlen($ftype)) {
				if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'");
				return false;
			} else {
				$ftype = strtoupper($ftype);
			}
Prasad's avatar
Prasad committed
			$ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec);
Prasad's avatar
Prasad committed
			if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; // some blob types do not accept nulls
Prasad's avatar
Prasad committed
			if ($fprimary) $pkey[] = $fname;
Prasad's avatar
Prasad committed
			// some databases do not allow blobs to have defaults
			if ($ty == 'X') $fdefault = false;
Satish's avatar
Satish committed

			// build list of indexes
			if ($findex != '') {
				if (array_key_exists($findex, $idxs)) {
					$idxs[$findex]['cols'][] = ($fname);
					if (in_array('UNIQUE', $idxs[$findex]['opts']) != $funiqueindex) {
						if ($this->debug) ADOConnection::outp("Index $findex defined once UNIQUE and once not");
					}
					if ($funiqueindex && !in_array('UNIQUE', $idxs[$findex]['opts']))
						$idxs[$findex]['opts'][] = 'UNIQUE';
				}
				else
				{
					$idxs[$findex] = array();
					$idxs[$findex]['cols'] = array($fname);
					if ($funiqueindex)
						$idxs[$findex]['opts'] = array('UNIQUE');
					else
						$idxs[$findex]['opts'] = array();
				}
			}

Prasad's avatar
Prasad committed
			//--------------------
			// CONSTRUCT FIELD SQL
			if ($fdefts) {
				if (substr($this->connection->databaseType,0,5) == 'mysql') {
					$ftype = 'TIMESTAMP';
				} else {
					$fdefault = $this->connection->sysTimeStamp;
				}
			} else if ($fdefdate) {
				if (substr($this->connection->databaseType,0,5) == 'mysql') {
					$ftype = 'TIMESTAMP';
				} else {
					$fdefault = $this->connection->sysDate;
				}
Satish's avatar
Satish committed
			} else if ($fdefault !== false && !$fnoquote) {
				if ($ty == 'C' or $ty == 'X' or
					( substr($fdefault,0,1) != "'" && !is_numeric($fdefault))) {

					if (($ty == 'D' || $ty == 'T') && strtolower($fdefault) != 'null') {
						// convert default date into database-aware code
						if ($ty == 'T')
						{
							$fdefault = $this->connection->DBTimeStamp($fdefault);
						}
						else
						{
							$fdefault = $this->connection->DBDate($fdefault);
						}
					}
					else
					if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ')
Prasad's avatar
Prasad committed
						$fdefault = trim($fdefault);
					else if (strtolower($fdefault) != 'null')
						$fdefault = $this->connection->qstr($fdefault);
Prasad's avatar
Prasad committed
			$suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned);
Satish's avatar
Satish committed

			// add index creation
Prasad's avatar
Prasad committed
			if ($widespacing) $fname = str_pad($fname,24);
Satish's avatar
Satish committed

			 // check for field names appearing twice
            if (array_key_exists($fid, $lines)) {
            	 ADOConnection::outp("Field '$fname' defined twice");
            }

Prasad's avatar
Prasad committed
			$lines[$fid] = $fname.' '.$ftype.$suffix;
Prasad's avatar
Prasad committed
			if ($fautoinc) $this->autoIncrement = true;
		} // foreach $flds
Satish's avatar
Satish committed

		return array($lines,$pkey,$idxs);
Prasad's avatar
Prasad committed
	}
Prasad's avatar
Prasad committed
		 GENERATE THE SIZE PART OF THE DATATYPE
			$ftype is the actual type
			$ty is the type defined originally in the DDL
	*/
	function _GetSize($ftype, $ty, $fsize, $fprec)
	{
		if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) {
			$ftype .= "(".$fsize;
			if (strlen($fprec)) $ftype .= ",".$fprec;
			$ftype .= ')';
		}
		return $ftype;
	}
Prasad's avatar
Prasad committed
	// return string must begin with space
Satish's avatar
Satish committed
	function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
	{
Prasad's avatar
Prasad committed
		$suffix = '';
		if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
		if ($fnotnull) $suffix .= ' NOT NULL';
		if ($fconstraint) $suffix .= ' '.$fconstraint;
		return $suffix;
	}
Prasad's avatar
Prasad committed
	function _IndexSQL($idxname, $tabname, $flds, $idxoptions)
	{
		$sql = array();
Prasad's avatar
Prasad committed
		if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) {
			$sql[] = sprintf ($this->dropIndex, $idxname);
			if ( isset($idxoptions['DROP']) )
				return $sql;
		}
Prasad's avatar
Prasad committed
		if ( empty ($flds) ) {
			return $sql;
		}
Prasad's avatar
Prasad committed
		$unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : '';
Prasad's avatar
Prasad committed
		$s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' ';
Prasad's avatar
Prasad committed
		if ( isset($idxoptions[$this->upperName]) )
			$s .= $idxoptions[$this->upperName];
Prasad's avatar
Prasad committed
		if ( is_array($flds) )
			$flds = implode(', ',$flds);
		$s .= '(' . $flds . ')';
		$sql[] = $s;
Prasad's avatar
Prasad committed
		return $sql;
	}
Prasad's avatar
Prasad committed
	function _DropAutoIncrement($tabname)
	{
		return false;
	}
Prasad's avatar
Prasad committed
	function _TableSQL($tabname,$lines,$pkey,$tableoptions)
	{
		$sql = array();
Prasad's avatar
Prasad committed
		if (isset($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) {
			$sql[] = sprintf($this->dropTable,$tabname);
			if ($this->autoIncrement) {
				$sInc = $this->_DropAutoIncrement($tabname);
				if ($sInc) $sql[] = $sInc;
			}
			if ( isset ($tableoptions['DROP']) ) {
				return $sql;
			}
		}
		$s = "CREATE TABLE $tabname (\n";
		$s .= implode(",\n", $lines);
		if (sizeof($pkey)>0) {
			$s .= ",\n                 PRIMARY KEY (";
			$s .= implode(", ",$pkey).")";
		}
Satish's avatar
Satish committed
		if (isset($tableoptions['CONSTRAINTS']))
Prasad's avatar
Prasad committed
			$s .= "\n".$tableoptions['CONSTRAINTS'];
Satish's avatar
Satish committed

		if (isset($tableoptions[$this->upperName.'_CONSTRAINTS']))
Prasad's avatar
Prasad committed
			$s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS'];
Prasad's avatar
Prasad committed
		$s .= "\n)";
		if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName];
		$sql[] = $s;
Prasad's avatar
Prasad committed
		return $sql;
	}
Prasad's avatar
Prasad committed
		GENERATE TRIGGERS IF NEEDED
		used when table has auto-incrementing field that is emulated using triggers
	*/
	function _Triggers($tabname,$taboptions)
	{
		return array();
	}
Prasad's avatar
Prasad committed
		Sanitize options, so that array elements with no keys are promoted to keys
	*/
	function _Options($opts)
	{
		if (!is_array($opts)) return array();
		$newopts = array();
		foreach($opts as $k => $v) {
			if (is_numeric($k)) $newopts[strtoupper($v)] = $v;
			else $newopts[strtoupper($k)] = $v;
		}
		return $newopts;
	}
Satish's avatar
Satish committed


	function _getSizePrec($size)
	{
		$fsize = false;
		$fprec = false;
		$dotat = strpos($size,'.');
		if ($dotat === false) $dotat = strpos($size,',');
		if ($dotat === false) $fsize = $size;
		else {
			$fsize = substr($size,0,$dotat);
			$fprec = substr($size,$dotat+1);
		}
		return array($fsize, $fprec);
	}

	/**
Prasad's avatar
Prasad committed
	"Florian Buzin [ easywe ]" <florian.buzin#easywe.de>
Prasad's avatar
Prasad committed
	This function changes/adds new fields to your table. You don't
	have to know if the col is new or not. It will check on its own.
	*/
Satish's avatar
Satish committed
	function ChangeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
Prasad's avatar
Prasad committed
	{
	global $ADODB_FETCH_MODE;
Prasad's avatar
Prasad committed
		$save = $ADODB_FETCH_MODE;
		$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
		if ($this->connection->fetchMode !== false) $savem = $this->connection->SetFetchMode(false);
Prasad's avatar
Prasad committed
		// check table exists
		$save_handler = $this->connection->raiseErrorFn;
		$this->connection->raiseErrorFn = '';
		$cols = $this->MetaColumns($tablename);
		$this->connection->raiseErrorFn = $save_handler;
Prasad's avatar
Prasad committed
		if (isset($savem)) $this->connection->SetFetchMode($savem);
		$ADODB_FETCH_MODE = $save;
Satish's avatar
Satish committed

		if ( empty($cols)) {
Prasad's avatar
Prasad committed
			return $this->CreateTableSQL($tablename, $flds, $tableoptions);
		}
Prasad's avatar
Prasad committed
		if (is_array($flds)) {
		// Cycle through the update fields, comparing
		// existing fields to fields to update.
		// if the Metatype and size is exactly the
		// same, ignore - by Mark Newham
			$holdflds = array();
			foreach($flds as $k=>$v) {
				if ( isset($cols[$k]) && is_object($cols[$k]) ) {
					// If already not allowing nulls, then don't change
					$obj = $cols[$k];
					if (isset($obj->not_null) && $obj->not_null)
						$v = str_replace('NOT NULL','',$v);
Satish's avatar
Satish committed
					if (isset($obj->auto_increment) && $obj->auto_increment && empty($v['AUTOINCREMENT']))
					    $v = str_replace('AUTOINCREMENT','',$v);
Prasad's avatar
Prasad committed

					$c = $cols[$k];
					$ml = $c->max_length;
					$mt = $this->MetaType($c->type,$ml);
Satish's avatar
Satish committed

					if (isset($c->scale)) $sc = $c->scale;
					else $sc = 99; // always force change if scale not known.

					if ($sc == -1) $sc = false;
					list($fsize, $fprec) = $this->_getSizePrec($v['SIZE']);

Prasad's avatar
Prasad committed
					if ($ml == -1) $ml = '';
					if ($mt == 'X') $ml = $v['SIZE'];
Satish's avatar
Satish committed
					if (($mt != $v['TYPE']) || ($ml != $fsize || $sc != $fprec) || (isset($v['AUTOINCREMENT']) && $v['AUTOINCREMENT'] != $obj->auto_increment)) {
Prasad's avatar
Prasad committed
						$holdflds[$k] = $v;
					}
				} else {
					$holdflds[$k] = $v;
Prasad's avatar
Prasad committed
			}
			$flds = $holdflds;
		}
Prasad's avatar
Prasad committed

		// already exists, alter table instead