Source for file adodb-datadict.inc.php
Documentation is available at adodb-datadict.inc.php
V4.97 22 Jan 2008 (c) 2000-2008 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.
See adodb/tests/test-datadict.php for docs and examples.
$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";
Parse arguments, treat "text" (text) and 'text' as quotation marks.
To escape, use "" or '' or ))
Will read in "abc def" sans quotes, as: abc def
However if `abc def`, then will read in as `abc def`
@param endstmtchar Character that indicates end of statement
@param tokenchars Include the following characters in tokens apart from A-Z and 0-9
@returns 2 dimensional array containing parsed tokens.
$tokens[$stmtno] = array();
$tokens[$stmtno][] = implode('',$tokarr);
if ($intoken) $tokarr[] = $ch;
$tokens[$stmtno][] = implode('',$tokarr);
if ($ch == '(') $endquote = ')';
} else if ($endquote == $ch) {
$ch2 = substr($args,$pos+ 1,1);
$tokens[$stmtno][] = implode('',$tokarr);
if ($ch == '(') $endquote = ')';
if ($ch == '`') $tokarr[] = '`';
if ($ch == $endstmtchar) {
$tokens[$stmtno] = array();
if ($quoted) $tokarr[] = $ch;
if ($ch == $endstmtchar) {
$tokens[$stmtno][] = implode('',$tokarr);
$tokens[$stmtno] = array();
$tokens[$stmtno][] = implode('',$tokarr);
$tokens[$stmtno][] = $ch;
if ($intoken) $tokens[$stmtno][] = implode('',$tokarr);
var $renameColumn = 'ALTER TABLE %s RENAME COLUMN %s TO %s'; // table, old-column, new-column, column-definitions (not used by default)
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.
if (!$this->connection->IsConnected()) return array();
if (!$this->connection->IsConnected()) return array();
if (!$this->connection->IsConnected()) return array();
function MetaIndexes($table, $primary = false, $owner = false)
if (!$this->connection->IsConnected()) return array();
function MetaType($t,$len=- 1,$fieldobj= false)
'INTERVAL' => 'C', # Postgres
'MACADDR' => 'C', # postgres
'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
'INTEGER UNSIGNED' => 'I',
'LONG' => 'N', // interbase is numeric, oci8 is blob
'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
'DOUBLE PRECISION' => 'N',
if (!$this->connection->IsConnected()) {
if (isset ($typeMap[$t])) return $typeMap[$t];
return $this->connection->MetaType($t,$len,$fieldobj);
function NameQuote($name = NULL,$allowBrackets= false)
// if name is of the form `name`, quote it
if ( preg_match('/^`(.+)`$/', $name, $matches) ) {
return $quote . $matches[1] . $quote;
// if name contains special characters, quote it
if ( !preg_match('/^[' . $regex . ']+$/', $name) ) {
return $quote . $name . $quote;
// Executes the sql array returned by GetTableSQL and GetIndexSQL
$ok = $conn->Execute($line);
if (!$continueOnError) return 0;
Returns the actual type given a character code.
X: CLOB (character large object) or largest varchar size if CLOB is not supported
B: BLOB (binary large object)
L: Integer field suitable for storing booleans (0 or 1)
N: Numeric or decimal number
$s = 'CREATE DATABASE ' . $this->NameQuote($dbname);
if (isset ($options[$this->upperName]))
$s .= ' '. $options[$this->upperName];
Generates the SQL to create index. Returns an array of sql strings.
function CreateIndexSQL($idxname, $tabname, $flds, $idxoptions = false)
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);
list ($lines,$pkey,$idxs) = $this->_GenFields($flds);
// genfields can return FALSE at times
if ($lines == null) $lines = array();
$alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';
foreach($idxs as $idx => $idxdef) {
$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
* 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= '')
list ($lines,$pkey,$idxs) = $this->_GenFields($flds);
// genfields can return FALSE at times
if ($lines == null) $lines = array();
$alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
foreach($idxs as $idx => $idxdef) {
$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
* 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
list ($lines,$pkey,$idxs) = $this->_GenFields($flds);
// genfields can return FALSE at times
if ($lines == null) $lines = array();
list (,$first) = each($lines);
list (,$column_def) = split("[\t ]+",$first,2);
* 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= '')
$alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' ';
Generate the SQL to create table. Returns an array of sql strings.
list ($lines,$pkey,$idxs) = $this->_GenFields($flds, true);
// genfields can return FALSE at times
if ($lines == null) $lines = array();
$taboptions = $this->_Options($tableoptions);
$sql = $this->_TableSQL($tabname,$lines,$pkey,$taboptions);
// 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
unset ($taboptions['REPLACE']);
$tsql = $this->_Triggers($tabname,$taboptions);
foreach($tsql as $s) $sql[] = $s;
foreach($idxs as $idx => $idxdef) {
$sql_idxs = $this->CreateIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
// fall through intentionally
if ($hasparam) $f1[$hasparam] = $token;
// 'index' token without a name means single column index: name it after column
$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];
$f1['INDEX'] = 'idx_'. $f1['INDEX'];
// reset it, so we don't get next field 1st token as INDEX...
foreach($fld as $attr => $v) {
case 'NAME': $fname = $v; break;
$dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,',');
if ($dotat === false) $fsize = $v;
case 'UNSIGNED': $funsigned = true; break;
case 'AUTO': $fautoinc = true; $fnotnull = true; break;
// a primary key col can be non unique in itself (if key spans many cols...)
case 'PRIMARY': $fprimary = $v; $fnotnull = true; /*$funiqueindex = true;*/ break;
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;
// let INDEX keyword create a 'very standard' index on column
case 'INDEX': $findex = $v; break;
case 'UNIQUE': $funiqueindex = true; break;
$ftype = $this->_GetSize($ftype, $ty, $fsize, $fprec);
if ($ty == 'X' || $ty == 'X2' || $ty == 'B') $fnotnull = false; // some blob types do not accept nulls
if ($fprimary) $pkey[] = $fname;
// some databases do not allow blobs to have defaults
if ($ty == 'X') $fdefault = false;
$idxs[$findex]['cols'][] = ($fname);
if (in_array('UNIQUE', $idxs[$findex]['opts']) != $funiqueindex) {
if ($funiqueindex && !in_array('UNIQUE', $idxs[$findex]['opts']))
$idxs[$findex]['opts'][] = 'UNIQUE';
$idxs[$findex] = array();
$idxs[$findex]['cols'] = array($fname);
$idxs[$findex]['opts'] = array('UNIQUE');
$idxs[$findex]['opts'] = array();
} else if ($fdefault !== false && !$fnoquote) {
if ($ty == 'C' or $ty == 'X' or
if (($ty == 'D' || $ty == 'T') && strtolower($fdefault) != 'null') {
// convert default date into database-aware code
$fdefault = $this->connection->DBTimeStamp($fdefault);
$fdefault = trim($fdefault);
$suffix = $this->_CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned);
if ($widespacing) $fname = str_pad($fname,24);
// check for field names appearing twice
$lines[$fid] = $fname. ' '. $ftype. $suffix;
return array($lines,$pkey,$idxs);
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) {
if (strlen($fprec)) $ftype .= ",". $fprec;
// return string must begin with space
function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
if ($fnotnull) $suffix .= ' NOT NULL';
if ($fconstraint) $suffix .= ' '. $fconstraint;
function _IndexSQL($idxname, $tabname, $flds, $idxoptions)
if ( isset ($idxoptions['REPLACE']) || isset ($idxoptions['DROP']) ) {
if ( isset ($idxoptions['DROP']) )
$unique = isset ($idxoptions['UNIQUE']) ? ' UNIQUE' : '';
$s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' ';
if ( isset ($idxoptions[$this->upperName]) )
$s .= $idxoptions[$this->upperName];
function _TableSQL($tabname,$lines,$pkey,$tableoptions)
if (isset ($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) {
if ($sInc) $sql[] = $sInc;
if ( isset ($tableoptions['DROP']) ) {
$s = "CREATE TABLE $tabname (\n";
$s .= ",\n PRIMARY KEY (";
if (isset ($tableoptions['CONSTRAINTS']))
$s .= "\n". $tableoptions['CONSTRAINTS'];
if (isset ($tableoptions[$this->upperName. '_CONSTRAINTS']))
$s .= "\n". $tableoptions[$this->upperName. '_CONSTRAINTS'];
if (isset ($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName];
GENERATE TRIGGERS IF NEEDED
used when table has auto-incrementing field that is emulated using triggers
Sanitize options, so that array elements with no keys are promoted to keys
foreach($opts as $k => $v) {
"Florian Buzin [ easywe ]" <florian.buzin#easywe.de>
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.
global $ADODB_FETCH_MODE;
$save = $ADODB_FETCH_MODE;
if (isset ($savem)) $this->connection->SetFetchMode($savem);
$ADODB_FETCH_MODE = $save;
// 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
foreach($flds as $k=> $v) {
if ( isset ($cols[$k]) && is_object($cols[$k]) ) {
// If already not allowing nulls, then don't change
if (isset ($obj->not_null) && $obj->not_null)
if ($mt == 'X') $ml = $v['SIZE'];
if (($mt != $v['TYPE']) || $ml != $v['SIZE']) {
// already exists, alter table instead
list ($lines,$pkey,$idxs) = $this->_GenFields($flds);
// genfields can return FALSE at times
if ($lines == null) $lines = array();
$alter = 'ALTER TABLE ' . $this->TableName($tablename);
foreach ( $lines as $id => $v ) {
if ( isset ($cols[$id]) && is_object($cols[$id]) ) {
// We are trying to change the size of the field, if not allowed, simply ignore the request.
// $flds[1] holds the type, $flds[2] holds the size -postnuke addition
&& (isset ($flds[0][2]) && is_numeric($flds[0][2]))) {
#echo "<h3>$this->alterCol cannot be changed to $flds currently</h3>";
$sql[] = $alter . $this->alterCol . ' ' . $v;
$sql[] = $alter . $this->addCol . ' ' . $v;
|