<?php
/*
-@version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+@version V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
Latest version is available at http://adodb.sourceforge.net
Released under both BSD license and Lesser GPL library license.
Active Record implementation. Superset of Zend Framework's.
- Version 0.08
+ Version 0.92
See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord
for info on Ruby on Rails Active Record implementation
*/
+
global $_ADODB_ACTIVE_DBS;
global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
var $flds; // assoc array of adofieldobjs, indexed by fieldname
var $keys; // assoc array of primary keys, indexed by fieldname
var $_created; // only used when stored as a cached file
+ var $_belongsTo = array();
+ var $_hasMany = array();
}
+// $db = database connection
+// $index = name of index - can be associative, for an example see
+// http://phplens.com/lens/lensforum/msgs.php?id=17790
// returns index into $_ADODB_ACTIVE_DBS
-function ADODB_SetDatabaseAdapter(&$db)
+function ADODB_SetDatabaseAdapter(&$db, $index=false)
{
global $_ADODB_ACTIVE_DBS;
$obj->db = $db;
$obj->tables = array();
- $_ADODB_ACTIVE_DBS[] = $obj;
+ if ($index == false) $index = sizeof($_ADODB_ACTIVE_DBS);
+
+
+ $_ADODB_ACTIVE_DBS[$index] = $obj;
return sizeof($_ADODB_ACTIVE_DBS)-1;
}
class ADODB_Active_Record {
+ static $_changeNames = true; // dynamically pluralize table names
+ static $_foreignSuffix = '_id'; //
var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
var $_table; // tablename, if set in class definition then use it as table name
var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
var $_saved = false; // indicates whether data is already inserted.
var $_lasterr = false; // last error message
var $_original = false; // the original values loaded or inserted, refreshed on update
-
+
+ var $foreignName; // CFR: class name when in a relationship
+
static function UseDefaultValues($bool=null)
{
global $ADODB_ACTIVE_DEFVALS;
}
// should be static
- static function SetDatabaseAdapter(&$db)
+ static function SetDatabaseAdapter(&$db, $index=false)
{
- return ADODB_SetDatabaseAdapter($db);
+ return ADODB_SetDatabaseAdapter($db, $index);
}
if (!empty($this->_table)) $table = $this->_table;
else $table = $this->_pluralize(get_class($this));
}
+ $this->foreignName = strtolower(get_class($this)); // CFR: default foreign name
if ($db) {
$this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
- } else
- $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1;
-
-
- if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
-
+ } else if (!isset($this->_dbat)) {
+ if (sizeof($_ADODB_ACTIVE_DBS) == 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
+ end($_ADODB_ACTIVE_DBS);
+ $this->_dbat = key($_ADODB_ACTIVE_DBS);
+ }
+
$this->_table = $table;
$this->_tableat = $table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
+
$this->UpdateActiveTable($pkeyarr);
}
function _pluralize($table)
{
+ if (!ADODB_Active_Record::$_changeNames) return $table;
+
$ut = strtoupper($table);
$len = strlen($table);
$lastc = $ut[$len-1];
}
}
+ // CFR Lamest singular inflector ever - @todo Make it real!
+ // Note: There is an assumption here...and it is that the argument's length >= 4
+ function _singularize($tables)
+ {
+
+ if (!ADODB_Active_Record::$_changeNames) return $table;
+
+ $ut = strtoupper($tables);
+ $len = strlen($tables);
+ if($ut[$len-1] != 'S')
+ return $tables; // I know...forget oxen
+ if($ut[$len-2] != 'E')
+ return substr($tables, 0, $len-1);
+ switch($ut[$len-3])
+ {
+ case 'S':
+ case 'X':
+ return substr($tables, 0, $len-2);
+ case 'I':
+ return substr($tables, 0, $len-3) . 'y';
+ case 'H';
+ if($ut[$len-4] == 'C' || $ut[$len-4] == 'S')
+ return substr($tables, 0, $len-2);
+ default:
+ return substr($tables, 0, $len-1); // ?
+ }
+ }
+
+ function hasMany($foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+ {
+ $ar = new $foreignClass($foreignRef);
+ $ar->foreignName = $foreignRef;
+ $ar->UpdateActiveTable();
+ $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
+ $table =& $this->TableInfo();
+ $table->_hasMany[$foreignRef] = $ar;
+ # $this->$foreignRef = $this->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get()
+ }
+
+ // use when you don't want ADOdb to auto-pluralize tablename
+ static function TableHasMany($table, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+ {
+ $ar = new ADODB_Active_Record($table);
+ $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
+ }
+
+ // use when you don't want ADOdb to auto-pluralize tablename
+ static function TableKeyHasMany($table, $tablePKey, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+ {
+ if (!is_array($tablePKey)) $tablePKey = array($tablePKey);
+ $ar = new ADODB_Active_Record($table,$tablePKey);
+ $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
+ }
+
+
+ // use when you want ADOdb to auto-pluralize tablename for you. Note that the class must already be defined.
+ // e.g. class Person will generate relationship for table Persons
+ static function ClassHasMany($parentclass, $foreignRef, $foreignKey = false, $foreignClass = 'ADODB_Active_Record')
+ {
+ $ar = new $parentclass();
+ $ar->hasMany($foreignRef, $foreignKey, $foreignClass);
+ }
+
+
+ function belongsTo($foreignRef,$foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+ {
+ global $inflector;
+
+ $ar = new $parentClass($this->_pluralize($foreignRef));
+ $ar->foreignName = $foreignRef;
+ $ar->parentKey = $parentKey;
+ $ar->UpdateActiveTable();
+ $ar->foreignKey = ($foreignKey) ? $foreignKey : $foreignRef.ADODB_Active_Record::$_foreignSuffix;
+
+ $table =& $this->TableInfo();
+ $table->_belongsTo[$foreignRef] = $ar;
+ # $this->$foreignRef = $this->_belongsTo[$foreignRef];
+ }
+
+ static function ClassBelongsTo($class, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+ {
+ $ar = new $class();
+ $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
+ }
+
+ static function TableBelongsTo($table, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+ {
+ $ar = new ADOdb_Active_Record($table);
+ $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
+ }
+
+ static function TableKeyBelongsTo($table, $tablePKey, $foreignRef, $foreignKey=false, $parentKey='', $parentClass = 'ADODB_Active_Record')
+ {
+ if (!is_array($tablePKey)) $tablePKey = array($tablePKey);
+ $ar = new ADOdb_Active_Record($table, $tablePKey);
+ $ar->belongsTo($foreignRef, $foreignKey, $parentKey, $parentClass);
+ }
+
+
+ /**
+ * __get Access properties - used for lazy loading
+ *
+ * @param mixed $name
+ * @access protected
+ * @return mixed
+ */
+ function __get($name)
+ {
+ return $this->LoadRelations($name, '', -1, -1);
+ }
+
+ /**
+ * @param string $name
+ * @param string $whereOrderBy : eg. ' AND field1 = value ORDER BY field2'
+ * @param offset
+ * @param limit
+ * @return mixed
+ */
+ function LoadRelations($name, $whereOrderBy='', $offset=-1,$limit=-1)
+ {
+ $extras = array();
+ $table = $this->TableInfo();
+ if ($limit >= 0) $extras['limit'] = $limit;
+ if ($offset >= 0) $extras['offset'] = $offset;
+
+ if (strlen($whereOrderBy))
+ if (!preg_match('/^[ \n\r]*AND/i',$whereOrderBy))
+ if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i',$whereOrderBy))
+ $whereOrderBy = 'AND '.$whereOrderBy;
+
+ if(!empty($table->_belongsTo[$name]))
+ {
+ $obj = $table->_belongsTo[$name];
+ $columnName = $obj->foreignKey;
+ if(empty($this->$columnName))
+ $this->$name = null;
+ else
+ {
+ if ($obj->parentKey) $key = $obj->parentKey;
+ else $key = reset($table->keys);
+
+ $arrayOfOne = $obj->Find($key.'='.$this->$columnName.' '.$whereOrderBy,false,false,$extras);
+ if ($arrayOfOne) {
+ $this->$name = $arrayOfOne[0];
+ return $arrayOfOne[0];
+ }
+ }
+ }
+ if(!empty($table->_hasMany[$name]))
+ {
+ $obj = $table->_hasMany[$name];
+ $key = reset($table->keys);
+ $id = @$this->$key;
+ if (!is_numeric($id)) {
+ $db = $this->DB();
+ $id = $db->qstr($id);
+ }
+ $objs = $obj->Find($obj->foreignKey.'='.$id. ' '.$whereOrderBy,false,false,$extras);
+ if (!$objs) $objs = array();
+ $this->$name = $objs;
+ return $objs;
+ }
+
+ return array();
+ }
//////////////////////////////////
// update metadata
function UpdateActiveTable($pkeys=false,$forceUpdate=false)
{
global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
- global $ADODB_ACTIVE_DEFVALS;
+ global $ADODB_ACTIVE_DEFVALS,$ADODB_FETCH_MODE;
$activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
}
return;
}
-
$db = $activedb->db;
$fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
$activetab = new ADODB_Active_Table();
$activetab->name = $table;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
+ if ($db->fetchMode !== false) $savem = $db->SetFetchMode(false);
$cols = $db->MetaColumns($table);
+
+ if (isset($savem)) $db->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+
if (!$cols) {
$this->Error("Invalid table name: $table",'UpdateActiveTable');
return false;
if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php');
adodb_write_file($fname,$s);
}
+ if (isset($activedb->tables[$table])) {
+ $oldtab = $activedb->tables[$table];
+
+ if ($oldtab) $activetab->_belongsTo = $oldtab->_belongsTo;
+ if ($oldtab) $activetab->_hasMany = $oldtab->_hasMany;
+ }
$activedb->tables[$table] = $activetab;
}
}
// retrieve ADODB_Active_Table
- function TableInfo()
+ function &TableInfo()
{
global $_ADODB_ACTIVE_DBS;
-
$activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
$table = $activedb->tables[$this->_tableat];
return $table;
}
+
+ // I have an ON INSERT trigger on a table that sets other columns in the table.
+ // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook
+ function Reload()
+ {
+ $db =& $this->DB(); if (!$db) return false;
+ $table =& $this->TableInfo();
+ $where = $this->GenWhere($db, $table);
+ return($this->Load($where));
+ }
+
+
// set a numeric array (using natural table field ordering) as object properties
function Set(&$row)
{
# </AP>
}
else
- $keys = array_keys($row);
+ $keys = array_keys($row);
+
# <AP>
reset($keys);
$this->_original = array();
$this->_original[] = $value;
next($keys);
}
+
# </AP>
return true;
}
case 'D':
case 'T':
if (empty($val)) return 'null';
-
+
+ case 'B':
+ case 'N':
case 'C':
case 'X':
if (is_null($val)) return 'null';
- if (strncmp($val,"'",1) != 0 && substr($val,strlen($val)-1,1) != "'") {
+ if (strlen($val)>1 &&
+ (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'")) {
return $db->qstr($val);
break;
}
//------------------------------------------------------------ Public functions below
- function Load($where,$bindarr=false)
+ function Load($where=null,$bindarr=false)
{
+ global $ADODB_FETCH_MODE;
+
$db = $this->DB(); if (!$db) return false;
$this->_where = $where;
- $save = $db->SetFetchMode(ADODB_FETCH_NUM);
- $row = $db->GetRow("select * from ".$this->_table.' WHERE '.$where,$bindarr);
- $db->SetFetchMode($save);
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($db->fetchMode !== false) $savem = $db->SetFetchMode(false);
+
+ $qry = "select * from ".$this->_table;
+
+ if($where) {
+ $qry .= ' WHERE '.$where;
+ }
+ $row = $db->GetRow($qry,$bindarr);
+
+ if (isset($savem)) $db->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
return $this->Set($row);
}
+ # useful for multiple record inserts
+ # see http://phplens.com/lens/lensforum/msgs.php?id=17795
+ function Reset()
+ {
+ $this->_where=null;
+ $this->_saved = false;
+ $this->_lasterr = false;
+ $this->_original = false;
+ $vars=get_object_vars($this);
+ foreach($vars as $k=>$v){
+ if(substr($k,0,1)!=='_'){
+ $this->{$k}=null;
+ }
+ }
+ $this->foreignName=strtolower(get_class($this));
+ return true;
+ }
+
// false on error
function Save()
{
foreach($table->flds as $name=>$fld) {
$val = $this->$name;
- if(!is_null($val) || !array_key_exists($name, $table->keys)) {
+ if(!is_array($val) || !is_null($val) || !array_key_exists($name, $table->keys)) {
$valarr[] = $val;
$names[] = $name;
$valstr[] = $db->Param($cnt);
}
// returns an array of active record objects
- function Find($whereOrderBy,$bindarr=false,$pkeysArr=false)
+ function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
{
$db = $this->DB(); if (!$db || empty($this->_table)) return false;
- $arr = $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr);
+ $arr = $db->GetActiveRecordsClass(get_class($this),$this->_table, $whereOrderBy,$bindarr,$pkeysArr,$extra);
return $arr;
}
if (is_null($val) && !empty($fld->auto_increment)) {
continue;
}
+
+ if (is_array($val)) continue;
+
$t = $db->MetaType($fld->type);
$arr[$name] = $this->doquote($db,$val,$t);
$valarr[] = $val;
$val = $this->$name;
$neworig[] = $val;
- if (isset($table->keys[$name])) {
+ if (isset($table->keys[$name]) || is_array($val))
continue;
- }
if (is_null($val)) {
if (isset($fld->not_null) && $fld->not_null) {
};
+function adodb_GetActiveRecordsClass(&$db, $class, $table,$whereOrderBy,$bindarr, $primkeyArr,
+ $extra)
+{
+global $_ADODB_ACTIVE_DBS;
+
+
+ $save = $db->SetFetchMode(ADODB_FETCH_NUM);
+ $qry = "select * from ".$table;
+
+ if (!empty($whereOrderBy))
+ $qry .= ' WHERE '.$whereOrderBy;
+ if(isset($extra['limit']))
+ {
+ $rows = false;
+ if(isset($extra['offset'])) {
+ $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset']);
+ } else {
+ $rs = $db->SelectLimit($qry, $extra['limit']);
+ }
+ if ($rs) {
+ while (!$rs->EOF) {
+ $rows[] = $rs->fields;
+ $rs->MoveNext();
+ }
+ }
+ } else
+ $rows = $db->GetAll($qry,$bindarr);
+
+ $db->SetFetchMode($save);
+
+ $false = false;
+
+ if ($rows === false) {
+ return $false;
+ }
+
+
+ if (!class_exists($class)) {
+ $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
+ return $false;
+ }
+ $arr = array();
+ // arrRef will be the structure that knows about our objects.
+ // It is an associative array.
+ // We will, however, return arr, preserving regular 0.. order so that
+ // obj[0] can be used by app developpers.
+ $arrRef = array();
+ $bTos = array(); // Will store belongTo's indices if any
+ foreach($rows as $row) {
+
+ $obj = new $class($table,$primkeyArr,$db);
+ if ($obj->ErrorNo()){
+ $db->_errorMsg = $obj->ErrorMsg();
+ return $false;
+ }
+ $obj->Set($row);
+ $arr[] = $obj;
+ } // foreach($rows as $row)
+
+ return $arr;
+}
?>
\ No newline at end of file
--- /dev/null
+<?php
+/*
+
+@version V5.06 29 Sept 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
+ Latest version is available at http://adodb.sourceforge.net
+
+ 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.
+
+ Active Record implementation. Superset of Zend Framework's.
+
+ This is "Active Record eXtended" to support JOIN, WORK and LAZY mode by Chris Ravenscroft chris#voilaweb.com
+
+ Version 0.9
+
+ See http://www-128.ibm.com/developerworks/java/library/j-cb03076/?ca=dgr-lnxw01ActiveRecord
+ for info on Ruby on Rails Active Record implementation
+*/
+
+
+ // CFR: Active Records Definitions
+define('ADODB_JOIN_AR', 0x01);
+define('ADODB_WORK_AR', 0x02);
+define('ADODB_LAZY_AR', 0x03);
+
+
+global $_ADODB_ACTIVE_DBS;
+global $ADODB_ACTIVE_CACHESECS; // set to true to enable caching of metadata such as field info
+global $ACTIVE_RECORD_SAFETY; // set to false to disable safety checks
+global $ADODB_ACTIVE_DEFVALS; // use default values of table definition when creating new active record.
+
+// array of ADODB_Active_DB's, indexed by ADODB_Active_Record->_dbat
+$_ADODB_ACTIVE_DBS = array();
+$ACTIVE_RECORD_SAFETY = true; // CFR: disabled while playing with relations
+$ADODB_ACTIVE_DEFVALS = false;
+
+class ADODB_Active_DB {
+ var $db; // ADOConnection
+ var $tables; // assoc array of ADODB_Active_Table objects, indexed by tablename
+}
+
+class ADODB_Active_Table {
+ var $name; // table name
+ var $flds; // assoc array of adofieldobjs, indexed by fieldname
+ var $keys; // assoc array of primary keys, indexed by fieldname
+ var $_created; // only used when stored as a cached file
+ var $_belongsTo = array();
+ var $_hasMany = array();
+ var $_colsCount; // total columns count, including relations
+
+ function updateColsCount()
+ {
+ $this->_colsCount = sizeof($this->flds);
+ foreach($this->_belongsTo as $foreignTable)
+ $this->_colsCount += sizeof($foreignTable->TableInfo()->flds);
+ foreach($this->_hasMany as $foreignTable)
+ $this->_colsCount += sizeof($foreignTable->TableInfo()->flds);
+ }
+}
+
+// returns index into $_ADODB_ACTIVE_DBS
+function ADODB_SetDatabaseAdapter(&$db)
+{
+ global $_ADODB_ACTIVE_DBS;
+
+ foreach($_ADODB_ACTIVE_DBS as $k => $d) {
+ if (PHP_VERSION >= 5) {
+ if ($d->db === $db) return $k;
+ } else {
+ if ($d->db->_connectionID === $db->_connectionID && $db->database == $d->db->database)
+ return $k;
+ }
+ }
+
+ $obj = new ADODB_Active_DB();
+ $obj->db = $db;
+ $obj->tables = array();
+
+ $_ADODB_ACTIVE_DBS[] = $obj;
+
+ return sizeof($_ADODB_ACTIVE_DBS)-1;
+}
+
+
+class ADODB_Active_Record {
+ static $_changeNames = true; // dynamically pluralize table names
+ static $_foreignSuffix = '_id'; //
+ var $_dbat; // associative index pointing to ADODB_Active_DB eg. $ADODB_Active_DBS[_dbat]
+ var $_table; // tablename, if set in class definition then use it as table name
+ var $_sTable; // singularized table name
+ var $_pTable; // pluralized table name
+ var $_tableat; // associative index pointing to ADODB_Active_Table, eg $ADODB_Active_DBS[_dbat]->tables[$this->_tableat]
+ var $_where; // where clause set in Load()
+ var $_saved = false; // indicates whether data is already inserted.
+ var $_lasterr = false; // last error message
+ var $_original = false; // the original values loaded or inserted, refreshed on update
+
+ var $foreignName; // CFR: class name when in a relationship
+
+ static function UseDefaultValues($bool=null)
+ {
+ global $ADODB_ACTIVE_DEFVALS;
+ if (isset($bool)) $ADODB_ACTIVE_DEFVALS = $bool;
+ return $ADODB_ACTIVE_DEFVALS;
+ }
+
+ // should be static
+ static function SetDatabaseAdapter(&$db)
+ {
+ return ADODB_SetDatabaseAdapter($db);
+ }
+
+
+ public function __set($name, $value)
+ {
+ $name = str_replace(' ', '_', $name);
+ $this->$name = $value;
+ }
+
+ // php5 constructor
+ // Note: if $table is defined, then we will use it as our table name
+ // Otherwise we will use our classname...
+ // In our database, table names are pluralized (because there can be
+ // more than one row!)
+ // Similarly, if $table is defined here, it has to be plural form.
+ //
+ // $options is an array that allows us to tweak the constructor's behaviour
+ // if $options['refresh'] is true, we re-scan our metadata information
+ // if $options['new'] is true, we forget all relations
+ function __construct($table = false, $pkeyarr=false, $db=false, $options=array())
+ {
+ global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS;
+
+ if ($db == false && is_object($pkeyarr)) {
+ $db = $pkeyarr;
+ $pkeyarr = false;
+ }
+
+ if($table)
+ {
+ // table argument exists. It is expected to be
+ // already plural form.
+ $this->_pTable = $table;
+ $this->_sTable = $this->_singularize($this->_pTable);
+ }
+ else
+ {
+ // We will use current classname as table name.
+ // We need to pluralize it for the real table name.
+ $this->_sTable = strtolower(get_class($this));
+ $this->_pTable = $this->_pluralize($this->_sTable);
+ }
+ $this->_table = &$this->_pTable;
+
+ $this->foreignName = $this->_sTable; // CFR: default foreign name (singular)
+
+ if ($db) {
+ $this->_dbat = ADODB_Active_Record::SetDatabaseAdapter($db);
+ } else
+ $this->_dbat = sizeof($_ADODB_ACTIVE_DBS)-1;
+
+
+ if ($this->_dbat < 0) $this->Error("No database connection set; use ADOdb_Active_Record::SetDatabaseAdapter(\$db)",'ADODB_Active_Record::__constructor');
+
+ $this->_tableat = $this->_table; # reserved for setting the assoc value to a non-table name, eg. the sql string in future
+
+ // CFR: Just added this option because UpdateActiveTable() can refresh its information
+ // but there was no way to ask it to do that.
+ $forceUpdate = (isset($options['refresh']) && true === $options['refresh']);
+ $this->UpdateActiveTable($pkeyarr, $forceUpdate);
+ if(isset($options['new']) && true === $options['new'])
+ {
+ $table =& $this->TableInfo();
+ unset($table->_hasMany);
+ unset($table->_belongsTo);
+ $table->_hasMany = array();
+ $table->_belongsTo = array();
+ }
+ }
+
+ function __wakeup()
+ {
+ $class = get_class($this);
+ new $class;
+ }
+
+ // CFR: Constants found in Rails
+ static $IrregularP = array(
+ 'PERSON' => 'people',
+ 'MAN' => 'men',
+ 'WOMAN' => 'women',
+ 'CHILD' => 'children',
+ 'COW' => 'kine',
+ );
+
+ static $IrregularS = array(
+ 'PEOPLE' => 'PERSON',
+ 'MEN' => 'man',
+ 'WOMEN' => 'woman',
+ 'CHILDREN' => 'child',
+ 'KINE' => 'cow',
+ );
+
+ static $WeIsI = array(
+ 'EQUIPMENT' => true,
+ 'INFORMATION' => true,
+ 'RICE' => true,
+ 'MONEY' => true,
+ 'SPECIES' => true,
+ 'SERIES' => true,
+ 'FISH' => true,
+ 'SHEEP' => true,
+ );
+
+ function _pluralize($table)
+ {
+ if (!ADODB_Active_Record::$_changeNames) return $table;
+
+ $ut = strtoupper($table);
+ if(isset(self::$WeIsI[$ut]))
+ {
+ return $table;
+ }
+ if(isset(self::$IrregularP[$ut]))
+ {
+ return self::$IrregularP[$ut];
+ }
+ $len = strlen($table);
+ $lastc = $ut[$len-1];
+ $lastc2 = substr($ut,$len-2);
+ switch ($lastc) {
+ case 'S':
+ return $table.'es';
+ case 'Y':
+ return substr($table,0,$len-1).'ies';
+ case 'X':
+ return $table.'es';
+ case 'H':
+ if ($lastc2 == 'CH' || $lastc2 == 'SH')
+ return $table.'es';
+ default:
+ return $table.'s';
+ }
+ }
+
+ // CFR Lamest singular inflector ever - @todo Make it real!
+ // Note: There is an assumption here...and it is that the argument's length >= 4
+ function _singularize($table)
+ {
+
+ if (!ADODB_Active_Record::$_changeNames) return $table;
+
+ $ut = strtoupper($table);
+ if(isset(self::$WeIsI[$ut]))
+ {
+ return $table;
+ }
+ if(isset(self::$IrregularS[$ut]))
+ {
+ return self::$IrregularS[$ut];
+ }
+ $len = strlen($table);
+ if($ut[$len-1] != 'S')
+ return $table; // I know...forget oxen
+ if($ut[$len-2] != 'E')
+ return substr($table, 0, $len-1);
+ switch($ut[$len-3])
+ {
+ case 'S':
+ case 'X':
+ return substr($table, 0, $len-2);
+ case 'I':
+ return substr($table, 0, $len-3) . 'y';
+ case 'H';
+ if($ut[$len-4] == 'C' || $ut[$len-4] == 'S')
+ return substr($table, 0, $len-2);
+ default:
+ return substr($table, 0, $len-1); // ?
+ }
+ }
+
+ /*
+ * ar->foreignName will contain the name of the tables associated with this table because
+ * these other tables' rows may also be referenced by this table using theirname_id or the provided
+ * foreign keys (this index name is stored in ar->foreignKey)
+ *
+ * this-table.id = other-table-#1.this-table_id
+ * = other-table-#2.this-table_id
+ */
+ function hasMany($foreignRef,$foreignKey=false)
+ {
+ $ar = new ADODB_Active_Record($foreignRef);
+ $ar->foreignName = $foreignRef;
+ $ar->UpdateActiveTable();
+ $ar->foreignKey = ($foreignKey) ? $foreignKey : strtolower(get_class($this)) . self::$_foreignSuffix;
+
+ $table =& $this->TableInfo();
+ if(!isset($table->_hasMany[$foreignRef]))
+ {
+ $table->_hasMany[$foreignRef] = $ar;
+ $table->updateColsCount();
+ }
+# @todo Can I make this guy be lazy?
+ $this->$foreignRef = $table->_hasMany[$foreignRef]; // WATCHME Removed assignment by ref. to please __get()
+ }
+
+ /**
+ * ar->foreignName will contain the name of the tables associated with this table because
+ * this table's rows may also be referenced by those tables using thistable_id or the provided
+ * foreign keys (this index name is stored in ar->foreignKey)
+ *
+ * this-table.other-table_id = other-table.id
+ */
+ function belongsTo($foreignRef,$foreignKey=false)
+ {
+ global $inflector;
+
+ $ar = new ADODB_Active_Record($this->_pluralize($foreignRef));
+ $ar->foreignName = $foreignRef;
+ $ar->UpdateActiveTable();
+ $ar->foreignKey = ($foreignKey) ? $foreignKey : $ar->foreignName . self::$_foreignSuffix;
+
+ $table =& $this->TableInfo();
+ if(!isset($table->_belongsTo[$foreignRef]))
+ {
+ $table->_belongsTo[$foreignRef] = $ar;
+ $table->updateColsCount();
+ }
+ $this->$foreignRef = $table->_belongsTo[$foreignRef];
+ }
+
+ /**
+ * __get Access properties - used for lazy loading
+ *
+ * @param mixed $name
+ * @access protected
+ * @return void
+ */
+ function __get($name)
+ {
+ return $this->LoadRelations($name, '', -1. -1);
+ }
+
+ function LoadRelations($name, $whereOrderBy, $offset=-1, $limit=-1)
+ {
+ $extras = array();
+ if($offset >= 0) $extras['offset'] = $offset;
+ if($limit >= 0) $extras['limit'] = $limit;
+ $table =& $this->TableInfo();
+
+ if (strlen($whereOrderBy))
+ if (!preg_match('/^[ \n\r]*AND/i',$whereOrderBy))
+ if (!preg_match('/^[ \n\r]*ORDER[ \n\r]/i',$whereOrderBy))
+ $whereOrderBy = 'AND '.$whereOrderBy;
+
+ if(!empty($table->_belongsTo[$name]))
+ {
+ $obj = $table->_belongsTo[$name];
+ $columnName = $obj->foreignKey;
+ if(empty($this->$columnName))
+ $this->$name = null;
+ else
+ {
+ if(($k = reset($obj->TableInfo()->keys)))
+ $belongsToId = $k;
+ else
+ $belongsToId = 'id';
+
+ $arrayOfOne =
+ $obj->Find(
+ $belongsToId.'='.$this->$columnName.' '.$whereOrderBy, false, false, $extras);
+ $this->$name = $arrayOfOne[0];
+ }
+ return $this->$name;
+ }
+ if(!empty($table->_hasMany[$name]))
+ {
+ $obj = $table->_hasMany[$name];
+ if(($k = reset($table->keys)))
+ $hasManyId = $k;
+ else
+ $hasManyId = 'id';
+
+ $this->$name =
+ $obj->Find(
+ $obj->foreignKey.'='.$this->$hasManyId.' '.$whereOrderBy, false, false, $extras);
+ return $this->$name;
+ }
+ }
+ //////////////////////////////////
+
+ // update metadata
+ function UpdateActiveTable($pkeys=false,$forceUpdate=false)
+ {
+ global $ADODB_ASSOC_CASE,$_ADODB_ACTIVE_DBS , $ADODB_CACHE_DIR, $ADODB_ACTIVE_CACHESECS;
+ global $ADODB_ACTIVE_DEFVALS, $ADODB_FETCH_MODE;
+
+ $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+
+ $table = $this->_table;
+ $tables = $activedb->tables;
+ $tableat = $this->_tableat;
+ if (!$forceUpdate && !empty($tables[$tableat])) {
+
+ $tobj = $tables[$tableat];
+ foreach($tobj->flds as $name => $fld) {
+ if ($ADODB_ACTIVE_DEFVALS && isset($fld->default_value))
+ $this->$name = $fld->default_value;
+ else
+ $this->$name = null;
+ }
+ return;
+ }
+
+ $db = $activedb->db;
+ $fname = $ADODB_CACHE_DIR . '/adodb_' . $db->databaseType . '_active_'. $table . '.cache';
+ if (!$forceUpdate && $ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR && file_exists($fname)) {
+ $fp = fopen($fname,'r');
+ @flock($fp, LOCK_SH);
+ $acttab = unserialize(fread($fp,100000));
+ fclose($fp);
+ if ($acttab->_created + $ADODB_ACTIVE_CACHESECS - (abs(rand()) % 16) > time()) {
+ // abs(rand()) randomizes deletion, reducing contention to delete/refresh file
+ // ideally, you should cache at least 32 secs
+ $activedb->tables[$table] = $acttab;
+
+ //if ($db->debug) ADOConnection::outp("Reading cached active record file: $fname");
+ return;
+ } else if ($db->debug) {
+ ADOConnection::outp("Refreshing cached active record file: $fname");
+ }
+ }
+ $activetab = new ADODB_Active_Table();
+ $activetab->name = $table;
+
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
+ if ($db->fetchMode !== false) $savem = $db->SetFetchMode(false);
+
+ $cols = $db->MetaColumns($table);
+
+ if (isset($savem)) $db->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+
+ if (!$cols) {
+ $this->Error("Invalid table name: $table",'UpdateActiveTable');
+ return false;
+ }
+ $fld = reset($cols);
+ if (!$pkeys) {
+ if (isset($fld->primary_key)) {
+ $pkeys = array();
+ foreach($cols as $name => $fld) {
+ if (!empty($fld->primary_key)) $pkeys[] = $name;
+ }
+ } else
+ $pkeys = $this->GetPrimaryKeys($db, $table);
+ }
+ if (empty($pkeys)) {
+ $this->Error("No primary key found for table $table",'UpdateActiveTable');
+ return false;
+ }
+
+ $attr = array();
+ $keys = array();
+
+ switch($ADODB_ASSOC_CASE) {
+ case 0:
+ foreach($cols as $name => $fldobj) {
+ $name = strtolower($name);
+ if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
+ $this->$name = $fldobj->default_value;
+ else
+ $this->$name = null;
+ $attr[$name] = $fldobj;
+ }
+ foreach($pkeys as $k => $name) {
+ $keys[strtolower($name)] = strtolower($name);
+ }
+ break;
+
+ case 1:
+ foreach($cols as $name => $fldobj) {
+ $name = strtoupper($name);
+
+ if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
+ $this->$name = $fldobj->default_value;
+ else
+ $this->$name = null;
+ $attr[$name] = $fldobj;
+ }
+
+ foreach($pkeys as $k => $name) {
+ $keys[strtoupper($name)] = strtoupper($name);
+ }
+ break;
+ default:
+ foreach($cols as $name => $fldobj) {
+ $name = ($fldobj->name);
+
+ if ($ADODB_ACTIVE_DEFVALS && isset($fldobj->default_value))
+ $this->$name = $fldobj->default_value;
+ else
+ $this->$name = null;
+ $attr[$name] = $fldobj;
+ }
+ foreach($pkeys as $k => $name) {
+ $keys[$name] = $cols[$name]->name;
+ }
+ break;
+ }
+
+ $activetab->keys = $keys;
+ $activetab->flds = $attr;
+ $activetab->updateColsCount();
+
+ if ($ADODB_ACTIVE_CACHESECS && $ADODB_CACHE_DIR) {
+ $activetab->_created = time();
+ $s = serialize($activetab);
+ if (!function_exists('adodb_write_file')) include(ADODB_DIR.'/adodb-csvlib.inc.php');
+ adodb_write_file($fname,$s);
+ }
+ if (isset($activedb->tables[$table])) {
+ $oldtab = $activedb->tables[$table];
+
+ if ($oldtab) $activetab->_belongsTo = $oldtab->_belongsTo;
+ if ($oldtab) $activetab->_hasMany = $oldtab->_hasMany;
+ }
+ $activedb->tables[$table] = $activetab;
+ }
+
+ function GetPrimaryKeys(&$db, $table)
+ {
+ return $db->MetaPrimaryKeys($table);
+ }
+
+ // error handler for both PHP4+5.
+ function Error($err,$fn)
+ {
+ global $_ADODB_ACTIVE_DBS;
+
+ $fn = get_class($this).'::'.$fn;
+ $this->_lasterr = $fn.': '.$err;
+
+ if ($this->_dbat < 0) $db = false;
+ else {
+ $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+ $db = $activedb->db;
+ }
+
+ if (function_exists('adodb_throw')) {
+ if (!$db) adodb_throw('ADOdb_Active_Record', $fn, -1, $err, 0, 0, false);
+ else adodb_throw($db->databaseType, $fn, -1, $err, 0, 0, $db);
+ } else
+ if (!$db || $db->debug) ADOConnection::outp($this->_lasterr);
+
+ }
+
+ // return last error message
+ function ErrorMsg()
+ {
+ if (!function_exists('adodb_throw')) {
+ if ($this->_dbat < 0) $db = false;
+ else $db = $this->DB();
+
+ // last error could be database error too
+ if ($db && $db->ErrorMsg()) return $db->ErrorMsg();
+ }
+ return $this->_lasterr;
+ }
+
+ function ErrorNo()
+ {
+ if ($this->_dbat < 0) return -9999; // no database connection...
+ $db = $this->DB();
+
+ return (int) $db->ErrorNo();
+ }
+
+
+ // retrieve ADOConnection from _ADODB_Active_DBs
+ function DB()
+ {
+ global $_ADODB_ACTIVE_DBS;
+
+ if ($this->_dbat < 0) {
+ $false = false;
+ $this->Error("No database connection set: use ADOdb_Active_Record::SetDatabaseAdaptor(\$db)", "DB");
+ return $false;
+ }
+ $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+ $db = $activedb->db;
+ return $db;
+ }
+
+ // retrieve ADODB_Active_Table
+ function &TableInfo()
+ {
+ global $_ADODB_ACTIVE_DBS;
+
+ $activedb = $_ADODB_ACTIVE_DBS[$this->_dbat];
+ $table = $activedb->tables[$this->_tableat];
+ return $table;
+ }
+
+
+ // I have an ON INSERT trigger on a table that sets other columns in the table.
+ // So, I find that for myTable, I want to reload an active record after saving it. -- Malcolm Cook
+ function Reload()
+ {
+ $db =& $this->DB(); if (!$db) return false;
+ $table =& $this->TableInfo();
+ $where = $this->GenWhere($db, $table);
+ return($this->Load($where));
+ }
+
+
+ // set a numeric array (using natural table field ordering) as object properties
+ function Set(&$row)
+ {
+ global $ACTIVE_RECORD_SAFETY;
+
+ $db = $this->DB();
+
+ if (!$row) {
+ $this->_saved = false;
+ return false;
+ }
+
+ $this->_saved = true;
+
+ $table = $this->TableInfo();
+ $sizeofFlds = sizeof($table->flds);
+ $sizeofRow = sizeof($row);
+ if ($ACTIVE_RECORD_SAFETY && $table->_colsCount != $sizeofRow && $sizeofFlds != $sizeofRow) {
+ # <AP>
+ $bad_size = TRUE;
+ if($sizeofRow == 2 * $table->_colsCount || $sizeofRow == 2 * $sizeofFlds) {
+ // Only keep string keys
+ $keys = array_filter(array_keys($row), 'is_string');
+ if (sizeof($keys) == sizeof($table->flds))
+ $bad_size = FALSE;
+ }
+ if ($bad_size) {
+ $this->Error("Table structure of $this->_table has changed","Load");
+ return false;
+ }
+ # </AP>
+ }
+ else
+ $keys = array_keys($row);
+ # <AP>
+ reset($keys);
+ $this->_original = array();
+ foreach($table->flds as $name=>$fld)
+ {
+ $value = $row[current($keys)];
+ $this->$name = $value;
+ $this->_original[] = $value;
+ if(!next($keys)) break;
+ }
+ $table =& $this->TableInfo();
+ foreach($table->_belongsTo as $foreignTable)
+ {
+ $ft = $foreignTable->TableInfo();
+ $propertyName = $ft->name;
+ foreach($ft->flds as $name=>$fld)
+ {
+ $value = $row[current($keys)];
+ $foreignTable->$name = $value;
+ $foreignTable->_original[] = $value;
+ if(!next($keys)) break;
+ }
+ }
+ foreach($table->_hasMany as $foreignTable)
+ {
+ $ft = $foreignTable->TableInfo();
+ foreach($ft->flds as $name=>$fld)
+ {
+ $value = $row[current($keys)];
+ $foreignTable->$name = $value;
+ $foreignTable->_original[] = $value;
+ if(!next($keys)) break;
+ }
+ }
+ # </AP>
+ return true;
+ }
+
+ // get last inserted id for INSERT
+ function LastInsertID(&$db,$fieldname)
+ {
+ if ($db->hasInsertID)
+ $val = $db->Insert_ID($this->_table,$fieldname);
+ else
+ $val = false;
+
+ if (is_null($val) || $val === false) {
+ // this might not work reliably in multi-user environment
+ return $db->GetOne("select max(".$fieldname.") from ".$this->_table);
+ }
+ return $val;
+ }
+
+ // quote data in where clause
+ function doquote(&$db, $val,$t)
+ {
+ switch($t) {
+ case 'D':
+ case 'T':
+ if (empty($val)) return 'null';
+
+ case 'C':
+ case 'X':
+ if (is_null($val)) return 'null';
+
+ if (strlen($val)>1 &&
+ (strncmp($val,"'",1) != 0 || substr($val,strlen($val)-1,1) != "'")) {
+ return $db->qstr($val);
+ break;
+ }
+ default:
+ return $val;
+ break;
+ }
+ }
+
+ // generate where clause for an UPDATE/SELECT
+ function GenWhere(&$db, &$table)
+ {
+ $keys = $table->keys;
+ $parr = array();
+
+ foreach($keys as $k) {
+ $f = $table->flds[$k];
+ if ($f) {
+ $parr[] = $k.' = '.$this->doquote($db,$this->$k,$db->MetaType($f->type));
+ }
+ }
+ return implode(' and ', $parr);
+ }
+
+
+ //------------------------------------------------------------ Public functions below
+
+ function Load($where=null,$bindarr=false)
+ {
+ $db = $this->DB(); if (!$db) return false;
+ $this->_where = $where;
+
+ $save = $db->SetFetchMode(ADODB_FETCH_NUM);
+ $qry = "select * from ".$this->_table;
+ $table =& $this->TableInfo();
+
+ if(($k = reset($table->keys)))
+ $hasManyId = $k;
+ else
+ $hasManyId = 'id';
+
+ foreach($table->_belongsTo as $foreignTable)
+ {
+ if(($k = reset($foreignTable->TableInfo()->keys)))
+ {
+ $belongsToId = $k;
+ }
+ else
+ {
+ $belongsToId = 'id';
+ }
+ $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+ $this->_table.'.'.$foreignTable->foreignKey.'='.
+ $foreignTable->_table.'.'.$belongsToId;
+ }
+ foreach($table->_hasMany as $foreignTable)
+ {
+ $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+ $this->_table.'.'.$hasManyId.'='.
+ $foreignTable->_table.'.'.$foreignTable->foreignKey;
+ }
+ if($where)
+ $qry .= ' WHERE '.$where;
+
+ // Simple case: no relations. Load row and return.
+ if((count($table->_hasMany) + count($table->_belongsTo)) < 1)
+ {
+ $row = $db->GetRow($qry,$bindarr);
+ if(!$row)
+ return false;
+ $db->SetFetchMode($save);
+ return $this->Set($row);
+ }
+
+ // More complex case when relations have to be collated
+ $rows = $db->GetAll($qry,$bindarr);
+ if(!$rows)
+ return false;
+ $db->SetFetchMode($save);
+ if(count($rows) < 1)
+ return false;
+ $class = get_class($this);
+ $isFirstRow = true;
+
+ if(($k = reset($this->TableInfo()->keys)))
+ $myId = $k;
+ else
+ $myId = 'id';
+ $index = 0; $found = false;
+ /** @todo Improve by storing once and for all in table metadata */
+ /** @todo Also re-use info for hasManyId */
+ foreach($this->TableInfo()->flds as $fld)
+ {
+ if($fld->name == $myId)
+ {
+ $found = true;
+ break;
+ }
+ $index++;
+ }
+ if(!$found)
+ $this->outp_throw("Unable to locate key $myId for $class in Load()",'Load');
+
+ foreach($rows as $row)
+ {
+ $rowId = intval($row[$index]);
+ if($rowId > 0)
+ {
+ if($isFirstRow)
+ {
+ $isFirstRow = false;
+ if(!$this->Set($row))
+ return false;
+ }
+ $obj = new $class($table,false,$db);
+ $obj->Set($row);
+ // TODO Copy/paste code below: bad!
+ if(count($table->_hasMany) > 0)
+ {
+ foreach($table->_hasMany as $foreignTable)
+ {
+ $foreignName = $foreignTable->foreignName;
+ if(!empty($obj->$foreignName))
+ {
+ if(!is_array($this->$foreignName))
+ {
+ $foreignObj = $this->$foreignName;
+ $this->$foreignName = array(clone($foreignObj));
+ }
+ else
+ {
+ $foreignObj = $obj->$foreignName;
+ array_push($this->$foreignName, clone($foreignObj));
+ }
+ }
+ }
+ }
+ if(count($table->_belongsTo) > 0)
+ {
+ foreach($table->_belongsTo as $foreignTable)
+ {
+ $foreignName = $foreignTable->foreignName;
+ if(!empty($obj->$foreignName))
+ {
+ if(!is_array($this->$foreignName))
+ {
+ $foreignObj = $this->$foreignName;
+ $this->$foreignName = array(clone($foreignObj));
+ }
+ else
+ {
+ $foreignObj = $obj->$foreignName;
+ array_push($this->$foreignName, clone($foreignObj));
+ }
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ // false on error
+ function Save()
+ {
+ if ($this->_saved) $ok = $this->Update();
+ else $ok = $this->Insert();
+
+ return $ok;
+ }
+
+ // CFR: Sometimes we may wish to consider that an object is not to be replaced but inserted.
+ // Sample use case: an 'undo' command object (after a delete())
+ function Dirty()
+ {
+ $this->_saved = false;
+ }
+
+ // false on error
+ function Insert()
+ {
+ $db = $this->DB(); if (!$db) return false;
+ $cnt = 0;
+ $table = $this->TableInfo();
+
+ $valarr = array();
+ $names = array();
+ $valstr = array();
+
+ foreach($table->flds as $name=>$fld) {
+ $val = $this->$name;
+ if(!is_null($val) || !array_key_exists($name, $table->keys)) {
+ $valarr[] = $val;
+ $names[] = $name;
+ $valstr[] = $db->Param($cnt);
+ $cnt += 1;
+ }
+ }
+
+ if (empty($names)){
+ foreach($table->flds as $name=>$fld) {
+ $valarr[] = null;
+ $names[] = $name;
+ $valstr[] = $db->Param($cnt);
+ $cnt += 1;
+ }
+ }
+ $sql = 'INSERT INTO '.$this->_table."(".implode(',',$names).') VALUES ('.implode(',',$valstr).')';
+ $ok = $db->Execute($sql,$valarr);
+
+ if ($ok) {
+ $this->_saved = true;
+ $autoinc = false;
+ foreach($table->keys as $k) {
+ if (is_null($this->$k)) {
+ $autoinc = true;
+ break;
+ }
+ }
+ if ($autoinc && sizeof($table->keys) == 1) {
+ $k = reset($table->keys);
+ $this->$k = $this->LastInsertID($db,$k);
+ }
+ }
+
+ $this->_original = $valarr;
+ return !empty($ok);
+ }
+
+ function Delete()
+ {
+ $db = $this->DB(); if (!$db) return false;
+ $table = $this->TableInfo();
+
+ $where = $this->GenWhere($db,$table);
+ $sql = 'DELETE FROM '.$this->_table.' WHERE '.$where;
+ $ok = $db->Execute($sql);
+
+ return $ok ? true : false;
+ }
+
+ // returns an array of active record objects
+ function Find($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
+ {
+ $db = $this->DB(); if (!$db || empty($this->_table)) return false;
+ $table =& $this->TableInfo();
+ $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra,
+ array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany));
+ return $arr;
+ }
+
+ // CFR: In introduced this method to ensure that inner workings are not disturbed by
+ // subclasses...for instance when GetActiveRecordsClass invokes Find()
+ // Why am I not invoking parent::Find?
+ // Shockingly because I want to preserve PHP4 compatibility.
+ function packageFind($whereOrderBy,$bindarr=false,$pkeysArr=false,$extra=array())
+ {
+ $db = $this->DB(); if (!$db || empty($this->_table)) return false;
+ $table =& $this->TableInfo();
+ $arr = $db->GetActiveRecordsClass(get_class($this),$this, $whereOrderBy,$bindarr,$pkeysArr,$extra,
+ array('foreignName'=>$this->foreignName, 'belongsTo'=>$table->_belongsTo, 'hasMany'=>$table->_hasMany));
+ return $arr;
+ }
+
+ // returns 0 on error, 1 on update, 2 on insert
+ function Replace()
+ {
+ global $ADODB_ASSOC_CASE;
+
+ $db = $this->DB(); if (!$db) return false;
+ $table = $this->TableInfo();
+
+ $pkey = $table->keys;
+
+ foreach($table->flds as $name=>$fld) {
+ $val = $this->$name;
+ /*
+ if (is_null($val)) {
+ if (isset($fld->not_null) && $fld->not_null) {
+ if (isset($fld->default_value) && strlen($fld->default_value)) continue;
+ else {
+ $this->Error("Cannot update null into $name","Replace");
+ return false;
+ }
+ }
+ }*/
+ if (is_null($val) && !empty($fld->auto_increment)) {
+ continue;
+ }
+ $t = $db->MetaType($fld->type);
+ $arr[$name] = $this->doquote($db,$val,$t);
+ $valarr[] = $val;
+ }
+
+ if (!is_array($pkey)) $pkey = array($pkey);
+
+
+ if ($ADODB_ASSOC_CASE == 0)
+ foreach($pkey as $k => $v)
+ $pkey[$k] = strtolower($v);
+ elseif ($ADODB_ASSOC_CASE == 1)
+ foreach($pkey as $k => $v)
+ $pkey[$k] = strtoupper($v);
+
+ $ok = $db->Replace($this->_table,$arr,$pkey);
+ if ($ok) {
+ $this->_saved = true; // 1= update 2=insert
+ if ($ok == 2) {
+ $autoinc = false;
+ foreach($table->keys as $k) {
+ if (is_null($this->$k)) {
+ $autoinc = true;
+ break;
+ }
+ }
+ if ($autoinc && sizeof($table->keys) == 1) {
+ $k = reset($table->keys);
+ $this->$k = $this->LastInsertID($db,$k);
+ }
+ }
+
+ $this->_original = $valarr;
+ }
+ return $ok;
+ }
+
+ // returns 0 on error, 1 on update, -1 if no change in data (no update)
+ function Update()
+ {
+ $db = $this->DB(); if (!$db) return false;
+ $table = $this->TableInfo();
+
+ $where = $this->GenWhere($db, $table);
+
+ if (!$where) {
+ $this->error("Where missing for table $table", "Update");
+ return false;
+ }
+ $valarr = array();
+ $neworig = array();
+ $pairs = array();
+ $i = -1;
+ $cnt = 0;
+ foreach($table->flds as $name=>$fld) {
+ $i += 1;
+ $val = $this->$name;
+ $neworig[] = $val;
+
+ if (isset($table->keys[$name])) {
+ continue;
+ }
+
+ if (is_null($val)) {
+ if (isset($fld->not_null) && $fld->not_null) {
+ if (isset($fld->default_value) && strlen($fld->default_value)) continue;
+ else {
+ $this->Error("Cannot set field $name to NULL","Update");
+ return false;
+ }
+ }
+ }
+
+ if (isset($this->_original[$i]) && $val == $this->_original[$i]) {
+ continue;
+ }
+ $valarr[] = $val;
+ $pairs[] = $name.'='.$db->Param($cnt);
+ $cnt += 1;
+ }
+
+
+ if (!$cnt) return -1;
+ $sql = 'UPDATE '.$this->_table." SET ".implode(",",$pairs)." WHERE ".$where;
+ $ok = $db->Execute($sql,$valarr);
+ if ($ok) {
+ $this->_original = $neworig;
+ return 1;
+ }
+ return 0;
+ }
+
+ function GetAttributeNames()
+ {
+ $table = $this->TableInfo();
+ if (!$table) return false;
+ return array_keys($table->flds);
+ }
+
+};
+
+function adodb_GetActiveRecordsClass(&$db, $class, $tableObj,$whereOrderBy,$bindarr, $primkeyArr,
+ $extra, $relations)
+{
+ global $_ADODB_ACTIVE_DBS;
+
+ if (empty($extra['loading'])) $extra['loading'] = ADODB_LAZY_AR;
+
+ $save = $db->SetFetchMode(ADODB_FETCH_NUM);
+ $table = &$tableObj->_table;
+ $tableInfo =& $tableObj->TableInfo();
+ if(($k = reset($tableInfo->keys)))
+ $myId = $k;
+ else
+ $myId = 'id';
+ $index = 0; $found = false;
+ /** @todo Improve by storing once and for all in table metadata */
+ /** @todo Also re-use info for hasManyId */
+ foreach($tableInfo->flds as $fld)
+ {
+ if($fld->name == $myId)
+ {
+ $found = true;
+ break;
+ }
+ $index++;
+ }
+ if(!$found)
+ $db->outp_throw("Unable to locate key $myId for $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
+
+ $qry = "select * from ".$table;
+ if(ADODB_JOIN_AR == $extra['loading'])
+ {
+ if(!empty($relations['belongsTo']))
+ {
+ foreach($relations['belongsTo'] as $foreignTable)
+ {
+ if(($k = reset($foreignTable->TableInfo()->keys)))
+ {
+ $belongsToId = $k;
+ }
+ else
+ {
+ $belongsToId = 'id';
+ }
+
+ $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+ $table.'.'.$foreignTable->foreignKey.'='.
+ $foreignTable->_table.'.'.$belongsToId;
+ }
+ }
+ if(!empty($relations['hasMany']))
+ {
+ if(empty($relations['foreignName']))
+ $db->outp_throw("Missing foreignName is relation specification in GetActiveRecordsClass()",'GetActiveRecordsClass');
+ if(($k = reset($tableInfo->keys)))
+ $hasManyId = $k;
+ else
+ $hasManyId = 'id';
+
+ foreach($relations['hasMany'] as $foreignTable)
+ {
+ $qry .= ' LEFT JOIN '.$foreignTable->_table.' ON '.
+ $table.'.'.$hasManyId.'='.
+ $foreignTable->_table.'.'.$foreignTable->foreignKey;
+ }
+ }
+ }
+ if (!empty($whereOrderBy))
+ $qry .= ' WHERE '.$whereOrderBy;
+ if(isset($extra['limit']))
+ {
+ $rows = false;
+ if(isset($extra['offset'])) {
+ $rs = $db->SelectLimit($qry, $extra['limit'], $extra['offset']);
+ } else {
+ $rs = $db->SelectLimit($qry, $extra['limit']);
+ }
+ if ($rs) {
+ while (!$rs->EOF) {
+ $rows[] = $rs->fields;
+ $rs->MoveNext();
+ }
+ }
+ } else
+ $rows = $db->GetAll($qry,$bindarr);
+
+ $db->SetFetchMode($save);
+
+ $false = false;
+
+ if ($rows === false) {
+ return $false;
+ }
+
+
+ if (!isset($_ADODB_ACTIVE_DBS)) {
+ include(ADODB_DIR.'/adodb-active-record.inc.php');
+ }
+ if (!class_exists($class)) {
+ $db->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
+ return $false;
+ }
+ $uniqArr = array(); // CFR Keep track of records for relations
+ $arr = array();
+ // arrRef will be the structure that knows about our objects.
+ // It is an associative array.
+ // We will, however, return arr, preserving regular 0.. order so that
+ // obj[0] can be used by app developpers.
+ $arrRef = array();
+ $bTos = array(); // Will store belongTo's indices if any
+ foreach($rows as $row) {
+
+ $obj = new $class($table,$primkeyArr,$db);
+ if ($obj->ErrorNo()){
+ $db->_errorMsg = $obj->ErrorMsg();
+ return $false;
+ }
+ $obj->Set($row);
+ // CFR: FIXME: Insane assumption here:
+ // If the first column returned is an integer, then it's a 'id' field
+ // And to make things a bit worse, I use intval() rather than is_int() because, in fact,
+ // $row[0] is not an integer.
+ //
+ // So, what does this whole block do?
+ // When relationships are found, we perform JOINs. This is fast. But not accurate:
+ // instead of returning n objects with their n' associated cousins,
+ // we get n*n' objects. This code fixes this.
+ // Note: to-many relationships mess around with the 'limit' parameter
+ $rowId = intval($row[$index]);
+
+ if(ADODB_WORK_AR == $extra['loading'])
+ {
+ $arrRef[$rowId] = $obj;
+ $arr[] = &$arrRef[$rowId];
+ if(!isset($indices))
+ $indices = $rowId;
+ else
+ $indices .= ','.$rowId;
+ if(!empty($relations['belongsTo']))
+ {
+ foreach($relations['belongsTo'] as $foreignTable)
+ {
+ $foreignTableRef = $foreignTable->foreignKey;
+ // First array: list of foreign ids we are looking for
+ if(empty($bTos[$foreignTableRef]))
+ $bTos[$foreignTableRef] = array();
+ // Second array: list of ids found
+ if(empty($obj->$foreignTableRef))
+ continue;
+ if(empty($bTos[$foreignTableRef][$obj->$foreignTableRef]))
+ $bTos[$foreignTableRef][$obj->$foreignTableRef] = array();
+ $bTos[$foreignTableRef][$obj->$foreignTableRef][] = $obj;
+ }
+ }
+ continue;
+ }
+
+ if($rowId>0)
+ {
+ if(ADODB_JOIN_AR == $extra['loading'])
+ {
+ $isNewObj = !isset($uniqArr['_'.$row[0]]);
+ if($isNewObj)
+ $uniqArr['_'.$row[0]] = $obj;
+
+ // TODO Copy/paste code below: bad!
+ if(!empty($relations['hasMany']))
+ {
+ foreach($relations['hasMany'] as $foreignTable)
+ {
+ $foreignName = $foreignTable->foreignName;
+ if(!empty($obj->$foreignName))
+ {
+ $masterObj = &$uniqArr['_'.$row[0]];
+ // Assumption: this property exists in every object since they are instances of the same class
+ if(!is_array($masterObj->$foreignName))
+ {
+ // Pluck!
+ $foreignObj = $masterObj->$foreignName;
+ $masterObj->$foreignName = array(clone($foreignObj));
+ }
+ else
+ {
+ // Pluck pluck!
+ $foreignObj = $obj->$foreignName;
+ array_push($masterObj->$foreignName, clone($foreignObj));
+ }
+ }
+ }
+ }
+ if(!empty($relations['belongsTo']))
+ {
+ foreach($relations['belongsTo'] as $foreignTable)
+ {
+ $foreignName = $foreignTable->foreignName;
+ if(!empty($obj->$foreignName))
+ {
+ $masterObj = &$uniqArr['_'.$row[0]];
+ // Assumption: this property exists in every object since they are instances of the same class
+ if(!is_array($masterObj->$foreignName))
+ {
+ // Pluck!
+ $foreignObj = $masterObj->$foreignName;
+ $masterObj->$foreignName = array(clone($foreignObj));
+ }
+ else
+ {
+ // Pluck pluck!
+ $foreignObj = $obj->$foreignName;
+ array_push($masterObj->$foreignName, clone($foreignObj));
+ }
+ }
+ }
+ }
+ if(!$isNewObj)
+ unset($obj); // We do not need this object itself anymore and do not want it re-added to the main array
+ }
+ else if(ADODB_LAZY_AR == $extra['loading'])
+ {
+ // Lazy loading: we need to give AdoDb a hint that we have not really loaded
+ // anything, all the while keeping enough information on what we wish to load.
+ // Let's do this by keeping the relevant info in our relationship arrays
+ // but get rid of the actual properties.
+ // We will then use PHP's __get to load these properties on-demand.
+ if(!empty($relations['hasMany']))
+ {
+ foreach($relations['hasMany'] as $foreignTable)
+ {
+ $foreignName = $foreignTable->foreignName;
+ if(!empty($obj->$foreignName))
+ {
+ unset($obj->$foreignName);
+ }
+ }
+ }
+ if(!empty($relations['belongsTo']))
+ {
+ foreach($relations['belongsTo'] as $foreignTable)
+ {
+ $foreignName = $foreignTable->foreignName;
+ if(!empty($obj->$foreignName))
+ {
+ unset($obj->$foreignName);
+ }
+ }
+ }
+ }
+ }
+
+ if(isset($obj))
+ $arr[] = $obj;
+ }
+
+ if(ADODB_WORK_AR == $extra['loading'])
+ {
+ // The best of both worlds?
+ // Here, the number of queries is constant: 1 + n*relationship.
+ // The second query will allow us to perform a good join
+ // while preserving LIMIT etc.
+ if(!empty($relations['hasMany']))
+ {
+ foreach($relations['hasMany'] as $foreignTable)
+ {
+ $foreignName = $foreignTable->foreignName;
+ $className = ucfirst($foreignTable->_singularize($foreignName));
+ $obj = new $className();
+ $dbClassRef = $foreignTable->foreignKey;
+ $objs = $obj->packageFind($dbClassRef.' IN ('.$indices.')');
+ foreach($objs as $obj)
+ {
+ if(!is_array($arrRef[$obj->$dbClassRef]->$foreignName))
+ $arrRef[$obj->$dbClassRef]->$foreignName = array();
+ array_push($arrRef[$obj->$dbClassRef]->$foreignName, $obj);
+ }
+ }
+
+ }
+ if(!empty($relations['belongsTo']))
+ {
+ foreach($relations['belongsTo'] as $foreignTable)
+ {
+ $foreignTableRef = $foreignTable->foreignKey;
+ if(empty($bTos[$foreignTableRef]))
+ continue;
+ if(($k = reset($foreignTable->TableInfo()->keys)))
+ {
+ $belongsToId = $k;
+ }
+ else
+ {
+ $belongsToId = 'id';
+ }
+ $origObjsArr = $bTos[$foreignTableRef];
+ $bTosString = implode(',', array_keys($bTos[$foreignTableRef]));
+ $foreignName = $foreignTable->foreignName;
+ $className = ucfirst($foreignTable->_singularize($foreignName));
+ $obj = new $className();
+ $objs = $obj->packageFind($belongsToId.' IN ('.$bTosString.')');
+ foreach($objs as $obj)
+ {
+ foreach($origObjsArr[$obj->$belongsToId] as $idx=>$origObj)
+ {
+ $origObj->$foreignName = $obj;
+ }
+ }
+ }
+ }
+ }
+
+ return $arr;
+}
+?>
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
fclose($fd);
if ($ok) {
- chmod($tmpname,0644);
+ @chmod($tmpname,0644);
// the tricky moment
@unlink($filename);
if (!@rename($tmpname,$filename)) {
if (fwrite( $fd, $contents )) $ok = true;
else $ok = false;
fclose($fd);
- chmod($filename,0644);
+ @chmod($filename,0644);
}else {
fclose($fd);
if ($debug)ADOConnection::outp( " Failed acquiring lock for $filename<br>\n");
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
'CHARACTER' => 'C',
'INTERVAL' => 'C', # Postgres
'MACADDR' => 'C', # postgres
+ 'VAR_STRING' => 'C', # mysql
##
'LONGCHAR' => 'X',
'TEXT' => 'X',
'TIMESTAMP' => 'T',
'DATETIME' => 'T',
'TIMESTAMPTZ' => 'T',
+ 'SMALLDATETIME' => 'T',
'T' => 'T',
'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
##
// return string must begin with space
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
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.
*/
- function ChangeTableSQL($tablename, $flds, $tableoptions = false)
+ function ChangeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
{
global $ADODB_FETCH_MODE;
$obj = $cols[$k];
if (isset($obj->not_null) && $obj->not_null)
$v = str_replace('NOT NULL','',$v);
-
+ if (isset($obj->auto_increment) && $obj->auto_increment && empty($v['AUTOINCREMENT']))
+ $v = str_replace('AUTOINCREMENT','',$v);
+
$c = $cols[$k];
$ml = $c->max_length;
$mt = $this->MetaType($c->type,$ml);
if ($ml == -1) $ml = '';
if ($mt == 'X') $ml = $v['SIZE'];
- if (($mt != $v['TYPE']) || $ml != $v['SIZE']) {
+ if (($mt != $v['TYPE']) || $ml != $v['SIZE'] || (isset($v['AUTOINCREMENT']) && $v['AUTOINCREMENT'] != $obj->auto_increment)) {
$holdflds[$k] = $v;
}
} else {
}
}
+ if ($dropOldFlds) {
+ foreach ( $cols as $id => $v )
+ if ( !isset($lines[$id]) )
+ $sql[] = $alter . $this->dropCol . ' ' . $v->name;
+ }
return $sql;
}
} // class
<?php
/**
- * @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/**
- * @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/**
- * @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/**
- * @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
$ADODB_INCLUDED_LIB = 1;
/*
- @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim\@natsoft.com.my). All rights reserved.
+ @version V5.06 16 Oct 2008 (c) 2000-2009 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. See License.txt.
if ($v === null) {
$v = 'NULL';
$fieldArray[$k] = $v;
- } else if ($autoQuote && !is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ and strcasecmp($v,$zthis->null2null)!=0) {
+ } else if ($autoQuote && /*!is_numeric($v) /*and strncmp($v,"'",1) !== 0 -- sql injection risk*/ strcasecmp($v,$zthis->null2null)!=0) {
$v = $zthis->qstr($v);
$fieldArray[$k] = $v;
}
} else
$uSet .= ",$k=$v";
}
-
+
$where = false;
foreach ($keyCol as $v) {
if (isset($fieldArray[$v])) {
return $rsreturn;
}
-// Iv�n Oliva version
+// Iván Oliva version
function _adodb_pageexecute_no_last_page(&$zthis, $sql, $nrows, $page, $inputarr=false, $secs2cache=0)
{
case "T":
$val = $zthis->DBTimeStamp($arrFields[$fname]);
- break;
-
- case "F": //Floating point number // Moodle added
+ break;
+
+ case "F": //Floating point number // Moodle added
case "N":
$val = $arrFields[$fname];
if (!is_numeric($val)) $val = str_replace(',', '.', (float)$val);
break;
- case "L": //Integer field suitable for storing booleans (0 or 1) // Moodle added
+ case "L": //Integer field suitable for storing booleans (0 or 1) // Moodle added
case "I":
case "R":
$val = $arrFields[$fname];
$ss = '<code>'.htmlspecialchars($ss).'</code>';
}
if ($zthis->debug === -1)
- ADOConnection::outp( "<br />\n($dbt): ".htmlspecialchars($sqlTxt)." $ss\n<br />\n",false);
- else
- ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." $ss\n<hr />\n",false);
+ ADOConnection::outp( "<br />\n($dbt): ".htmlspecialchars($sqlTxt)." $ss\n<br />\n",false); /// Moodle XHTML
+ else if ($zthis->debug !== -99)
+ ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." $ss\n<hr />\n",false); /// Moodle XHTML
} else {
- ADOConnection::outp("-----\n($dbt): ".$sqlTxt."\n-----\n",false);
+ $ss = "\n ".$ss;
+ if ($zthis->debug !== -99)
+ ADOConnection::outp("-----<hr />\n($dbt): ".$sqlTxt." $ss\n-----<hr />\n",false); /// Moodle XHTML
}
$qID = $zthis->_query($sql,$inputarr);
*/
if ($zthis->databaseType == 'mssql') {
// ErrorNo is a slow function call in mssql, and not reliable in PHP 4.0.6
+
if($emsg = $zthis->ErrorMsg()) {
- if ($err = $zthis->ErrorNo()) ADOConnection::outp($err.': '.$emsg);
+ if ($err = $zthis->ErrorNo()) {
+ if ($zthis->debug === -99)
+ ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." $ss\n<hr />\n",false); /// Moodle XHTML
+
+ ADOConnection::outp($err.': '.$emsg);
+ }
}
} else if (!$qID) {
+
+ if ($zthis->debug === -99)
+ if ($inBrowser) ADOConnection::outp( "<hr />\n($dbt): ".htmlspecialchars($sqlTxt)." $ss\n<hr />\n",false); /// Moodle XHTML
+ else ADOConnection::outp("-----<hr />\n($dbt): ".$sqlTxt."$ss\n-----<hr />\n",false);
+
ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg());
}
}
# pretty print the debug_backtrace function
-function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0)
+function _adodb_backtrace($printOrArr=true,$levels=9999,$skippy=0,$ishtml=null)
{
if (!function_exists('debug_backtrace')) return '';
- $html = (isset($_SERVER['HTTP_USER_AGENT']));
-// moodle change start - see readme_moodle.txt
- $fmt = ($html) ? "</font><font color=\"#808080\" size=\"-1\"> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>" : "%% line %4d, file: %s";
-// moodle change end
+ if ($ishtml === null) $html = (isset($_SERVER['HTTP_USER_AGENT']));
+ else $html = $ishtml;
+
+ $fmt = ($html) ? "</font><font color=#808080 size=-1> %% line %4d, file: <a href=\"file:/%s\">%s</a></font>" : "%% line %4d, file: %s";
$MAXSTRLEN = 128;
-// moodle change start - see readme_moodle.txt
- $s = ($html) ? '<pre align="left">' : '';
-// moodle change end
+ $s = ($html) ? '<pre align=left>' : '';
if (is_array($printOrArr)) $traceArr = $printOrArr;
else $traceArr = debug_backtrace();
else if (is_bool($v)) $args[] = $v ? 'true' : 'false';
else {
$v = (string) @$v;
- $str = htmlspecialchars(substr($v,0,$MAXSTRLEN));
+ $str = htmlspecialchars(str_replace(array("\r","\n"),' ',substr($v,0,$MAXSTRLEN)));
if (strlen($v) > $MAXSTRLEN) $str .= '...';
$args[] = $str;
}
}
*/
-?>
\ No newline at end of file
+?>
global $ADODB_INCLUDED_MEMCACHE;
$ADODB_INCLUDED_MEMCACHE = 1;
+global $ADODB_INCLUDED_CSV;
+if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
+
/*
- V4.90 8 June 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
Set tabs to 4 for best viewing.
Latest version is available at http://adodb.sourceforge.net
+
+Usage:
+
+$db = NewADOConnection($driver);
+$db->memCache = true; /// should we use memCache instead of caching in files
+$db->memCacheHost = array($ip1, $ip2, $ip3);
+$db->memCachePort = 11211; /// this is default memCache port
+$db->memCacheCompress = false; /// Use 'true' to store the item compressed (uses zlib)
+
+$db->Connect(...);
+$db->CacheExecute($sql);
+
+ Note the memcache class is shared by all connections, is created during the first call to Connect/PConnect.
+ Class instance is stored in $ADODB_CACHE
*/
- function getmemcache($key,&$err, $timeout=0, $host, $port)
- {
- $false = false;
- $err = false;
-
- if (!function_exists('memcache_pconnect')) {
- $err = 'Memcache module PECL extension not found!';
- return $false;
+ class ADODB_Cache_MemCache {
+ var $createdir = false; // create caching directory structure?
+
+ //-----------------------------
+ // memcache specific variables
+
+ var $hosts; // array of hosts
+ var $port = 11211;
+ var $compress = false; // memcache compression with zlib
+
+ var $_connected = false;
+ var $_memcache = false;
+
+ function ADODB_Cache_MemCache(&$obj)
+ {
+ $this->hosts = $obj->memCacheHost;
+ $this->port = $obj->memCachePort;
+ $this->compress = $obj->memCacheCompress;
}
+
+ // implement as lazy connection. The connection only occurs on CacheExecute call
+ function connect(&$err)
+ {
+ if (!function_exists('memcache_pconnect')) {
+ $err = 'Memcache module PECL extension not found!';
+ return false;
+ }
- $memcache = new Memcache;
- if (!@$memcache->pconnect($host, $port)) {
- $err = 'Can\'t connect to memcache server on: '.$host.':'.$port;
- return $false;
+ $memcache = new MemCache;
+
+ if (!is_array($this->hosts)) $this->hosts = array($hosts);
+
+ $failcnt = 0;
+ foreach($this->hosts as $host) {
+ if (!@$memcache->addServer($host,$this->port,true)) {
+ $failcnt += 1;
+ }
+ }
+ if ($failcnt == sizeof($this->hosts)) {
+ $err = 'Can\'t connect to any memcache server';
+ return false;
+ }
+ $this->_connected = true;
+ $this->_memcache = $memcache;
+ return true;
}
-
- $rs = $memcache->get($key);
- if (!$rs) {
- $err = 'Item with such key doesn\'t exists on the memcached server.';
- return $false;
+
+ // returns true or false. true if successful save
+ function writecache($filename, $contents, $debug, $secs2cache)
+ {
+ if (!$this->_connected) {
+ $err = '';
+ if (!$this->connect($err) && $debug) ADOConnection::outp($err);
+ }
+ if (!$this->_memcache) return false;
+
+ if (!$this->_memcache->set($filename, $contents, $this->compress, $secs2cache)) {
+ if ($debug) ADOConnection::outp(" Failed to save data at the memcached server!<br>\n");
+ return false;
+ }
+
+ return true;
}
-
- $tdiff = intval($rs->timeCreated+$timeout - time());
- if ($tdiff <= 2) {
- switch($tdiff) {
- case 2:
- if ((rand() & 15) == 0) {
- $err = "Timeout 2";
- return $false;
- }
- break;
- case 1:
- if ((rand() & 3) == 0) {
- $err = "Timeout 1";
+
+ // returns a recordset
+ function readcache($filename, &$err, $secs2cache, $rsClass)
+ {
+ $false = false;
+ if (!$this->_connected) $this->connect($err);
+ if (!$this->_memcache) return $false;
+
+ $rs = $this->_memcache->get($filename);
+ if (!$rs) {
+ $err = 'Item with such key doesn\'t exists on the memcached server.';
+ return $false;
+ }
+
+ // hack, should actually use _csv2rs
+ $rs = explode("\n", $rs);
+ unset($rs[0]);
+ $rs = join("\n", $rs);
+ $rs = unserialize($rs);
+ if (! is_object($rs)) {
+ $err = 'Unable to unserialize $rs';
+ return $false;
+ }
+ if ($rs->timeCreated == 0) return $rs; // apparently have been reports that timeCreated was set to 0 somewhere
+
+ $tdiff = intval($rs->timeCreated+$secs2cache - time());
+ if ($tdiff <= 2) {
+ switch($tdiff) {
+ case 2:
+ if ((rand() & 15) == 0) {
+ $err = "Timeout 2";
+ return $false;
+ }
+ break;
+ case 1:
+ if ((rand() & 3) == 0) {
+ $err = "Timeout 1";
+ return $false;
+ }
+ break;
+ default:
+ $err = "Timeout 0";
return $false;
- }
- break;
- default:
- $err = "Timeout 0";
- return $false;
+ }
}
+ return $rs;
}
- return $rs;
- }
-
- function putmemcache($key, $rs, $host, $port, $compress, $debug=false)
- {
- $false = false;
- $true = true;
-
- if (!function_exists('memcache_pconnect')) {
- if ($debug) ADOConnection::outp(" Memcache module PECL extension not found!<br>\n");
- return $false;
+
+ function flushall($debug=false)
+ {
+ if (!$this->_connected) {
+ $err = '';
+ if (!$this->connect($err) && $debug) ADOConnection::outp($err);
+ }
+ if (!$this->_memcache) return false;
+
+ $del = $this->_memcache->flush();
+
+ if ($debug)
+ if (!$del) ADOConnection::outp("flushall: failed!<br>\n");
+ else ADOConnection::outp("flushall: succeeded!<br>\n");
+
+ return $del;
}
-
- $memcache = new Memcache;
- if (!@$memcache->pconnect($host, $port)) {
- if ($debug) ADOConnection::outp(" Can't connect to memcache server on: $host:$port<br>\n");
- return $false;
+
+ function flushcache($filename, $debug=false)
+ {
+ if (!$this->_connected) {
+ $err = '';
+ if (!$this->connect($err) && $debug) ADOConnection::outp($err);
+ }
+ if (!$this->_memcache) return false;
+
+ $del = $this->_memcache->delete($filename);
+
+ if ($debug)
+ if (!$del) ADOConnection::outp("flushcache: $key entry doesn't exist on memcached server!<br>\n");
+ else ADOConnection::outp("flushcache: $key entry flushed from memcached server!<br>\n");
+
+ return $del;
}
-
- $rs->timeCreated = time();
- if (!$memcache->set($key, $rs, $compress, 0)) {
- if ($debug) ADOConnection::outp(" Failed to save data at the memcached server!<br>\n");
- return $false;
+
+ // not used for memcache
+ function createdir($dir, $hash)
+ {
+ return true;
}
- return $true;
}
- function flushmemcache($key=false, $host, $port, $debug=false)
- {
- if (!function_exists('memcache_pconnect')) {
- if ($debug) ADOConnection::outp(" Memcache module PECL extension not found!<br>\n");
- return;
- }
-
- $memcache = new Memcache;
- if (!@$memcache->pconnect($host, $port)) {
- if ($debug) ADOConnection::outp(" Can't connect to memcache server on: $host:$port<br>\n");
- return;
- }
-
- if ($key) {
- if (!$memcache->delete($key)) {
- if ($debug) ADOConnection::outp("CacheFlush: $key entery doesn't exist on memcached server!<br>\n");
- } else {
- if ($debug) ADOConnection::outp("CacheFlush: $key entery flushed from memcached server!<br>\n");
- }
- } else {
- if (!$memcache->flush()) {
- if ($debug) ADOConnection::outp("CacheFlush: Failure flushing all enteries from memcached server!<br>\n");
- } else {
- if ($debug) ADOConnection::outp("CacheFlush: All enteries flushed from memcached server!<br>\n");
- }
- }
- return;
- }
-?>
+?>
\ No newline at end of file
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
global $PHP_SELF;
$curr_page = $id.'_curr_page';
- if (empty($PHP_SELF)) $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']); // htmlspecialchars() to prevent XSS attacks
+ if (!empty($PHP_SELF)) $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']); // htmlspecialchars() to prevent XSS attacks
$this->sql = $sql;
$this->id = $id;
<?php
/**
- * @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
function isError($value)
{
if (!is_object($value)) return false;
- $class = get_class($value);
+ $class = strtolower(get_class($value));
return $class == 'pear_error' || is_subclass_of($value, 'pear_error') ||
$class == 'db_error' || is_subclass_of($value, 'db_error');
}
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
{
$perf_table = adodb_perf::table();
$connx->fnExecute = false;
- $t0 = microtime();
+ $a0 = microtime(true);
$rs = $connx->Execute($sql,$inputarr);
- $t1 = microtime();
+ $a1 = microtime(true);
if (!empty($connx->_logsql) && (empty($connx->_logsqlErrors) || !$rs)) {
global $ADODB_LOG_CONN;
$conn->_logsql = false; // disable logsql error simulation
$dbT = $conn->databaseType;
- $a0 = split(' ',$t0);
- $a0 = (float)$a0[1]+(float)$a0[0];
-
- $a1 = split(' ',$t1);
- $a1 = (float)$a1[1]+(float)$a1[0];
-
$time = $a1 - $a0;
if (!$rs) {
}
if (isset($_SERVER['HTTP_HOST'])) {
$tracer .= '<br>'.$_SERVER['HTTP_HOST'];
- if (isset($_SERVER['PHP_SELF'])) $tracer .= $_SERVER['PHP_SELF'];
+ if (isset($_SERVER['PHP_SELF'])) $tracer .= htmlspecialchars($_SERVER['PHP_SELF']);
} else
- if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.$_SERVER['PHP_SELF'];
+ if (isset($_SERVER['PHP_SELF'])) $tracer .= '<br>'.htmlspecialchars($_SERVER['PHP_SELF']);
//$tracer .= (string) adodb_backtrace(false);
$tracer = (string) substr($tracer,0,500);
*/
// Algorithm is taken from
- // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/example__obtaining_raw_performance_data.asp
+ // http://social.technet.microsoft.com/Forums/en-US/winservergen/thread/414b0e1b-499c-411e-8a02-6a12e339c0f1/
if (strncmp(PHP_OS,'WIN',3)==0) {
if (PHP_VERSION == '5.0.0') return false;
if (PHP_VERSION == '5.0.1') return false;
if (PHP_VERSION == '5.0.3') return false;
if (PHP_VERSION == '4.3.10') return false; # see http://bugs.php.net/bug.php?id=31737
- @$c = new COM("WinMgmts:{impersonationLevel=impersonate}!Win32_PerfRawData_PerfOS_Processor.Name='_Total'");
- if (!$c) return false;
+ static $FAIL = false;
+ if ($FAIL) return false;
+
+ $objName = "winmgmts:{impersonationLevel=impersonate}!\\\\.\\root\\CIMV2";
+ $myQuery = "SELECT * FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name = '_Total'";
+
+ try {
+ @$objWMIService = new COM($objName);
+ if (!$objWMIService) {
+ $FAIL = true;
+ return false;
+ }
+
+ $info[0] = -1;
+ $info[1] = 0;
+ $info[2] = 0;
+ $info[3] = 0;
+ foreach($objWMIService->ExecQuery($myQuery) as $objItem) {
+ $info[0] = $objItem->PercentProcessorTime();
+ }
+
+ } catch(Exception $e) {
+ $FAIL = true;
+ echo $e->getMessage();
+ return false;
+ }
- $info[0] = $c->PercentProcessorTime;
- $info[1] = 0;
- $info[2] = 0;
- $info[3] = $c->TimeStamp_Sys100NS;
- //print_r($info);
return $info;
}
{
$info = $this->_CPULoad();
if (!$info) return false;
-
- if (empty($this->_lastLoad)) {
- sleep(1);
- $this->_lastLoad = $info;
- $info = $this->_CPULoad();
- }
-
- $last = $this->_lastLoad;
- $this->_lastLoad = $info;
-
- $d_user = $info[0] - $last[0];
- $d_nice = $info[1] - $last[1];
- $d_system = $info[2] - $last[2];
- $d_idle = $info[3] - $last[3];
- //printf("Delta - User: %f Nice: %f System: %f Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);
-
if (strncmp(PHP_OS,'WIN',3)==0) {
- if ($d_idle < 1) $d_idle = 1;
- return 100*(1-$d_user/$d_idle);
+ return (integer) $info[0];
}else {
+ if (empty($this->_lastLoad)) {
+ sleep(1);
+ $this->_lastLoad = $info;
+ $info = $this->_CPULoad();
+ }
+
+ $last = $this->_lastLoad;
+ $this->_lastLoad = $info;
+
+ $d_user = $info[0] - $last[0];
+ $d_nice = $info[1] - $last[1];
+ $d_system = $info[2] - $last[2];
+ $d_idle = $info[3] - $last[3];
+
+ //printf("Delta - User: %f Nice: %f System: %f Idle: %f<br>",$d_user,$d_nice,$d_system,$d_idle);
+
$total=$d_user+$d_nice+$d_system+$d_idle;
if ($total<1) $total=1;
return 100*($d_user+$d_nice+$d_system)/$total;
echo $this->CheckMemory();
break;
case 'poll':
+ $self = htmlspecialchars($_SERVER['PHP_SELF']);
echo "<iframe width=720 height=80%
- src=\"{$_SERVER['PHP_SELF']}?do=poll2&hidem=1\"></iframe>";
+ src=\"{$self}?do=poll2&hidem=1\"></iframe>";
break;
case 'poll2':
echo "<pre>";
echo $this->Tables(); break;
}
global $ADODB_vers;
- echo "<p><div class='mdl-align'><font size=1>$ADODB_vers Sponsored by <a href=http://phplens.com/>phpLens</a></font></div>";
+ echo "<p><div align=center><font size=1>$ADODB_vers Sponsored by <a href=http://phplens.com/>phpLens</a></font></div>";
}
/*
{
- $PHP_SELF = $_SERVER['PHP_SELF'];
+ $PHP_SELF = htmlspecialchars($_SERVER['PHP_SELF']);
$sql = isset($_REQUEST['sql']) ? $_REQUEST['sql'] : '';
if (isset($_SESSION['phplens_sqlrows'])) $rows = $_SESSION['phplens_sqlrows'];
// end hack
}
-?>
+?>
\ No newline at end of file
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
return sprintf('%s%02d%02d',($gmt<=0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60);
else
return sprintf('%s%02d%02d',($gmt<0)?'+':'-',floor($zhrs),($zhrs-$hrs)*60);
- break;
}
-<?php\r
-// Copyright (c) 2004 ars Cognita Inc., all rights reserved\r
-/* ******************************************************************************\r
- Released under both BSD license and Lesser GPL library license. \r
- Whenever there is any discrepancy between the two licenses, \r
- the BSD license will take precedence. \r
-*******************************************************************************/\r
-/**\r
- * xmlschema is a class that allows the user to quickly and easily\r
- * build a database on any ADOdb-supported platform using a simple\r
- * XML schema.\r
- *\r
- * Last Editor: $Author$\r
- * @author Richard Tango-Lowy & Dan Cech\r
- * @version $Revision$\r
- *\r
- * @package axmls\r
- * @tutorial getting_started.pkg\r
- */\r
- \r
-function _file_get_contents($file) \r
-{\r
- if (function_exists('file_get_contents')) return file_get_contents($file);\r
- \r
- $f = fopen($file,'r');\r
- if (!$f) return '';\r
- $t = '';\r
- \r
- while ($s = fread($f,100000)) $t .= $s;\r
- fclose($f);\r
- return $t;\r
-}\r
-\r
-\r
-/**\r
-* Debug on or off\r
-*/\r
-if( !defined( 'XMLS_DEBUG' ) ) {\r
- define( 'XMLS_DEBUG', FALSE );\r
-}\r
-\r
-/**\r
-* Default prefix key\r
-*/\r
-if( !defined( 'XMLS_PREFIX' ) ) {\r
- define( 'XMLS_PREFIX', '%%P' );\r
-}\r
-\r
-/**\r
-* Maximum length allowed for object prefix\r
-*/\r
-if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {\r
- define( 'XMLS_PREFIX_MAXLEN', 10 );\r
-}\r
-\r
-/**\r
-* Execute SQL inline as it is generated\r
-*/\r
-if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {\r
- define( 'XMLS_EXECUTE_INLINE', FALSE );\r
-}\r
-\r
-/**\r
-* Continue SQL Execution if an error occurs?\r
-*/\r
-if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {\r
- define( 'XMLS_CONTINUE_ON_ERROR', FALSE );\r
-}\r
-\r
-/**\r
-* Current Schema Version\r
-*/\r
-if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {\r
- define( 'XMLS_SCHEMA_VERSION', '0.2' );\r
-}\r
-\r
-/**\r
-* Default Schema Version. Used for Schemas without an explicit version set.\r
-*/\r
-if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {\r
- define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );\r
-}\r
-\r
-/**\r
-* Default Schema Version. Used for Schemas without an explicit version set.\r
-*/\r
-if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {\r
- define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );\r
-}\r
-\r
-/**\r
-* Include the main ADODB library\r
-*/\r
-if( !defined( '_ADODB_LAYER' ) ) {\r
- require( 'adodb.inc.php' );\r
- require( 'adodb-datadict.inc.php' );\r
-}\r
-\r
-/**\r
-* Abstract DB Object. This class provides basic methods for database objects, such\r
-* as tables and indexes.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbObject {\r
- \r
- /**\r
- * var object Parent\r
- */\r
- var $parent;\r
- \r
- /**\r
- * var string current element\r
- */\r
- var $currentElement;\r
- \r
- /**\r
- * NOP\r
- */\r
- function dbObject( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- \r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- \r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- \r
- }\r
- \r
- function create() {\r
- return array();\r
- }\r
- \r
- /**\r
- * Destroys the object\r
- */\r
- function destroy() {\r
- unset( $this );\r
- }\r
- \r
- /**\r
- * Checks whether the specified RDBMS is supported by the current\r
- * database object or its ranking ancestor.\r
- *\r
- * @param string $platform RDBMS platform name (from ADODB platform list).\r
- * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.\r
- */\r
- function supportedPlatform( $platform = NULL ) {\r
- return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;\r
- }\r
- \r
- /**\r
- * Returns the prefix set by the ranking ancestor of the database object.\r
- *\r
- * @param string $name Prefix string.\r
- * @return string Prefix.\r
- */\r
- function prefix( $name = '' ) {\r
- return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;\r
- }\r
- \r
- /**\r
- * Extracts a field ID from the specified field.\r
- *\r
- * @param string $field Field.\r
- * @return string Field ID.\r
- */\r
- function FieldID( $field ) {\r
- return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );\r
- }\r
-}\r
-\r
-/**\r
-* Creates a table object in ADOdb's datadict format\r
-*\r
-* This class stores information about a database table. As charactaristics\r
-* of the table are loaded from the external source, methods and properties\r
-* of this class are used to build up the table description in ADOdb's\r
-* datadict format.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbTable extends dbObject {\r
- \r
- /**\r
- * @var string Table name\r
- */\r
- var $name;\r
- \r
- /**\r
- * @var array Field specifier: Meta-information about each field\r
- */\r
- var $fields = array();\r
- \r
- /**\r
- * @var array List of table indexes.\r
- */\r
- var $indexes = array();\r
- \r
- /**\r
- * @var array Table options: Table-level options\r
- */\r
- var $opts = array();\r
- \r
- /**\r
- * @var string Field index: Keeps track of which field is currently being processed\r
- */\r
- var $current_field;\r
- \r
- /**\r
- * @var boolean Mark table for destruction\r
- * @access private\r
- */\r
- var $drop_table;\r
- \r
- /**\r
- * @var boolean Mark field for destruction (not yet implemented)\r
- * @access private\r
- */\r
- var $drop_field = array();\r
- \r
- /**\r
- * Iniitializes a new table object.\r
- *\r
- * @param string $prefix DB Object prefix\r
- * @param array $attributes Array of table attributes.\r
- */\r
- function dbTable( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- $this->name = $this->prefix($attributes['NAME']);\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements. Elements currently \r
- * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'INDEX':\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- xml_set_object( $parser, $this->addIndex( $attributes ) );\r
- }\r
- break;\r
- case 'DATA':\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- xml_set_object( $parser, $this->addData( $attributes ) );\r
- }\r
- break;\r
- case 'DROP':\r
- $this->drop();\r
- break;\r
- case 'FIELD':\r
- // Add a field\r
- $fieldName = $attributes['NAME'];\r
- $fieldType = $attributes['TYPE'];\r
- $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;\r
- $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;\r
- \r
- $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );\r
- break;\r
- case 'KEY':\r
- case 'NOTNULL':\r
- case 'AUTOINCREMENT':\r
- // Add a field option\r
- $this->addFieldOpt( $this->current_field, $this->currentElement );\r
- break;\r
- case 'DEFAULT':\r
- // Add a field option to the table object\r
- \r
- // Work around ADOdb datadict issue that misinterprets empty strings.\r
- if( $attributes['VALUE'] == '' ) {\r
- $attributes['VALUE'] = " '' ";\r
- }\r
- \r
- $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );\r
- break;\r
- case 'DEFDATE':\r
- case 'DEFTIMESTAMP':\r
- // Add a field option to the table object\r
- $this->addFieldOpt( $this->current_field, $this->currentElement );\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Table constraint\r
- case 'CONSTRAINT':\r
- if( isset( $this->current_field ) ) {\r
- $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );\r
- } else {\r
- $this->addTableOpt( $cdata );\r
- }\r
- break;\r
- // Table option\r
- case 'OPT':\r
- $this->addTableOpt( $cdata );\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'TABLE':\r
- $this->parent->addSQL( $this->create( $this->parent ) );\r
- xml_set_object( $parser, $this->parent );\r
- $this->destroy();\r
- break;\r
- case 'FIELD':\r
- unset($this->current_field);\r
- break;\r
-\r
- }\r
- }\r
- \r
- /**\r
- * Adds an index to a table object\r
- *\r
- * @param array $attributes Index attributes\r
- * @return object dbIndex object\r
- */\r
- function addIndex( $attributes ) {\r
- $name = strtoupper( $attributes['NAME'] );\r
- $this->indexes[$name] = new dbIndex( $this, $attributes );\r
- return $this->indexes[$name];\r
- }\r
- \r
- /**\r
- * Adds data to a table object\r
- *\r
- * @param array $attributes Data attributes\r
- * @return object dbData object\r
- */\r
- function addData( $attributes ) {\r
- if( !isset( $this->data ) ) {\r
- $this->data = new dbData( $this, $attributes );\r
- }\r
- return $this->data;\r
- }\r
- \r
- /**\r
- * Adds a field to a table object\r
- *\r
- * $name is the name of the table to which the field should be added. \r
- * $type is an ADODB datadict field type. The following field types\r
- * are supported as of ADODB 3.40:\r
- * - C: varchar\r
- * - X: CLOB (character large object) or largest varchar size\r
- * if CLOB is not supported\r
- * - C2: Multibyte varchar\r
- * - X2: Multibyte CLOB\r
- * - B: BLOB (binary large object)\r
- * - D: Date (some databases do not support this, and we return a datetime type)\r
- * - T: Datetime or Timestamp\r
- * - L: Integer field suitable for storing booleans (0 or 1)\r
- * - I: Integer (mapped to I4)\r
- * - I1: 1-byte integer\r
- * - I2: 2-byte integer\r
- * - I4: 4-byte integer\r
- * - I8: 8-byte integer\r
- * - F: Floating point number\r
- * - N: Numeric or decimal number\r
- *\r
- * @param string $name Name of the table to which the field will be added.\r
- * @param string $type ADODB datadict field type.\r
- * @param string $size Field size\r
- * @param array $opts Field options array\r
- * @return array Field specifier array\r
- */\r
- function addField( $name, $type, $size = NULL, $opts = NULL ) {\r
- $field_id = $this->FieldID( $name );\r
- \r
- // Set the field index so we know where we are\r
- $this->current_field = $field_id;\r
- \r
- // Set the field name (required)\r
- $this->fields[$field_id]['NAME'] = $name;\r
- \r
- // Set the field type (required)\r
- $this->fields[$field_id]['TYPE'] = $type;\r
- \r
- // Set the field size (optional)\r
- if( isset( $size ) ) {\r
- $this->fields[$field_id]['SIZE'] = $size;\r
- }\r
- \r
- // Set the field options\r
- if( isset( $opts ) ) {\r
- $this->fields[$field_id]['OPTS'][] = $opts;\r
- }\r
- }\r
- \r
- /**\r
- * Adds a field option to the current field specifier\r
- *\r
- * This method adds a field option allowed by the ADOdb datadict \r
- * and appends it to the given field.\r
- *\r
- * @param string $field Field name\r
- * @param string $opt ADOdb field option\r
- * @param mixed $value Field option value\r
- * @return array Field specifier array\r
- */\r
- function addFieldOpt( $field, $opt, $value = NULL ) {\r
- if( !isset( $value ) ) {\r
- $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;\r
- // Add the option and value\r
- } else {\r
- $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );\r
- }\r
- }\r
- \r
- /**\r
- * Adds an option to the table\r
- *\r
- * This method takes a comma-separated list of table-level options\r
- * and appends them to the table object.\r
- *\r
- * @param string $opt Table option\r
- * @return array Options\r
- */\r
- function addTableOpt( $opt ) {\r
- $this->opts[] = $opt;\r
- \r
- return $this->opts;\r
- }\r
- \r
- /**\r
- * Generates the SQL that will create the table in the database\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Array containing table creation SQL\r
- */\r
- function create( &$xmls ) {\r
- $sql = array();\r
- \r
- // drop any existing indexes\r
- if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {\r
- foreach( $legacy_indexes as $index => $index_details ) {\r
- $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );\r
- }\r
- }\r
- \r
- // remove fields to be dropped from table object\r
- foreach( $this->drop_field as $field ) {\r
- unset( $this->fields[$field] );\r
- }\r
- \r
- // if table exists\r
- if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {\r
- // drop table\r
- if( $this->drop_table ) {\r
- $sql[] = $xmls->dict->DropTableSQL( $this->name );\r
- \r
- return $sql;\r
- }\r
- \r
- // drop any existing fields not in schema\r
- foreach( $legacy_fields as $field_id => $field ) {\r
- if( !isset( $this->fields[$field_id] ) ) {\r
- $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' );\r
- }\r
- }\r
- // if table doesn't exist\r
- } else {\r
- if( $this->drop_table ) {\r
- return $sql;\r
- }\r
- \r
- $legacy_fields = array();\r
- }\r
- \r
- // Loop through the field specifier array, building the associative array for the field options\r
- $fldarray = array();\r
- \r
- foreach( $this->fields as $field_id => $finfo ) {\r
- // Set an empty size if it isn't supplied\r
- if( !isset( $finfo['SIZE'] ) ) {\r
- $finfo['SIZE'] = '';\r
- }\r
- \r
- // Initialize the field array with the type and size\r
- $fldarray[$field_id] = array(\r
- 'NAME' => $finfo['NAME'],\r
- 'TYPE' => $finfo['TYPE'],\r
- 'SIZE' => $finfo['SIZE']\r
- );\r
- \r
- // Loop through the options array and add the field options. \r
- if( isset( $finfo['OPTS'] ) ) {\r
- foreach( $finfo['OPTS'] as $opt ) {\r
- // Option has an argument.\r
- if( is_array( $opt ) ) {\r
- $key = key( $opt );\r
- $value = $opt[key( $opt )];\r
- @$fldarray[$field_id][$key] .= $value;\r
- // Option doesn't have arguments\r
- } else {\r
- $fldarray[$field_id][$opt] = $opt;\r
- }\r
- }\r
- }\r
- }\r
- \r
- if( empty( $legacy_fields ) ) {\r
- // Create the new table\r
- $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );\r
- logMsg( end( $sql ), 'Generated CreateTableSQL' );\r
- } else {\r
- // Upgrade an existing table\r
- logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );\r
- switch( $xmls->upgrade ) {\r
- // Use ChangeTableSQL\r
- case 'ALTER':\r
- logMsg( 'Generated ChangeTableSQL (ALTERing table)' );\r
- $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );\r
- break;\r
- case 'REPLACE':\r
- logMsg( 'Doing upgrade REPLACE (testing)' );\r
- $sql[] = $xmls->dict->DropTableSQL( $this->name );\r
- $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );\r
- break;\r
- // ignore table\r
- default:\r
- return array();\r
- }\r
- }\r
- \r
- foreach( $this->indexes as $index ) {\r
- $sql[] = $index->create( $xmls );\r
- }\r
- \r
- if( isset( $this->data ) ) {\r
- $sql[] = $this->data->create( $xmls );\r
- }\r
- \r
- return $sql;\r
- }\r
- \r
- /**\r
- * Marks a field or table for destruction\r
- */\r
- function drop() {\r
- if( isset( $this->current_field ) ) {\r
- // Drop the current field\r
- logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );\r
- // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );\r
- $this->drop_field[$this->current_field] = $this->current_field;\r
- } else {\r
- // Drop the current table\r
- logMsg( "Dropping table '{$this->name}'" );\r
- // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );\r
- $this->drop_table = TRUE;\r
- }\r
- }\r
-}\r
-\r
-/**\r
-* Creates an index object in ADOdb's datadict format\r
-*\r
-* This class stores information about a database index. As charactaristics\r
-* of the index are loaded from the external source, methods and properties\r
-* of this class are used to build up the index description in ADOdb's\r
-* datadict format.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbIndex extends dbObject {\r
- \r
- /**\r
- * @var string Index name\r
- */\r
- var $name;\r
- \r
- /**\r
- * @var array Index options: Index-level options\r
- */\r
- var $opts = array();\r
- \r
- /**\r
- * @var array Indexed fields: Table columns included in this index\r
- */\r
- var $columns = array();\r
- \r
- /**\r
- * @var boolean Mark index for destruction\r
- * @access private\r
- */\r
- var $drop = FALSE;\r
- \r
- /**\r
- * Initializes the new dbIndex object.\r
- *\r
- * @param object $parent Parent object\r
- * @param array $attributes Attributes\r
- *\r
- * @internal\r
- */\r
- function dbIndex( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- \r
- $this->name = $this->prefix ($attributes['NAME']);\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * Processes XML opening tags. \r
- * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'DROP':\r
- $this->drop();\r
- break;\r
- case 'CLUSTERED':\r
- case 'BITMAP':\r
- case 'UNIQUE':\r
- case 'FULLTEXT':\r
- case 'HASH':\r
- // Add index Option\r
- $this->addIndexOpt( $this->currentElement );\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * Processes XML cdata.\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Index field name\r
- case 'COL':\r
- $this->addField( $cdata );\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'INDEX':\r
- xml_set_object( $parser, $this->parent );\r
- break;\r
- }\r
- }\r
- \r
- /**\r
- * Adds a field to the index\r
- *\r
- * @param string $name Field name\r
- * @return string Field list\r
- */\r
- function addField( $name ) {\r
- $this->columns[$this->FieldID( $name )] = $name;\r
- \r
- // Return the field list\r
- return $this->columns;\r
- }\r
- \r
- /**\r
- * Adds options to the index\r
- *\r
- * @param string $opt Comma-separated list of index options.\r
- * @return string Option list\r
- */\r
- function addIndexOpt( $opt ) {\r
- $this->opts[] = $opt;\r
- \r
- // Return the options list\r
- return $this->opts;\r
- }\r
- \r
- /**\r
- * Generates the SQL that will create the index in the database\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Array containing index creation SQL\r
- */\r
- function create( &$xmls ) {\r
- if( $this->drop ) {\r
- return NULL;\r
- }\r
- \r
- // eliminate any columns that aren't in the table\r
- foreach( $this->columns as $id => $col ) {\r
- if( !isset( $this->parent->fields[$id] ) ) {\r
- unset( $this->columns[$id] );\r
- }\r
- }\r
- \r
- return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );\r
- }\r
- \r
- /**\r
- * Marks an index for destruction\r
- */\r
- function drop() {\r
- $this->drop = TRUE;\r
- }\r
-}\r
-\r
-/**\r
-* Creates a data object in ADOdb's datadict format\r
-*\r
-* This class stores information about table data.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbData extends dbObject {\r
- \r
- var $data = array();\r
- \r
- var $row;\r
- \r
- /**\r
- * Initializes the new dbIndex object.\r
- *\r
- * @param object $parent Parent object\r
- * @param array $attributes Attributes\r
- *\r
- * @internal\r
- */\r
- function dbData( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * Processes XML opening tags. \r
- * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'ROW':\r
- $this->row = count( $this->data );\r
- $this->data[$this->row] = array();\r
- break;\r
- case 'F':\r
- $this->addField($attributes);\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * Processes XML cdata.\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Index field name\r
- case 'F':\r
- $this->addData( $cdata );\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'DATA':\r
- xml_set_object( $parser, $this->parent );\r
- break;\r
- }\r
- }\r
- \r
- /**\r
- * Adds a field to the index\r
- *\r
- * @param string $name Field name\r
- * @return string Field list\r
- */\r
- function addField( $attributes ) {\r
- if( isset( $attributes['NAME'] ) ) {\r
- $name = $attributes['NAME'];\r
- } else {\r
- $name = count($this->data[$this->row]);\r
- }\r
- \r
- // Set the field index so we know where we are\r
- $this->current_field = $this->FieldID( $name );\r
- }\r
- \r
- /**\r
- * Adds options to the index\r
- *\r
- * @param string $opt Comma-separated list of index options.\r
- * @return string Option list\r
- */\r
- function addData( $cdata ) {\r
- if( !isset( $this->data[$this->row] ) ) {\r
- $this->data[$this->row] = array();\r
- }\r
- \r
- if( !isset( $this->data[$this->row][$this->current_field] ) ) {\r
- $this->data[$this->row][$this->current_field] = '';\r
- }\r
- \r
- $this->data[$this->row][$this->current_field] .= $cdata;\r
- }\r
- \r
- /**\r
- * Generates the SQL that will create the index in the database\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Array containing index creation SQL\r
- */\r
- function create( &$xmls ) {\r
- $table = $xmls->dict->TableName($this->parent->name);\r
- $table_field_count = count($this->parent->fields);\r
- $sql = array();\r
- \r
- // eliminate any columns that aren't in the table\r
- foreach( $this->data as $row ) {\r
- $table_fields = $this->parent->fields;\r
- $fields = array();\r
- \r
- foreach( $row as $field_id => $field_data ) {\r
- if( !array_key_exists( $field_id, $table_fields ) ) {\r
- if( is_numeric( $field_id ) ) {\r
- $field_id = reset( array_keys( $table_fields ) );\r
- } else {\r
- continue;\r
- }\r
- }\r
- \r
- $name = $table_fields[$field_id]['NAME'];\r
- \r
- switch( $table_fields[$field_id]['TYPE'] ) {\r
- case 'C':\r
- case 'C2':\r
- case 'X':\r
- case 'X2':\r
- $fields[$name] = $xmls->db->qstr( $field_data );\r
- break;\r
- case 'I':\r
- case 'I1':\r
- case 'I2':\r
- case 'I4':\r
- case 'I8':\r
- $fields[$name] = intval($field_data);\r
- break;\r
- default:\r
- $fields[$name] = $field_data;\r
- }\r
- \r
- unset($table_fields[$field_id]);\r
- }\r
- \r
- // check that at least 1 column is specified\r
- if( empty( $fields ) ) {\r
- continue;\r
- }\r
- \r
- // check that no required columns are missing\r
- if( count( $fields ) < $table_field_count ) {\r
- foreach( $table_fields as $field ) {\r
- if (isset( $field['OPTS'] ))\r
- if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {\r
- continue(2);\r
- }\r
- }\r
- }\r
- \r
- $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';\r
- }\r
- \r
- return $sql;\r
- }\r
-}\r
-\r
-/**\r
-* Creates the SQL to execute a list of provided SQL queries\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbQuerySet extends dbObject {\r
- \r
- /**\r
- * @var array List of SQL queries\r
- */\r
- var $queries = array();\r
- \r
- /**\r
- * @var string String used to build of a query line by line\r
- */\r
- var $query;\r
- \r
- /**\r
- * @var string Query prefix key\r
- */\r
- var $prefixKey = '';\r
- \r
- /**\r
- * @var boolean Auto prefix enable (TRUE)\r
- */\r
- var $prefixMethod = 'AUTO';\r
- \r
- /**\r
- * Initializes the query set.\r
- *\r
- * @param object $parent Parent object\r
- * @param array $attributes Attributes\r
- */\r
- function dbQuerySet( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- \r
- // Overrides the manual prefix key\r
- if( isset( $attributes['KEY'] ) ) {\r
- $this->prefixKey = $attributes['KEY'];\r
- }\r
- \r
- $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';\r
- \r
- // Enables or disables automatic prefix prepending\r
- switch( $prefixMethod ) {\r
- case 'AUTO':\r
- $this->prefixMethod = 'AUTO';\r
- break;\r
- case 'MANUAL':\r
- $this->prefixMethod = 'MANUAL';\r
- break;\r
- case 'NONE':\r
- $this->prefixMethod = 'NONE';\r
- break;\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements. Elements currently \r
- * processed are: QUERY. \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'QUERY':\r
- // Create a new query in a SQL queryset.\r
- // Ignore this query set if a platform is specified and it's different than the \r
- // current connection platform.\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- $this->newQuery();\r
- } else {\r
- $this->discardQuery();\r
- }\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Line of queryset SQL data\r
- case 'QUERY':\r
- $this->buildQuery( $cdata );\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'QUERY':\r
- // Add the finished query to the open query set.\r
- $this->addQuery();\r
- break;\r
- case 'SQL':\r
- $this->parent->addSQL( $this->create( $this->parent ) );\r
- xml_set_object( $parser, $this->parent );\r
- $this->destroy();\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * Re-initializes the query.\r
- *\r
- * @return boolean TRUE\r
- */\r
- function newQuery() {\r
- $this->query = '';\r
- \r
- return TRUE;\r
- }\r
- \r
- /**\r
- * Discards the existing query.\r
- *\r
- * @return boolean TRUE\r
- */\r
- function discardQuery() {\r
- unset( $this->query );\r
- \r
- return TRUE;\r
- }\r
- \r
- /** \r
- * Appends a line to a query that is being built line by line\r
- *\r
- * @param string $data Line of SQL data or NULL to initialize a new query\r
- * @return string SQL query string.\r
- */\r
- function buildQuery( $sql = NULL ) {\r
- if( !isset( $this->query ) OR empty( $sql ) ) {\r
- return FALSE;\r
- }\r
- \r
- $this->query .= $sql;\r
- \r
- return $this->query;\r
- }\r
- \r
- /**\r
- * Adds a completed query to the query list\r
- *\r
- * @return string SQL of added query\r
- */\r
- function addQuery() {\r
- if( !isset( $this->query ) ) {\r
- return FALSE;\r
- }\r
- \r
- $this->queries[] = $return = trim($this->query);\r
- \r
- unset( $this->query );\r
- \r
- return $return;\r
- }\r
- \r
- /**\r
- * Creates and returns the current query set\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Query set\r
- */\r
- function create( &$xmls ) {\r
- foreach( $this->queries as $id => $query ) {\r
- switch( $this->prefixMethod ) {\r
- case 'AUTO':\r
- // Enable auto prefix replacement\r
- \r
- // Process object prefix.\r
- // Evaluate SQL statements to prepend prefix to objects\r
- $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
- $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
- $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
- \r
- // SELECT statements aren't working yet\r
- #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );\r
- \r
- case 'MANUAL':\r
- // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.\r
- // If prefixKey is not set, we use the default constant XMLS_PREFIX\r
- if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {\r
- // Enable prefix override\r
- $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );\r
- } else {\r
- // Use default replacement\r
- $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );\r
- }\r
- }\r
- \r
- $this->queries[$id] = trim( $query );\r
- }\r
- \r
- // Return the query set array\r
- return $this->queries;\r
- }\r
- \r
- /**\r
- * Rebuilds the query with the prefix attached to any objects\r
- *\r
- * @param string $regex Regex used to add prefix\r
- * @param string $query SQL query string\r
- * @param string $prefix Prefix to be appended to tables, indices, etc.\r
- * @return string Prefixed SQL query string.\r
- */\r
- function prefixQuery( $regex, $query, $prefix = NULL ) {\r
- if( !isset( $prefix ) ) {\r
- return $query;\r
- }\r
- \r
- if( preg_match( $regex, $query, $match ) ) {\r
- $preamble = $match[1];\r
- $postamble = $match[5];\r
- $objectList = explode( ',', $match[3] );\r
- // $prefix = $prefix . '_';\r
- \r
- $prefixedList = '';\r
- \r
- foreach( $objectList as $object ) {\r
- if( $prefixedList !== '' ) {\r
- $prefixedList .= ', ';\r
- }\r
- \r
- $prefixedList .= $prefix . trim( $object );\r
- }\r
- \r
- $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;\r
- }\r
- \r
- return $query;\r
- }\r
-}\r
-\r
-/**\r
-* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements\r
-* \r
-* This class is used to load and parse the XML file, to create an array of SQL statements\r
-* that can be used to build a database, and to build the database using the SQL array.\r
-*\r
-* @tutorial getting_started.pkg\r
-*\r
-* @author Richard Tango-Lowy & Dan Cech\r
-* @version $Revision$\r
-*\r
-* @package axmls\r
-*/\r
-class adoSchema {\r
- \r
- /**\r
- * @var array Array containing SQL queries to generate all objects\r
- * @access private\r
- */\r
- var $sqlArray;\r
- \r
- /**\r
- * @var object ADOdb connection object\r
- * @access private\r
- */\r
- var $db;\r
- \r
- /**\r
- * @var object ADOdb Data Dictionary\r
- * @access private\r
- */\r
- var $dict;\r
- \r
- /**\r
- * @var string Current XML element\r
- * @access private\r
- */\r
- var $currentElement = '';\r
- \r
- /**\r
- * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database\r
- * @access private\r
- */\r
- var $upgrade = '';\r
- \r
- /**\r
- * @var string Optional object prefix\r
- * @access private\r
- */\r
- var $objectPrefix = '';\r
- \r
- /**\r
- * @var long Original Magic Quotes Runtime value\r
- * @access private\r
- */\r
- var $mgq;\r
- \r
- /**\r
- * @var long System debug\r
- * @access private\r
- */\r
- var $debug;\r
- \r
- /**\r
- * @var string Regular expression to find schema version\r
- * @access private\r
- */\r
- var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';\r
- \r
- /**\r
- * @var string Current schema version\r
- * @access private\r
- */\r
- var $schemaVersion;\r
- \r
- /**\r
- * @var int Success of last Schema execution\r
- */\r
- var $success;\r
- \r
- /**\r
- * @var bool Execute SQL inline as it is generated\r
- */\r
- var $executeInline;\r
- \r
- /**\r
- * @var bool Continue SQL execution if errors occur\r
- */\r
- var $continueOnError;\r
- \r
- /**\r
- * Creates an adoSchema object\r
- *\r
- * Creating an adoSchema object is the first step in processing an XML schema.\r
- * The only parameter is an ADOdb database connection object, which must already\r
- * have been created.\r
- *\r
- * @param object $db ADOdb database connection object.\r
- */\r
- function adoSchema( &$db ) {\r
- // Initialize the environment\r
- $this->mgq = get_magic_quotes_runtime();\r
- set_magic_quotes_runtime(0);\r
- \r
- $this->db = $db;\r
- $this->debug = $this->db->debug;\r
- $this->dict = NewDataDictionary( $this->db );\r
- $this->sqlArray = array();\r
- $this->schemaVersion = XMLS_SCHEMA_VERSION;\r
- $this->executeInline( XMLS_EXECUTE_INLINE );\r
- $this->continueOnError( XMLS_CONTINUE_ON_ERROR );\r
- $this->setUpgradeMethod();\r
- }\r
- \r
- /**\r
- * Sets the method to be used for upgrading an existing database\r
- *\r
- * Use this method to specify how existing database objects should be upgraded.\r
- * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to\r
- * alter each database object directly, REPLACE attempts to rebuild each object\r
- * from scratch, BEST attempts to determine the best upgrade method for each\r
- * object, and NONE disables upgrading.\r
- *\r
- * This method is not yet used by AXMLS, but exists for backward compatibility.\r
- * The ALTER method is automatically assumed when the adoSchema object is\r
- * instantiated; other upgrade methods are not currently supported.\r
- *\r
- * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)\r
- * @returns string Upgrade method used\r
- */\r
- function SetUpgradeMethod( $method = '' ) {\r
- if( !is_string( $method ) ) {\r
- return FALSE;\r
- }\r
- \r
- $method = strtoupper( $method );\r
- \r
- // Handle the upgrade methods\r
- switch( $method ) {\r
- case 'ALTER':\r
- $this->upgrade = $method;\r
- break;\r
- case 'REPLACE':\r
- $this->upgrade = $method;\r
- break;\r
- case 'BEST':\r
- $this->upgrade = 'ALTER';\r
- break;\r
- case 'NONE':\r
- $this->upgrade = 'NONE';\r
- break;\r
- default:\r
- // Use default if no legitimate method is passed.\r
- $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;\r
- }\r
- \r
- return $this->upgrade;\r
- }\r
- \r
- /**\r
- * Enables/disables inline SQL execution.\r
- *\r
- * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),\r
- * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode\r
- * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()\r
- * to apply the schema to the database.\r
- *\r
- * @param bool $mode execute\r
- * @return bool current execution mode\r
- *\r
- * @see ParseSchema(), ExecuteSchema()\r
- */\r
- function ExecuteInline( $mode = NULL ) {\r
- if( is_bool( $mode ) ) {\r
- $this->executeInline = $mode;\r
- }\r
- \r
- return $this->executeInline;\r
- }\r
- \r
- /**\r
- * Enables/disables SQL continue on error.\r
- *\r
- * Call this method to enable or disable continuation of SQL execution if an error occurs.\r
- * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.\r
- * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing\r
- * of the schema will continue.\r
- *\r
- * @param bool $mode execute\r
- * @return bool current continueOnError mode\r
- *\r
- * @see addSQL(), ExecuteSchema()\r
- */\r
- function ContinueOnError( $mode = NULL ) {\r
- if( is_bool( $mode ) ) {\r
- $this->continueOnError = $mode;\r
- }\r
- \r
- return $this->continueOnError;\r
- }\r
- \r
- /**\r
- * Loads an XML schema from a file and converts it to SQL.\r
- *\r
- * Call this method to load the specified schema (see the DTD for the proper format) from\r
- * the filesystem and generate the SQL necessary to create the database described. \r
- * @see ParseSchemaString()\r
- *\r
- * @param string $file Name of XML schema file.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute\r
- */\r
- function ParseSchema( $filename, $returnSchema = FALSE ) {\r
- return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );\r
- }\r
- \r
- /**\r
- * Loads an XML schema from a file and converts it to SQL.\r
- *\r
- * Call this method to load the specified schema from a file (see the DTD for the proper format) \r
- * and generate the SQL necessary to create the database described by the schema.\r
- *\r
- * @param string $file Name of XML schema file.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute.\r
- *\r
- * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()\r
- * @see ParseSchema(), ParseSchemaString()\r
- */\r
- function ParseSchemaFile( $filename, $returnSchema = FALSE ) {\r
- // Open the file\r
- if( !($fp = fopen( $filename, 'r' )) ) {\r
- // die( 'Unable to open file' );\r
- return FALSE;\r
- }\r
- \r
- // do version detection here\r
- if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {\r
- return FALSE;\r
- }\r
- \r
- if ( $returnSchema )\r
- {\r
- $xmlstring = '';\r
- while( $data = fread( $fp, 100000 ) ) {\r
- $xmlstring .= $data;\r
- }\r
- return $xmlstring;\r
- }\r
- \r
- $this->success = 2;\r
- \r
- $xmlParser = $this->create_parser();\r
- \r
- // Process the file\r
- while( $data = fread( $fp, 4096 ) ) {\r
- if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {\r
- die( sprintf(\r
- "XML error: %s at line %d",\r
- xml_error_string( xml_get_error_code( $xmlParser) ),\r
- xml_get_current_line_number( $xmlParser)\r
- ) );\r
- }\r
- }\r
- \r
- xml_parser_free( $xmlParser );\r
- \r
- return $this->sqlArray;\r
- }\r
- \r
- /**\r
- * Converts an XML schema string to SQL.\r
- *\r
- * Call this method to parse a string containing an XML schema (see the DTD for the proper format)\r
- * and generate the SQL necessary to create the database described by the schema. \r
- * @see ParseSchema()\r
- *\r
- * @param string $xmlstring XML schema string.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute.\r
- */\r
- function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {\r
- if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {\r
- return FALSE;\r
- }\r
- \r
- // do version detection here\r
- if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {\r
- return FALSE;\r
- }\r
- \r
- if ( $returnSchema )\r
- {\r
- return $xmlstring;\r
- }\r
- \r
- $this->success = 2;\r
- \r
- $xmlParser = $this->create_parser();\r
- \r
- if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {\r
- die( sprintf(\r
- "XML error: %s at line %d",\r
- xml_error_string( xml_get_error_code( $xmlParser) ),\r
- xml_get_current_line_number( $xmlParser)\r
- ) );\r
- }\r
- \r
- xml_parser_free( $xmlParser );\r
- \r
- return $this->sqlArray;\r
- }\r
- \r
- /**\r
- * Loads an XML schema from a file and converts it to uninstallation SQL.\r
- *\r
- * Call this method to load the specified schema (see the DTD for the proper format) from\r
- * the filesystem and generate the SQL necessary to remove the database described.\r
- * @see RemoveSchemaString()\r
- *\r
- * @param string $file Name of XML schema file.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute\r
- */\r
- function RemoveSchema( $filename, $returnSchema = FALSE ) {\r
- return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );\r
- }\r
- \r
- /**\r
- * Converts an XML schema string to uninstallation SQL.\r
- *\r
- * Call this method to parse a string containing an XML schema (see the DTD for the proper format)\r
- * and generate the SQL necessary to uninstall the database described by the schema. \r
- * @see RemoveSchema()\r
- *\r
- * @param string $schema XML schema string.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute.\r
- */\r
- function RemoveSchemaString( $schema, $returnSchema = FALSE ) {\r
- \r
- // grab current version\r
- if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {\r
- return FALSE;\r
- }\r
- \r
- return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );\r
- }\r
- \r
- /**\r
- * Applies the current XML schema to the database (post execution).\r
- *\r
- * Call this method to apply the current schema (generally created by calling \r
- * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, \r
- * and executing other SQL specified in the schema) after parsing.\r
- * @see ParseSchema(), ParseSchemaString(), ExecuteInline()\r
- *\r
- * @param array $sqlArray Array of SQL statements that will be applied rather than\r
- * the current schema.\r
- * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.\r
- * @returns integer 0 if failure, 1 if errors, 2 if successful.\r
- */\r
- function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) {\r
- if( !is_bool( $continueOnErr ) ) {\r
- $continueOnErr = $this->ContinueOnError();\r
- }\r
- \r
- if( !isset( $sqlArray ) ) {\r
- $sqlArray = $this->sqlArray;\r
- }\r
- \r
- if( !is_array( $sqlArray ) ) {\r
- $this->success = 0;\r
- } else {\r
- $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );\r
- }\r
- \r
- return $this->success;\r
- }\r
- \r
- /**\r
- * Returns the current SQL array. \r
- *\r
- * Call this method to fetch the array of SQL queries resulting from \r
- * ParseSchema() or ParseSchemaString(). \r
- *\r
- * @param string $format Format: HTML, TEXT, or NONE (PHP array)\r
- * @return array Array of SQL statements or FALSE if an error occurs\r
- */\r
- function PrintSQL( $format = 'NONE' ) {\r
- $sqlArray = null;\r
- return $this->getSQL( $format, $sqlArray );\r
- }\r
- \r
- /**\r
- * Saves the current SQL array to the local filesystem as a list of SQL queries.\r
- *\r
- * Call this method to save the array of SQL queries (generally resulting from a\r
- * parsed XML schema) to the filesystem.\r
- *\r
- * @param string $filename Path and name where the file should be saved.\r
- * @return boolean TRUE if save is successful, else FALSE. \r
- */\r
- function SaveSQL( $filename = './schema.sql' ) {\r
- \r
- if( !isset( $sqlArray ) ) {\r
- $sqlArray = $this->sqlArray;\r
- }\r
- if( !isset( $sqlArray ) ) {\r
- return FALSE;\r
- }\r
- \r
- $fp = fopen( $filename, "w" );\r
- \r
- foreach( $sqlArray as $key => $query ) {\r
- fwrite( $fp, $query . ";\n" );\r
- }\r
- fclose( $fp );\r
- }\r
- \r
- /**\r
- * Create an xml parser\r
- *\r
- * @return object PHP XML parser object\r
- *\r
- * @access private\r
- */\r
- function create_parser() {\r
- // Create the parser\r
- $xmlParser = xml_parser_create();\r
- xml_set_object( $xmlParser, $this );\r
- \r
- // Initialize the XML callback functions\r
- xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );\r
- xml_set_character_data_handler( $xmlParser, '_tag_cdata' );\r
- \r
- return $xmlParser;\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- switch( strtoupper( $tag ) ) {\r
- case 'TABLE':\r
- $this->obj = new dbTable( $this, $attributes );\r
- xml_set_object( $parser, $this->obj );\r
- break;\r
- case 'SQL':\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- $this->obj = new dbQuerySet( $this, $attributes );\r
- xml_set_object( $parser, $this->obj );\r
- }\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- \r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- * @internal\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- \r
- }\r
- \r
- /**\r
- * Converts an XML schema string to the specified DTD version.\r
- *\r
- * Call this method to convert a string containing an XML schema to a different AXMLS\r
- * DTD version. For instance, to convert a schema created for an pre-1.0 version for \r
- * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version \r
- * parameter is specified, the schema will be converted to the current DTD version. \r
- * If the newFile parameter is provided, the converted schema will be written to the specified\r
- * file.\r
- * @see ConvertSchemaFile()\r
- *\r
- * @param string $schema String containing XML schema that will be converted.\r
- * @param string $newVersion DTD version to convert to.\r
- * @param string $newFile File name of (converted) output file.\r
- * @return string Converted XML schema or FALSE if an error occurs.\r
- */\r
- function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {\r
- \r
- // grab current version\r
- if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {\r
- return FALSE;\r
- }\r
- \r
- if( !isset ($newVersion) ) {\r
- $newVersion = $this->schemaVersion;\r
- }\r
- \r
- if( $version == $newVersion ) {\r
- $result = $schema;\r
- } else {\r
- $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);\r
- }\r
- \r
- if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {\r
- fwrite( $fp, $result );\r
- fclose( $fp );\r
- }\r
- \r
- return $result;\r
- }\r
- \r
- // compat for pre-4.3 - jlim\r
- function _file_get_contents($path)\r
- {\r
- if (function_exists('file_get_contents')) return file_get_contents($path);\r
- return join('',file($path));\r
- }\r
- \r
- /**\r
- * Converts an XML schema file to the specified DTD version.\r
- *\r
- * Call this method to convert the specified XML schema file to a different AXMLS\r
- * DTD version. For instance, to convert a schema created for an pre-1.0 version for \r
- * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version \r
- * parameter is specified, the schema will be converted to the current DTD version. \r
- * If the newFile parameter is provided, the converted schema will be written to the specified\r
- * file.\r
- * @see ConvertSchemaString()\r
- *\r
- * @param string $filename Name of XML schema file that will be converted.\r
- * @param string $newVersion DTD version to convert to.\r
- * @param string $newFile File name of (converted) output file.\r
- * @return string Converted XML schema or FALSE if an error occurs.\r
- */\r
- function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {\r
- \r
- // grab current version\r
- if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {\r
- return FALSE;\r
- }\r
- \r
- if( !isset ($newVersion) ) {\r
- $newVersion = $this->schemaVersion;\r
- }\r
- \r
- if( $version == $newVersion ) {\r
- $result = _file_get_contents( $filename );\r
- \r
- // remove unicode BOM if present\r
- if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {\r
- $result = substr( $result, 3 );\r
- }\r
- } else {\r
- $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );\r
- }\r
- \r
- if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {\r
- fwrite( $fp, $result );\r
- fclose( $fp );\r
- }\r
- \r
- return $result;\r
- }\r
- \r
- function TransformSchema( $schema, $xsl, $schematype='string' )\r
- {\r
- // Fail if XSLT extension is not available\r
- if( ! function_exists( 'xslt_create' ) ) {\r
- return FALSE;\r
- }\r
- \r
- $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';\r
- \r
- // look for xsl\r
- if( !is_readable( $xsl_file ) ) {\r
- return FALSE;\r
- }\r
- \r
- switch( $schematype )\r
- {\r
- case 'file':\r
- if( !is_readable( $schema ) ) {\r
- return FALSE;\r
- }\r
- \r
- $schema = _file_get_contents( $schema );\r
- break;\r
- case 'string':\r
- default:\r
- if( !is_string( $schema ) ) {\r
- return FALSE;\r
- }\r
- }\r
- \r
- $arguments = array (\r
- '/_xml' => $schema,\r
- '/_xsl' => _file_get_contents( $xsl_file )\r
- );\r
- \r
- // create an XSLT processor\r
- $xh = xslt_create ();\r
- \r
- // set error handler\r
- xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));\r
- \r
- // process the schema\r
- $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); \r
- \r
- xslt_free ($xh);\r
- \r
- return $result;\r
- }\r
- \r
- /**\r
- * Processes XSLT transformation errors\r
- *\r
- * @param object $parser XML parser object\r
- * @param integer $errno Error number\r
- * @param integer $level Error level\r
- * @param array $fields Error information fields\r
- *\r
- * @access private\r
- */\r
- function xslt_error_handler( $parser, $errno, $level, $fields ) {\r
- if( is_array( $fields ) ) {\r
- $msg = array(\r
- 'Message Type' => ucfirst( $fields['msgtype'] ),\r
- 'Message Code' => $fields['code'],\r
- 'Message' => $fields['msg'],\r
- 'Error Number' => $errno,\r
- 'Level' => $level\r
- );\r
- \r
- switch( $fields['URI'] ) {\r
- case 'arg:/_xml':\r
- $msg['Input'] = 'XML';\r
- break;\r
- case 'arg:/_xsl':\r
- $msg['Input'] = 'XSL';\r
- break;\r
- default:\r
- $msg['Input'] = $fields['URI'];\r
- }\r
- \r
- $msg['Line'] = $fields['line'];\r
- } else {\r
- $msg = array(\r
- 'Message Type' => 'Error',\r
- 'Error Number' => $errno,\r
- 'Level' => $level,\r
- 'Fields' => var_export( $fields, TRUE )\r
- );\r
- }\r
- \r
- $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"\r
- . '<table>' . "\n";\r
- \r
- foreach( $msg as $label => $details ) {\r
- $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";\r
- }\r
- \r
- $error_details .= '</table>';\r
- \r
- trigger_error( $error_details, E_USER_ERROR );\r
- }\r
- \r
- /**\r
- * Returns the AXMLS Schema Version of the requested XML schema file.\r
- *\r
- * Call this method to obtain the AXMLS DTD version of the requested XML schema file.\r
- * @see SchemaStringVersion()\r
- *\r
- * @param string $filename AXMLS schema file\r
- * @return string Schema version number or FALSE on error\r
- */\r
- function SchemaFileVersion( $filename ) {\r
- // Open the file\r
- if( !($fp = fopen( $filename, 'r' )) ) {\r
- // die( 'Unable to open file' );\r
- return FALSE;\r
- }\r
- \r
- // Process the file\r
- while( $data = fread( $fp, 4096 ) ) {\r
- if( preg_match( $this->versionRegex, $data, $matches ) ) {\r
- return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;\r
- }\r
- }\r
- \r
- return FALSE;\r
- }\r
- \r
- /**\r
- * Returns the AXMLS Schema Version of the provided XML schema string.\r
- *\r
- * Call this method to obtain the AXMLS DTD version of the provided XML schema string.\r
- * @see SchemaFileVersion()\r
- *\r
- * @param string $xmlstring XML schema string\r
- * @return string Schema version number or FALSE on error\r
- */\r
- function SchemaStringVersion( $xmlstring ) {\r
- if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {\r
- return FALSE;\r
- }\r
- \r
- if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {\r
- return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;\r
- }\r
- \r
- return FALSE;\r
- }\r
- \r
- /**\r
- * Extracts an XML schema from an existing database.\r
- *\r
- * Call this method to create an XML schema string from an existing database.\r
- * If the data parameter is set to TRUE, AXMLS will include the data from the database\r
- * in the schema. \r
- *\r
- * @param boolean $data Include data in schema dump\r
- * @return string Generated XML schema\r
- */\r
- function ExtractSchema( $data = FALSE ) {\r
- $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );\r
- \r
- $schema = '<?xml version="1.0"?>' . "\n"\r
- . '<schema version="' . $this->schemaVersion . '">' . "\n";\r
- \r
- if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) {\r
- foreach( $tables as $table ) {\r
- $schema .= ' <table name="' . $table . '">' . "\n";\r
- \r
- // grab details from database\r
- $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' );\r
- $fields = $this->db->MetaColumns( $table );\r
- $indexes = $this->db->MetaIndexes( $table );\r
- \r
- if( is_array( $fields ) ) {\r
- foreach( $fields as $details ) {\r
- $extra = '';\r
- $content = array();\r
- \r
- if( $details->max_length > 0 ) {\r
- $extra .= ' size="' . $details->max_length . '"';\r
- }\r
- \r
- if( $details->primary_key ) {\r
- $content[] = '<KEY/>';\r
- } elseif( $details->not_null ) {\r
- $content[] = '<NOTNULL/>';\r
- }\r
- \r
- if( $details->has_default ) {\r
- $content[] = '<DEFAULT value="' . $details->default_value . '"/>';\r
- }\r
- \r
- if( $details->auto_increment ) {\r
- $content[] = '<AUTOINCREMENT/>';\r
- }\r
- \r
- // this stops the creation of 'R' columns,\r
- // AUTOINCREMENT is used to create auto columns\r
- $details->primary_key = 0;\r
- $type = $rs->MetaType( $details );\r
- \r
- $schema .= ' <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>';\r
- \r
- if( !empty( $content ) ) {\r
- $schema .= "\n " . implode( "\n ", $content ) . "\n ";\r
- }\r
- \r
- $schema .= '</field>' . "\n";\r
- }\r
- }\r
- \r
- if( is_array( $indexes ) ) {\r
- foreach( $indexes as $index => $details ) {\r
- $schema .= ' <index name="' . $index . '">' . "\n";\r
- \r
- if( $details['unique'] ) {\r
- $schema .= ' <UNIQUE/>' . "\n";\r
- }\r
- \r
- foreach( $details['columns'] as $column ) {\r
- $schema .= ' <col>' . $column . '</col>' . "\n";\r
- }\r
- \r
- $schema .= ' </index>' . "\n";\r
- }\r
- }\r
- \r
- if( $data ) {\r
- $rs = $this->db->Execute( 'SELECT * FROM ' . $table );\r
- \r
- if( is_object( $rs ) ) {\r
- $schema .= ' <data>' . "\n";\r
- \r
- while( $row = $rs->FetchRow() ) {\r
- foreach( $row as $key => $val ) {\r
- $row[$key] = htmlentities($val);\r
- }\r
- \r
- $schema .= ' <row><f>' . implode( '</f><f>', $row ) . '</f></row>' . "\n";\r
- }\r
- \r
- $schema .= ' </data>' . "\n";\r
- }\r
- }\r
- \r
- $schema .= ' </table>' . "\n";\r
- }\r
- }\r
- \r
- $this->db->SetFetchMode( $old_mode );\r
- \r
- $schema .= '</schema>';\r
- return $schema;\r
- }\r
- \r
- /**\r
- * Sets a prefix for database objects\r
- *\r
- * Call this method to set a standard prefix that will be prepended to all database tables \r
- * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.\r
- *\r
- * @param string $prefix Prefix that will be prepended.\r
- * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.\r
- * @return boolean TRUE if successful, else FALSE\r
- */\r
- function SetPrefix( $prefix = '', $underscore = TRUE ) {\r
- switch( TRUE ) {\r
- // clear prefix\r
- case empty( $prefix ):\r
- logMsg( 'Cleared prefix' );\r
- $this->objectPrefix = '';\r
- return TRUE;\r
- // prefix too long\r
- case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:\r
- // prefix contains invalid characters\r
- case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):\r
- logMsg( 'Invalid prefix: ' . $prefix );\r
- return FALSE;\r
- }\r
- \r
- if( $underscore AND substr( $prefix, -1 ) != '_' ) {\r
- $prefix .= '_';\r
- }\r
- \r
- // prefix valid\r
- logMsg( 'Set prefix: ' . $prefix );\r
- $this->objectPrefix = $prefix;\r
- return TRUE;\r
- }\r
- \r
- /**\r
- * Returns an object name with the current prefix prepended.\r
- *\r
- * @param string $name Name\r
- * @return string Prefixed name\r
- *\r
- * @access private\r
- */\r
- function prefix( $name = '' ) {\r
- // if prefix is set\r
- if( !empty( $this->objectPrefix ) ) {\r
- // Prepend the object prefix to the table name\r
- // prepend after quote if used\r
- return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );\r
- }\r
- \r
- // No prefix set. Use name provided.\r
- return $name;\r
- }\r
- \r
- /**\r
- * Checks if element references a specific platform\r
- *\r
- * @param string $platform Requested platform\r
- * @returns boolean TRUE if platform check succeeds\r
- *\r
- * @access private\r
- */\r
- function supportedPlatform( $platform = NULL ) {\r
- $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/';\r
- \r
- if( !isset( $platform ) OR preg_match( $regex, $platform ) ) {\r
- logMsg( "Platform $platform is supported" );\r
- return TRUE;\r
- } else {\r
- logMsg( "Platform $platform is NOT supported" );\r
- return FALSE;\r
- }\r
- }\r
- \r
- /**\r
- * Clears the array of generated SQL.\r
- *\r
- * @access private\r
- */\r
- function clearSQL() {\r
- $this->sqlArray = array();\r
- }\r
- \r
- /**\r
- * Adds SQL into the SQL array.\r
- *\r
- * @param mixed $sql SQL to Add\r
- * @return boolean TRUE if successful, else FALSE.\r
- *\r
- * @access private\r
- */ \r
- function addSQL( $sql = NULL ) {\r
- if( is_array( $sql ) ) {\r
- foreach( $sql as $line ) {\r
- $this->addSQL( $line );\r
- }\r
- \r
- return TRUE;\r
- }\r
- \r
- if( is_string( $sql ) ) {\r
- $this->sqlArray[] = $sql;\r
- \r
- // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.\r
- if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {\r
- $saved = $this->db->debug;\r
- $this->db->debug = $this->debug;\r
- $ok = $this->db->Execute( $sql );\r
- $this->db->debug = $saved;\r
- \r
- if( !$ok ) {\r
- if( $this->debug ) {\r
- ADOConnection::outp( $this->db->ErrorMsg() );\r
- }\r
- \r
- $this->success = 1;\r
- }\r
- }\r
- \r
- return TRUE;\r
- }\r
- \r
- return FALSE;\r
- }\r
- \r
- /**\r
- * Gets the SQL array in the specified format.\r
- *\r
- * @param string $format Format\r
- * @return mixed SQL\r
- * \r
- * @access private\r
- */\r
- function getSQL( $format = NULL, $sqlArray = NULL ) {\r
- if( !is_array( $sqlArray ) ) {\r
- $sqlArray = $this->sqlArray;\r
- }\r
- \r
- if( !is_array( $sqlArray ) ) {\r
- return FALSE;\r
- }\r
- \r
- switch( strtolower( $format ) ) {\r
- case 'string':\r
- case 'text':\r
- return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';\r
- case'html':\r
- return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';\r
- }\r
- \r
- return $this->sqlArray;\r
- }\r
- \r
- /**\r
- * Destroys an adoSchema object.\r
- *\r
- * Call this method to clean up after an adoSchema object that is no longer in use.\r
- * @deprecated adoSchema now cleans up automatically.\r
- */\r
- function Destroy() {\r
- set_magic_quotes_runtime( $this->mgq );\r
- unset( $this );\r
- }\r
-}\r
-\r
-/**\r
-* Message logging function\r
-*\r
-* @access private\r
-*/\r
-function logMsg( $msg, $title = NULL, $force = FALSE ) {\r
- if( XMLS_DEBUG or $force ) {\r
- echo '<pre>';\r
- \r
- if( isset( $title ) ) {\r
- echo '<h3>' . htmlentities( $title ) . '</h3>';\r
- }\r
- \r
- if( is_object( $this ) ) {\r
- echo '[' . get_class( $this ) . '] ';\r
- }\r
- \r
- print_r( $msg );\r
- \r
- echo '</pre>';\r
- }\r
-}\r
+<?php
+// Copyright (c) 2004 ars Cognita Inc., 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.
+*******************************************************************************/
+/**
+ * xmlschema is a class that allows the user to quickly and easily
+ * build a database on any ADOdb-supported platform using a simple
+ * XML schema.
+ *
+ * Last Editor: $Author$
+ * @author Richard Tango-Lowy & Dan Cech
+ * @version $Revision$
+ *
+ * @package axmls
+ * @tutorial getting_started.pkg
+ */
+
+function _file_get_contents($file)
+{
+ if (function_exists('file_get_contents')) return file_get_contents($file);
+
+ $f = fopen($file,'r');
+ if (!$f) return '';
+ $t = '';
+
+ while ($s = fread($f,100000)) $t .= $s;
+ fclose($f);
+ return $t;
+}
+
+
+/**
+* Debug on or off
+*/
+if( !defined( 'XMLS_DEBUG' ) ) {
+ define( 'XMLS_DEBUG', FALSE );
+}
+
+/**
+* Default prefix key
+*/
+if( !defined( 'XMLS_PREFIX' ) ) {
+ define( 'XMLS_PREFIX', '%%P' );
+}
+
+/**
+* Maximum length allowed for object prefix
+*/
+if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
+ define( 'XMLS_PREFIX_MAXLEN', 10 );
+}
+
+/**
+* Execute SQL inline as it is generated
+*/
+if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
+ define( 'XMLS_EXECUTE_INLINE', FALSE );
+}
+
+/**
+* Continue SQL Execution if an error occurs?
+*/
+if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
+ define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
+}
+
+/**
+* Current Schema Version
+*/
+if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
+ define( 'XMLS_SCHEMA_VERSION', '0.2' );
+}
+
+/**
+* Default Schema Version. Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
+ define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
+}
+
+/**
+* Default Schema Version. Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
+ define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
+}
+
+/**
+* Include the main ADODB library
+*/
+if( !defined( '_ADODB_LAYER' ) ) {
+ require( 'adodb.inc.php' );
+ require( 'adodb-datadict.inc.php' );
+}
+
+/**
+* Abstract DB Object. This class provides basic methods for database objects, such
+* as tables and indexes.
+*
+* @package axmls
+* @access private
+*/
+class dbObject {
+
+ /**
+ * var object Parent
+ */
+ var $parent;
+
+ /**
+ * var string current element
+ */
+ var $currentElement;
+
+ /**
+ * NOP
+ */
+ function dbObject( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+
+ }
+
+ function create() {
+ return array();
+ }
+
+ /**
+ * Destroys the object
+ */
+ function destroy() {
+ unset( $this );
+ }
+
+ /**
+ * Checks whether the specified RDBMS is supported by the current
+ * database object or its ranking ancestor.
+ *
+ * @param string $platform RDBMS platform name (from ADODB platform list).
+ * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
+ */
+ function supportedPlatform( $platform = NULL ) {
+ return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
+ }
+
+ /**
+ * Returns the prefix set by the ranking ancestor of the database object.
+ *
+ * @param string $name Prefix string.
+ * @return string Prefix.
+ */
+ function prefix( $name = '' ) {
+ return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
+ }
+
+ /**
+ * Extracts a field ID from the specified field.
+ *
+ * @param string $field Field.
+ * @return string Field ID.
+ */
+ function FieldID( $field ) {
+ return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
+ }
+}
+
+/**
+* Creates a table object in ADOdb's datadict format
+*
+* This class stores information about a database table. As charactaristics
+* of the table are loaded from the external source, methods and properties
+* of this class are used to build up the table description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbTable extends dbObject {
+
+ /**
+ * @var string Table name
+ */
+ var $name;
+
+ /**
+ * @var array Field specifier: Meta-information about each field
+ */
+ var $fields = array();
+
+ /**
+ * @var array List of table indexes.
+ */
+ var $indexes = array();
+
+ /**
+ * @var array Table options: Table-level options
+ */
+ var $opts = array();
+
+ /**
+ * @var string Field index: Keeps track of which field is currently being processed
+ */
+ var $current_field;
+
+ /**
+ * @var boolean Mark table for destruction
+ * @access private
+ */
+ var $drop_table;
+
+ /**
+ * @var boolean Mark field for destruction (not yet implemented)
+ * @access private
+ */
+ var $drop_field = array();
+
+ /**
+ * Iniitializes a new table object.
+ *
+ * @param string $prefix DB Object prefix
+ * @param array $attributes Array of table attributes.
+ */
+ function dbTable( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+ $this->name = $this->prefix($attributes['NAME']);
+ }
+
+ /**
+ * XML Callback to process start elements. Elements currently
+ * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT.
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'INDEX':
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ xml_set_object( $parser, $this->addIndex( $attributes ) );
+ }
+ break;
+ case 'DATA':
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ xml_set_object( $parser, $this->addData( $attributes ) );
+ }
+ break;
+ case 'DROP':
+ $this->drop();
+ break;
+ case 'FIELD':
+ // Add a field
+ $fieldName = $attributes['NAME'];
+ $fieldType = $attributes['TYPE'];
+ $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
+ $fieldOpts = isset( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
+
+ $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
+ break;
+ case 'KEY':
+ case 'NOTNULL':
+ case 'AUTOINCREMENT':
+ // Add a field option
+ $this->addFieldOpt( $this->current_field, $this->currentElement );
+ break;
+ case 'DEFAULT':
+ // Add a field option to the table object
+
+ // Work around ADOdb datadict issue that misinterprets empty strings.
+ if( $attributes['VALUE'] == '' ) {
+ $attributes['VALUE'] = " '' ";
+ }
+
+ $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
+ break;
+ case 'DEFDATE':
+ case 'DEFTIMESTAMP':
+ // Add a field option to the table object
+ $this->addFieldOpt( $this->current_field, $this->currentElement );
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Table constraint
+ case 'CONSTRAINT':
+ if( isset( $this->current_field ) ) {
+ $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
+ } else {
+ $this->addTableOpt( $cdata );
+ }
+ break;
+ // Table option
+ case 'OPT':
+ $this->addTableOpt( $cdata );
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'TABLE':
+ $this->parent->addSQL( $this->create( $this->parent ) );
+ xml_set_object( $parser, $this->parent );
+ $this->destroy();
+ break;
+ case 'FIELD':
+ unset($this->current_field);
+ break;
+
+ }
+ }
+
+ /**
+ * Adds an index to a table object
+ *
+ * @param array $attributes Index attributes
+ * @return object dbIndex object
+ */
+ function addIndex( $attributes ) {
+ $name = strtoupper( $attributes['NAME'] );
+ $this->indexes[$name] = new dbIndex( $this, $attributes );
+ return $this->indexes[$name];
+ }
+
+ /**
+ * Adds data to a table object
+ *
+ * @param array $attributes Data attributes
+ * @return object dbData object
+ */
+ function addData( $attributes ) {
+ if( !isset( $this->data ) ) {
+ $this->data = new dbData( $this, $attributes );
+ }
+ return $this->data;
+ }
+
+ /**
+ * Adds a field to a table object
+ *
+ * $name is the name of the table to which the field should be added.
+ * $type is an ADODB datadict field type. The following field types
+ * are supported as of ADODB 3.40:
+ * - C: varchar
+ * - X: CLOB (character large object) or largest varchar size
+ * if CLOB is not supported
+ * - C2: Multibyte varchar
+ * - X2: Multibyte CLOB
+ * - B: BLOB (binary large object)
+ * - D: Date (some databases do not support this, and we return a datetime type)
+ * - T: Datetime or Timestamp
+ * - L: Integer field suitable for storing booleans (0 or 1)
+ * - I: Integer (mapped to I4)
+ * - I1: 1-byte integer
+ * - I2: 2-byte integer
+ * - I4: 4-byte integer
+ * - I8: 8-byte integer
+ * - F: Floating point number
+ * - N: Numeric or decimal number
+ *
+ * @param string $name Name of the table to which the field will be added.
+ * @param string $type ADODB datadict field type.
+ * @param string $size Field size
+ * @param array $opts Field options array
+ * @return array Field specifier array
+ */
+ function addField( $name, $type, $size = NULL, $opts = NULL ) {
+ $field_id = $this->FieldID( $name );
+
+ // Set the field index so we know where we are
+ $this->current_field = $field_id;
+
+ // Set the field name (required)
+ $this->fields[$field_id]['NAME'] = $name;
+
+ // Set the field type (required)
+ $this->fields[$field_id]['TYPE'] = $type;
+
+ // Set the field size (optional)
+ if( isset( $size ) ) {
+ $this->fields[$field_id]['SIZE'] = $size;
+ }
+
+ // Set the field options
+ if( isset( $opts ) ) {
+ $this->fields[$field_id]['OPTS'][] = $opts;
+ }
+ }
+
+ /**
+ * Adds a field option to the current field specifier
+ *
+ * This method adds a field option allowed by the ADOdb datadict
+ * and appends it to the given field.
+ *
+ * @param string $field Field name
+ * @param string $opt ADOdb field option
+ * @param mixed $value Field option value
+ * @return array Field specifier array
+ */
+ function addFieldOpt( $field, $opt, $value = NULL ) {
+ if( !isset( $value ) ) {
+ $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
+ // Add the option and value
+ } else {
+ $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
+ }
+ }
+
+ /**
+ * Adds an option to the table
+ *
+ * This method takes a comma-separated list of table-level options
+ * and appends them to the table object.
+ *
+ * @param string $opt Table option
+ * @return array Options
+ */
+ function addTableOpt( $opt ) {
+ if(isset($this->currentPlatform)) {
+ $this->opts[$this->parent->db->databaseType] = $opt;
+ }
+ return $this->opts;
+ }
+
+
+ /**
+ * Generates the SQL that will create the table in the database
+ *
+ * @param object $xmls adoSchema object
+ * @return array Array containing table creation SQL
+ */
+ function create( &$xmls ) {
+ $sql = array();
+
+ // drop any existing indexes
+ if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
+ foreach( $legacy_indexes as $index => $index_details ) {
+ $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
+ }
+ }
+
+ // remove fields to be dropped from table object
+ foreach( $this->drop_field as $field ) {
+ unset( $this->fields[$field] );
+ }
+
+ // if table exists
+ if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
+ // drop table
+ if( $this->drop_table ) {
+ $sql[] = $xmls->dict->DropTableSQL( $this->name );
+
+ return $sql;
+ }
+
+ // drop any existing fields not in schema
+ foreach( $legacy_fields as $field_id => $field ) {
+ if( !isset( $this->fields[$field_id] ) ) {
+ $sql[] = $xmls->dict->DropColumnSQL( $this->name, '`'.$field->name.'`' );
+ }
+ }
+ // if table doesn't exist
+ } else {
+ if( $this->drop_table ) {
+ return $sql;
+ }
+
+ $legacy_fields = array();
+ }
+
+ // Loop through the field specifier array, building the associative array for the field options
+ $fldarray = array();
+
+ foreach( $this->fields as $field_id => $finfo ) {
+ // Set an empty size if it isn't supplied
+ if( !isset( $finfo['SIZE'] ) ) {
+ $finfo['SIZE'] = '';
+ }
+
+ // Initialize the field array with the type and size
+ $fldarray[$field_id] = array(
+ 'NAME' => $finfo['NAME'],
+ 'TYPE' => $finfo['TYPE'],
+ 'SIZE' => $finfo['SIZE']
+ );
+
+ // Loop through the options array and add the field options.
+ if( isset( $finfo['OPTS'] ) ) {
+ foreach( $finfo['OPTS'] as $opt ) {
+ // Option has an argument.
+ if( is_array( $opt ) ) {
+ $key = key( $opt );
+ $value = $opt[key( $opt )];
+ @$fldarray[$field_id][$key] .= $value;
+ // Option doesn't have arguments
+ } else {
+ $fldarray[$field_id][$opt] = $opt;
+ }
+ }
+ }
+ }
+
+ if( empty( $legacy_fields ) ) {
+ // Create the new table
+ $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+ logMsg( end( $sql ), 'Generated CreateTableSQL' );
+ } else {
+ // Upgrade an existing table
+ logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
+ switch( $xmls->upgrade ) {
+ // Use ChangeTableSQL
+ case 'ALTER':
+ logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
+ $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
+ break;
+ case 'REPLACE':
+ logMsg( 'Doing upgrade REPLACE (testing)' );
+ $sql[] = $xmls->dict->DropTableSQL( $this->name );
+ $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+ break;
+ // ignore table
+ default:
+ return array();
+ }
+ }
+
+ foreach( $this->indexes as $index ) {
+ $sql[] = $index->create( $xmls );
+ }
+
+ if( isset( $this->data ) ) {
+ $sql[] = $this->data->create( $xmls );
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Marks a field or table for destruction
+ */
+ function drop() {
+ if( isset( $this->current_field ) ) {
+ // Drop the current field
+ logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
+ // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
+ $this->drop_field[$this->current_field] = $this->current_field;
+ } else {
+ // Drop the current table
+ logMsg( "Dropping table '{$this->name}'" );
+ // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
+ $this->drop_table = TRUE;
+ }
+ }
+}
+
+/**
+* Creates an index object in ADOdb's datadict format
+*
+* This class stores information about a database index. As charactaristics
+* of the index are loaded from the external source, methods and properties
+* of this class are used to build up the index description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbIndex extends dbObject {
+
+ /**
+ * @var string Index name
+ */
+ var $name;
+
+ /**
+ * @var array Index options: Index-level options
+ */
+ var $opts = array();
+
+ /**
+ * @var array Indexed fields: Table columns included in this index
+ */
+ var $columns = array();
+
+ /**
+ * @var boolean Mark index for destruction
+ * @access private
+ */
+ var $drop = FALSE;
+
+ /**
+ * Initializes the new dbIndex object.
+ *
+ * @param object $parent Parent object
+ * @param array $attributes Attributes
+ *
+ * @internal
+ */
+ function dbIndex( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+
+ $this->name = $this->prefix ($attributes['NAME']);
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * Processes XML opening tags.
+ * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH.
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'DROP':
+ $this->drop();
+ break;
+ case 'CLUSTERED':
+ case 'BITMAP':
+ case 'UNIQUE':
+ case 'FULLTEXT':
+ case 'HASH':
+ // Add index Option
+ $this->addIndexOpt( $this->currentElement );
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * Processes XML cdata.
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Index field name
+ case 'COL':
+ $this->addField( $cdata );
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'INDEX':
+ xml_set_object( $parser, $this->parent );
+ break;
+ }
+ }
+
+ /**
+ * Adds a field to the index
+ *
+ * @param string $name Field name
+ * @return string Field list
+ */
+ function addField( $name ) {
+ $this->columns[$this->FieldID( $name )] = $name;
+
+ // Return the field list
+ return $this->columns;
+ }
+
+ /**
+ * Adds options to the index
+ *
+ * @param string $opt Comma-separated list of index options.
+ * @return string Option list
+ */
+ function addIndexOpt( $opt ) {
+ $this->opts[] = $opt;
+
+ // Return the options list
+ return $this->opts;
+ }
+
+ /**
+ * Generates the SQL that will create the index in the database
+ *
+ * @param object $xmls adoSchema object
+ * @return array Array containing index creation SQL
+ */
+ function create( &$xmls ) {
+ if( $this->drop ) {
+ return NULL;
+ }
+
+ // eliminate any columns that aren't in the table
+ foreach( $this->columns as $id => $col ) {
+ if( !isset( $this->parent->fields[$id] ) ) {
+ unset( $this->columns[$id] );
+ }
+ }
+
+ return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
+ }
+
+ /**
+ * Marks an index for destruction
+ */
+ function drop() {
+ $this->drop = TRUE;
+ }
+}
+
+/**
+* Creates a data object in ADOdb's datadict format
+*
+* This class stores information about table data.
+*
+* @package axmls
+* @access private
+*/
+class dbData extends dbObject {
+
+ var $data = array();
+
+ var $row;
+
+ /**
+ * Initializes the new dbIndex object.
+ *
+ * @param object $parent Parent object
+ * @param array $attributes Attributes
+ *
+ * @internal
+ */
+ function dbData( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * Processes XML opening tags.
+ * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH.
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'ROW':
+ $this->row = count( $this->data );
+ $this->data[$this->row] = array();
+ break;
+ case 'F':
+ $this->addField($attributes);
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * Processes XML cdata.
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Index field name
+ case 'F':
+ $this->addData( $cdata );
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'DATA':
+ xml_set_object( $parser, $this->parent );
+ break;
+ }
+ }
+
+ /**
+ * Adds a field to the index
+ *
+ * @param string $name Field name
+ * @return string Field list
+ */
+ function addField( $attributes ) {
+ if( isset( $attributes['NAME'] ) ) {
+ $name = $attributes['NAME'];
+ } else {
+ $name = count($this->data[$this->row]);
+ }
+
+ // Set the field index so we know where we are
+ $this->current_field = $this->FieldID( $name );
+ }
+
+ /**
+ * Adds options to the index
+ *
+ * @param string $opt Comma-separated list of index options.
+ * @return string Option list
+ */
+ function addData( $cdata ) {
+ if( !isset( $this->data[$this->row] ) ) {
+ $this->data[$this->row] = array();
+ }
+
+ if( !isset( $this->data[$this->row][$this->current_field] ) ) {
+ $this->data[$this->row][$this->current_field] = '';
+ }
+
+ $this->data[$this->row][$this->current_field] .= $cdata;
+ }
+
+ /**
+ * Generates the SQL that will create the index in the database
+ *
+ * @param object $xmls adoSchema object
+ * @return array Array containing index creation SQL
+ */
+ function create( &$xmls ) {
+ $table = $xmls->dict->TableName($this->parent->name);
+ $table_field_count = count($this->parent->fields);
+ $sql = array();
+
+ // eliminate any columns that aren't in the table
+ foreach( $this->data as $row ) {
+ $table_fields = $this->parent->fields;
+ $fields = array();
+
+ foreach( $row as $field_id => $field_data ) {
+ if( !array_key_exists( $field_id, $table_fields ) ) {
+ if( is_numeric( $field_id ) ) {
+ $field_id = reset( array_keys( $table_fields ) );
+ } else {
+ continue;
+ }
+ }
+
+ $name = $table_fields[$field_id]['NAME'];
+
+ switch( $table_fields[$field_id]['TYPE'] ) {
+ case 'C':
+ case 'C2':
+ case 'X':
+ case 'X2':
+ $fields[$name] = $xmls->db->qstr( $field_data );
+ break;
+ case 'I':
+ case 'I1':
+ case 'I2':
+ case 'I4':
+ case 'I8':
+ $fields[$name] = intval($field_data);
+ break;
+ default:
+ $fields[$name] = $field_data;
+ }
+
+ unset($table_fields[$field_id]);
+ }
+
+ // check that at least 1 column is specified
+ if( empty( $fields ) ) {
+ continue;
+ }
+
+ // check that no required columns are missing
+ if( count( $fields ) < $table_field_count ) {
+ foreach( $table_fields as $field ) {
+ if (isset( $field['OPTS'] ))
+ if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
+ continue(2);
+ }
+ }
+ }
+
+ $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
+ }
+
+ return $sql;
+ }
+}
+
+/**
+* Creates the SQL to execute a list of provided SQL queries
+*
+* @package axmls
+* @access private
+*/
+class dbQuerySet extends dbObject {
+
+ /**
+ * @var array List of SQL queries
+ */
+ var $queries = array();
+
+ /**
+ * @var string String used to build of a query line by line
+ */
+ var $query;
+
+ /**
+ * @var string Query prefix key
+ */
+ var $prefixKey = '';
+
+ /**
+ * @var boolean Auto prefix enable (TRUE)
+ */
+ var $prefixMethod = 'AUTO';
+
+ /**
+ * Initializes the query set.
+ *
+ * @param object $parent Parent object
+ * @param array $attributes Attributes
+ */
+ function dbQuerySet( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+
+ // Overrides the manual prefix key
+ if( isset( $attributes['KEY'] ) ) {
+ $this->prefixKey = $attributes['KEY'];
+ }
+
+ $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
+
+ // Enables or disables automatic prefix prepending
+ switch( $prefixMethod ) {
+ case 'AUTO':
+ $this->prefixMethod = 'AUTO';
+ break;
+ case 'MANUAL':
+ $this->prefixMethod = 'MANUAL';
+ break;
+ case 'NONE':
+ $this->prefixMethod = 'NONE';
+ break;
+ }
+ }
+
+ /**
+ * XML Callback to process start elements. Elements currently
+ * processed are: QUERY.
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'QUERY':
+ // Create a new query in a SQL queryset.
+ // Ignore this query set if a platform is specified and it's different than the
+ // current connection platform.
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ $this->newQuery();
+ } else {
+ $this->discardQuery();
+ }
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Line of queryset SQL data
+ case 'QUERY':
+ $this->buildQuery( $cdata );
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'QUERY':
+ // Add the finished query to the open query set.
+ $this->addQuery();
+ break;
+ case 'SQL':
+ $this->parent->addSQL( $this->create( $this->parent ) );
+ xml_set_object( $parser, $this->parent );
+ $this->destroy();
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * Re-initializes the query.
+ *
+ * @return boolean TRUE
+ */
+ function newQuery() {
+ $this->query = '';
+
+ return TRUE;
+ }
+
+ /**
+ * Discards the existing query.
+ *
+ * @return boolean TRUE
+ */
+ function discardQuery() {
+ unset( $this->query );
+
+ return TRUE;
+ }
+
+ /**
+ * Appends a line to a query that is being built line by line
+ *
+ * @param string $data Line of SQL data or NULL to initialize a new query
+ * @return string SQL query string.
+ */
+ function buildQuery( $sql = NULL ) {
+ if( !isset( $this->query ) OR empty( $sql ) ) {
+ return FALSE;
+ }
+
+ $this->query .= $sql;
+
+ return $this->query;
+ }
+
+ /**
+ * Adds a completed query to the query list
+ *
+ * @return string SQL of added query
+ */
+ function addQuery() {
+ if( !isset( $this->query ) ) {
+ return FALSE;
+ }
+
+ $this->queries[] = $return = trim($this->query);
+
+ unset( $this->query );
+
+ return $return;
+ }
+
+ /**
+ * Creates and returns the current query set
+ *
+ * @param object $xmls adoSchema object
+ * @return array Query set
+ */
+ function create( &$xmls ) {
+ foreach( $this->queries as $id => $query ) {
+ switch( $this->prefixMethod ) {
+ case 'AUTO':
+ // Enable auto prefix replacement
+
+ // Process object prefix.
+ // Evaluate SQL statements to prepend prefix to objects
+ $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+ $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+ $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+
+ // SELECT statements aren't working yet
+ #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
+
+ case 'MANUAL':
+ // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.
+ // If prefixKey is not set, we use the default constant XMLS_PREFIX
+ if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
+ // Enable prefix override
+ $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
+ } else {
+ // Use default replacement
+ $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
+ }
+ }
+
+ $this->queries[$id] = trim( $query );
+ }
+
+ // Return the query set array
+ return $this->queries;
+ }
+
+ /**
+ * Rebuilds the query with the prefix attached to any objects
+ *
+ * @param string $regex Regex used to add prefix
+ * @param string $query SQL query string
+ * @param string $prefix Prefix to be appended to tables, indices, etc.
+ * @return string Prefixed SQL query string.
+ */
+ function prefixQuery( $regex, $query, $prefix = NULL ) {
+ if( !isset( $prefix ) ) {
+ return $query;
+ }
+
+ if( preg_match( $regex, $query, $match ) ) {
+ $preamble = $match[1];
+ $postamble = $match[5];
+ $objectList = explode( ',', $match[3] );
+ // $prefix = $prefix . '_';
+
+ $prefixedList = '';
+
+ foreach( $objectList as $object ) {
+ if( $prefixedList !== '' ) {
+ $prefixedList .= ', ';
+ }
+
+ $prefixedList .= $prefix . trim( $object );
+ }
+
+ $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
+ }
+
+ return $query;
+ }
+}
+
+/**
+* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements
+*
+* This class is used to load and parse the XML file, to create an array of SQL statements
+* that can be used to build a database, and to build the database using the SQL array.
+*
+* @tutorial getting_started.pkg
+*
+* @author Richard Tango-Lowy & Dan Cech
+* @version $Revision$
+*
+* @package axmls
+*/
+class adoSchema {
+
+ /**
+ * @var array Array containing SQL queries to generate all objects
+ * @access private
+ */
+ var $sqlArray;
+
+ /**
+ * @var object ADOdb connection object
+ * @access private
+ */
+ var $db;
+
+ /**
+ * @var object ADOdb Data Dictionary
+ * @access private
+ */
+ var $dict;
+
+ /**
+ * @var string Current XML element
+ * @access private
+ */
+ var $currentElement = '';
+
+ /**
+ * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
+ * @access private
+ */
+ var $upgrade = '';
+
+ /**
+ * @var string Optional object prefix
+ * @access private
+ */
+ var $objectPrefix = '';
+
+ /**
+ * @var long Original Magic Quotes Runtime value
+ * @access private
+ */
+ var $mgq;
+
+ /**
+ * @var long System debug
+ * @access private
+ */
+ var $debug;
+
+ /**
+ * @var string Regular expression to find schema version
+ * @access private
+ */
+ var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
+
+ /**
+ * @var string Current schema version
+ * @access private
+ */
+ var $schemaVersion;
+
+ /**
+ * @var int Success of last Schema execution
+ */
+ var $success;
+
+ /**
+ * @var bool Execute SQL inline as it is generated
+ */
+ var $executeInline;
+
+ /**
+ * @var bool Continue SQL execution if errors occur
+ */
+ var $continueOnError;
+
+ /**
+ * Creates an adoSchema object
+ *
+ * Creating an adoSchema object is the first step in processing an XML schema.
+ * The only parameter is an ADOdb database connection object, which must already
+ * have been created.
+ *
+ * @param object $db ADOdb database connection object.
+ */
+ function adoSchema( &$db ) {
+ // Initialize the environment
+ $this->mgq = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+
+ $this->db = $db;
+ $this->debug = $this->db->debug;
+ $this->dict = NewDataDictionary( $this->db );
+ $this->sqlArray = array();
+ $this->schemaVersion = XMLS_SCHEMA_VERSION;
+ $this->executeInline( XMLS_EXECUTE_INLINE );
+ $this->continueOnError( XMLS_CONTINUE_ON_ERROR );
+ $this->setUpgradeMethod();
+ }
+
+ /**
+ * Sets the method to be used for upgrading an existing database
+ *
+ * Use this method to specify how existing database objects should be upgraded.
+ * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to
+ * alter each database object directly, REPLACE attempts to rebuild each object
+ * from scratch, BEST attempts to determine the best upgrade method for each
+ * object, and NONE disables upgrading.
+ *
+ * This method is not yet used by AXMLS, but exists for backward compatibility.
+ * The ALTER method is automatically assumed when the adoSchema object is
+ * instantiated; other upgrade methods are not currently supported.
+ *
+ * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)
+ * @returns string Upgrade method used
+ */
+ function SetUpgradeMethod( $method = '' ) {
+ if( !is_string( $method ) ) {
+ return FALSE;
+ }
+
+ $method = strtoupper( $method );
+
+ // Handle the upgrade methods
+ switch( $method ) {
+ case 'ALTER':
+ $this->upgrade = $method;
+ break;
+ case 'REPLACE':
+ $this->upgrade = $method;
+ break;
+ case 'BEST':
+ $this->upgrade = 'ALTER';
+ break;
+ case 'NONE':
+ $this->upgrade = 'NONE';
+ break;
+ default:
+ // Use default if no legitimate method is passed.
+ $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
+ }
+
+ return $this->upgrade;
+ }
+
+ /**
+ * Enables/disables inline SQL execution.
+ *
+ * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),
+ * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode
+ * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()
+ * to apply the schema to the database.
+ *
+ * @param bool $mode execute
+ * @return bool current execution mode
+ *
+ * @see ParseSchema(), ExecuteSchema()
+ */
+ function ExecuteInline( $mode = NULL ) {
+ if( is_bool( $mode ) ) {
+ $this->executeInline = $mode;
+ }
+
+ return $this->executeInline;
+ }
+
+ /**
+ * Enables/disables SQL continue on error.
+ *
+ * Call this method to enable or disable continuation of SQL execution if an error occurs.
+ * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.
+ * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing
+ * of the schema will continue.
+ *
+ * @param bool $mode execute
+ * @return bool current continueOnError mode
+ *
+ * @see addSQL(), ExecuteSchema()
+ */
+ function ContinueOnError( $mode = NULL ) {
+ if( is_bool( $mode ) ) {
+ $this->continueOnError = $mode;
+ }
+
+ return $this->continueOnError;
+ }
+
+ /**
+ * Loads an XML schema from a file and converts it to SQL.
+ *
+ * Call this method to load the specified schema (see the DTD for the proper format) from
+ * the filesystem and generate the SQL necessary to create the database described.
+ * @see ParseSchemaString()
+ *
+ * @param string $file Name of XML schema file.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute
+ */
+ function ParseSchema( $filename, $returnSchema = FALSE ) {
+ return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+ }
+
+ /**
+ * Loads an XML schema from a file and converts it to SQL.
+ *
+ * Call this method to load the specified schema from a file (see the DTD for the proper format)
+ * and generate the SQL necessary to create the database described by the schema.
+ *
+ * @param string $file Name of XML schema file.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute.
+ *
+ * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()
+ * @see ParseSchema(), ParseSchemaString()
+ */
+ function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
+ // Open the file
+ if( !($fp = fopen( $filename, 'r' )) ) {
+ // die( 'Unable to open file' );
+ return FALSE;
+ }
+
+ // do version detection here
+ if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
+ return FALSE;
+ }
+
+ if ( $returnSchema )
+ {
+ $xmlstring = '';
+ while( $data = fread( $fp, 100000 ) ) {
+ $xmlstring .= $data;
+ }
+ return $xmlstring;
+ }
+
+ $this->success = 2;
+
+ $xmlParser = $this->create_parser();
+
+ // Process the file
+ while( $data = fread( $fp, 4096 ) ) {
+ if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
+ die( sprintf(
+ "XML error: %s at line %d",
+ xml_error_string( xml_get_error_code( $xmlParser) ),
+ xml_get_current_line_number( $xmlParser)
+ ) );
+ }
+ }
+
+ xml_parser_free( $xmlParser );
+
+ return $this->sqlArray;
+ }
+
+ /**
+ * Converts an XML schema string to SQL.
+ *
+ * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+ * and generate the SQL necessary to create the database described by the schema.
+ * @see ParseSchema()
+ *
+ * @param string $xmlstring XML schema string.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute.
+ */
+ function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
+ if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+ return FALSE;
+ }
+
+ // do version detection here
+ if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
+ return FALSE;
+ }
+
+ if ( $returnSchema )
+ {
+ return $xmlstring;
+ }
+
+ $this->success = 2;
+
+ $xmlParser = $this->create_parser();
+
+ if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
+ die( sprintf(
+ "XML error: %s at line %d",
+ xml_error_string( xml_get_error_code( $xmlParser) ),
+ xml_get_current_line_number( $xmlParser)
+ ) );
+ }
+
+ xml_parser_free( $xmlParser );
+
+ return $this->sqlArray;
+ }
+
+ /**
+ * Loads an XML schema from a file and converts it to uninstallation SQL.
+ *
+ * Call this method to load the specified schema (see the DTD for the proper format) from
+ * the filesystem and generate the SQL necessary to remove the database described.
+ * @see RemoveSchemaString()
+ *
+ * @param string $file Name of XML schema file.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute
+ */
+ function RemoveSchema( $filename, $returnSchema = FALSE ) {
+ return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+ }
+
+ /**
+ * Converts an XML schema string to uninstallation SQL.
+ *
+ * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+ * and generate the SQL necessary to uninstall the database described by the schema.
+ * @see RemoveSchema()
+ *
+ * @param string $schema XML schema string.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute.
+ */
+ function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
+
+ // grab current version
+ if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+ return FALSE;
+ }
+
+ return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
+ }
+
+ /**
+ * Applies the current XML schema to the database (post execution).
+ *
+ * Call this method to apply the current schema (generally created by calling
+ * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes,
+ * and executing other SQL specified in the schema) after parsing.
+ * @see ParseSchema(), ParseSchemaString(), ExecuteInline()
+ *
+ * @param array $sqlArray Array of SQL statements that will be applied rather than
+ * the current schema.
+ * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.
+ * @returns integer 0 if failure, 1 if errors, 2 if successful.
+ */
+ function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) {
+ if( !is_bool( $continueOnErr ) ) {
+ $continueOnErr = $this->ContinueOnError();
+ }
+
+ if( !isset( $sqlArray ) ) {
+ $sqlArray = $this->sqlArray;
+ }
+
+ if( !is_array( $sqlArray ) ) {
+ $this->success = 0;
+ } else {
+ $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
+ }
+
+ return $this->success;
+ }
+
+ /**
+ * Returns the current SQL array.
+ *
+ * Call this method to fetch the array of SQL queries resulting from
+ * ParseSchema() or ParseSchemaString().
+ *
+ * @param string $format Format: HTML, TEXT, or NONE (PHP array)
+ * @return array Array of SQL statements or FALSE if an error occurs
+ */
+ function PrintSQL( $format = 'NONE' ) {
+ $sqlArray = null;
+ return $this->getSQL( $format, $sqlArray );
+ }
+
+ /**
+ * Saves the current SQL array to the local filesystem as a list of SQL queries.
+ *
+ * Call this method to save the array of SQL queries (generally resulting from a
+ * parsed XML schema) to the filesystem.
+ *
+ * @param string $filename Path and name where the file should be saved.
+ * @return boolean TRUE if save is successful, else FALSE.
+ */
+ function SaveSQL( $filename = './schema.sql' ) {
+
+ if( !isset( $sqlArray ) ) {
+ $sqlArray = $this->sqlArray;
+ }
+ if( !isset( $sqlArray ) ) {
+ return FALSE;
+ }
+
+ $fp = fopen( $filename, "w" );
+
+ foreach( $sqlArray as $key => $query ) {
+ fwrite( $fp, $query . ";\n" );
+ }
+ fclose( $fp );
+ }
+
+ /**
+ * Create an xml parser
+ *
+ * @return object PHP XML parser object
+ *
+ * @access private
+ */
+ function create_parser() {
+ // Create the parser
+ $xmlParser = xml_parser_create();
+ xml_set_object( $xmlParser, $this );
+
+ // Initialize the XML callback functions
+ xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
+ xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
+
+ return $xmlParser;
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ switch( strtoupper( $tag ) ) {
+ case 'TABLE':
+ $this->obj = new dbTable( $this, $attributes );
+ xml_set_object( $parser, $this->obj );
+ break;
+ case 'SQL':
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ $this->obj = new dbQuerySet( $this, $attributes );
+ xml_set_object( $parser, $this->obj );
+ }
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ * @internal
+ */
+ function _tag_close( &$parser, $tag ) {
+
+ }
+
+ /**
+ * Converts an XML schema string to the specified DTD version.
+ *
+ * Call this method to convert a string containing an XML schema to a different AXMLS
+ * DTD version. For instance, to convert a schema created for an pre-1.0 version for
+ * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version
+ * parameter is specified, the schema will be converted to the current DTD version.
+ * If the newFile parameter is provided, the converted schema will be written to the specified
+ * file.
+ * @see ConvertSchemaFile()
+ *
+ * @param string $schema String containing XML schema that will be converted.
+ * @param string $newVersion DTD version to convert to.
+ * @param string $newFile File name of (converted) output file.
+ * @return string Converted XML schema or FALSE if an error occurs.
+ */
+ function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
+
+ // grab current version
+ if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+ return FALSE;
+ }
+
+ if( !isset ($newVersion) ) {
+ $newVersion = $this->schemaVersion;
+ }
+
+ if( $version == $newVersion ) {
+ $result = $schema;
+ } else {
+ $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
+ }
+
+ if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+ fwrite( $fp, $result );
+ fclose( $fp );
+ }
+
+ return $result;
+ }
+
+ // compat for pre-4.3 - jlim
+ function _file_get_contents($path)
+ {
+ if (function_exists('file_get_contents')) return file_get_contents($path);
+ return join('',file($path));
+ }
+
+ /**
+ * Converts an XML schema file to the specified DTD version.
+ *
+ * Call this method to convert the specified XML schema file to a different AXMLS
+ * DTD version. For instance, to convert a schema created for an pre-1.0 version for
+ * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version
+ * parameter is specified, the schema will be converted to the current DTD version.
+ * If the newFile parameter is provided, the converted schema will be written to the specified
+ * file.
+ * @see ConvertSchemaString()
+ *
+ * @param string $filename Name of XML schema file that will be converted.
+ * @param string $newVersion DTD version to convert to.
+ * @param string $newFile File name of (converted) output file.
+ * @return string Converted XML schema or FALSE if an error occurs.
+ */
+ function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
+
+ // grab current version
+ if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
+ return FALSE;
+ }
+
+ if( !isset ($newVersion) ) {
+ $newVersion = $this->schemaVersion;
+ }
+
+ if( $version == $newVersion ) {
+ $result = _file_get_contents( $filename );
+
+ // remove unicode BOM if present
+ if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
+ $result = substr( $result, 3 );
+ }
+ } else {
+ $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
+ }
+
+ if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+ fwrite( $fp, $result );
+ fclose( $fp );
+ }
+
+ return $result;
+ }
+
+ function TransformSchema( $schema, $xsl, $schematype='string' )
+ {
+ // Fail if XSLT extension is not available
+ if( ! function_exists( 'xslt_create' ) ) {
+ return FALSE;
+ }
+
+ $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
+
+ // look for xsl
+ if( !is_readable( $xsl_file ) ) {
+ return FALSE;
+ }
+
+ switch( $schematype )
+ {
+ case 'file':
+ if( !is_readable( $schema ) ) {
+ return FALSE;
+ }
+
+ $schema = _file_get_contents( $schema );
+ break;
+ case 'string':
+ default:
+ if( !is_string( $schema ) ) {
+ return FALSE;
+ }
+ }
+
+ $arguments = array (
+ '/_xml' => $schema,
+ '/_xsl' => _file_get_contents( $xsl_file )
+ );
+
+ // create an XSLT processor
+ $xh = xslt_create ();
+
+ // set error handler
+ xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
+
+ // process the schema
+ $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments);
+
+ xslt_free ($xh);
+
+ return $result;
+ }
+
+ /**
+ * Processes XSLT transformation errors
+ *
+ * @param object $parser XML parser object
+ * @param integer $errno Error number
+ * @param integer $level Error level
+ * @param array $fields Error information fields
+ *
+ * @access private
+ */
+ function xslt_error_handler( $parser, $errno, $level, $fields ) {
+ if( is_array( $fields ) ) {
+ $msg = array(
+ 'Message Type' => ucfirst( $fields['msgtype'] ),
+ 'Message Code' => $fields['code'],
+ 'Message' => $fields['msg'],
+ 'Error Number' => $errno,
+ 'Level' => $level
+ );
+
+ switch( $fields['URI'] ) {
+ case 'arg:/_xml':
+ $msg['Input'] = 'XML';
+ break;
+ case 'arg:/_xsl':
+ $msg['Input'] = 'XSL';
+ break;
+ default:
+ $msg['Input'] = $fields['URI'];
+ }
+
+ $msg['Line'] = $fields['line'];
+ } else {
+ $msg = array(
+ 'Message Type' => 'Error',
+ 'Error Number' => $errno,
+ 'Level' => $level,
+ 'Fields' => var_export( $fields, TRUE )
+ );
+ }
+
+ $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
+ . '<table>' . "\n";
+
+ foreach( $msg as $label => $details ) {
+ $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
+ }
+
+ $error_details .= '</table>';
+
+ trigger_error( $error_details, E_USER_ERROR );
+ }
+
+ /**
+ * Returns the AXMLS Schema Version of the requested XML schema file.
+ *
+ * Call this method to obtain the AXMLS DTD version of the requested XML schema file.
+ * @see SchemaStringVersion()
+ *
+ * @param string $filename AXMLS schema file
+ * @return string Schema version number or FALSE on error
+ */
+ function SchemaFileVersion( $filename ) {
+ // Open the file
+ if( !($fp = fopen( $filename, 'r' )) ) {
+ // die( 'Unable to open file' );
+ return FALSE;
+ }
+
+ // Process the file
+ while( $data = fread( $fp, 4096 ) ) {
+ if( preg_match( $this->versionRegex, $data, $matches ) ) {
+ return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+ }
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Returns the AXMLS Schema Version of the provided XML schema string.
+ *
+ * Call this method to obtain the AXMLS DTD version of the provided XML schema string.
+ * @see SchemaFileVersion()
+ *
+ * @param string $xmlstring XML schema string
+ * @return string Schema version number or FALSE on error
+ */
+ function SchemaStringVersion( $xmlstring ) {
+ if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+ return FALSE;
+ }
+
+ if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
+ return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Extracts an XML schema from an existing database.
+ *
+ * Call this method to create an XML schema string from an existing database.
+ * If the data parameter is set to TRUE, AXMLS will include the data from the database
+ * in the schema.
+ *
+ * @param boolean $data Include data in schema dump
+ * @return string Generated XML schema
+ */
+ function ExtractSchema( $data = FALSE ) {
+ $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
+
+ $schema = '<?xml version="1.0"?>' . "\n"
+ . '<schema version="' . $this->schemaVersion . '">' . "\n";
+
+ if( is_array( $tables = $this->db->MetaTables( 'TABLES' ) ) ) {
+ foreach( $tables as $table ) {
+ $schema .= ' <table name="' . $table . '">' . "\n";
+
+ // grab details from database
+ $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE 1=1' );
+ $fields = $this->db->MetaColumns( $table );
+ $indexes = $this->db->MetaIndexes( $table );
+
+ if( is_array( $fields ) ) {
+ foreach( $fields as $details ) {
+ $extra = '';
+ $content = array();
+
+ if( $details->max_length > 0 ) {
+ $extra .= ' size="' . $details->max_length . '"';
+ }
+
+ if( $details->primary_key ) {
+ $content[] = '<KEY/>';
+ } elseif( $details->not_null ) {
+ $content[] = '<NOTNULL/>';
+ }
+
+ if( $details->has_default ) {
+ $content[] = '<DEFAULT value="' . $details->default_value . '"/>';
+ }
+
+ if( $details->auto_increment ) {
+ $content[] = '<AUTOINCREMENT/>';
+ }
+
+ // this stops the creation of 'R' columns,
+ // AUTOINCREMENT is used to create auto columns
+ $details->primary_key = 0;
+ $type = $rs->MetaType( $details );
+
+ $schema .= ' <field name="' . $details->name . '" type="' . $type . '"' . $extra . '>';
+
+ if( !empty( $content ) ) {
+ $schema .= "\n " . implode( "\n ", $content ) . "\n ";
+ }
+
+ $schema .= '</field>' . "\n";
+ }
+ }
+
+ if( is_array( $indexes ) ) {
+ foreach( $indexes as $index => $details ) {
+ $schema .= ' <index name="' . $index . '">' . "\n";
+
+ if( $details['unique'] ) {
+ $schema .= ' <UNIQUE/>' . "\n";
+ }
+
+ foreach( $details['columns'] as $column ) {
+ $schema .= ' <col>' . $column . '</col>' . "\n";
+ }
+
+ $schema .= ' </index>' . "\n";
+ }
+ }
+
+ if( $data ) {
+ $rs = $this->db->Execute( 'SELECT * FROM ' . $table );
+
+ if( is_object( $rs ) ) {
+ $schema .= ' <data>' . "\n";
+
+ while( $row = $rs->FetchRow() ) {
+ foreach( $row as $key => $val ) {
+ $row[$key] = htmlentities($val);
+ }
+
+ $schema .= ' <row><f>' . implode( '</f><f>', $row ) . '</f></row>' . "\n";
+ }
+
+ $schema .= ' </data>' . "\n";
+ }
+ }
+
+ $schema .= ' </table>' . "\n";
+ }
+ }
+
+ $this->db->SetFetchMode( $old_mode );
+
+ $schema .= '</schema>';
+ return $schema;
+ }
+
+ /**
+ * Sets a prefix for database objects
+ *
+ * Call this method to set a standard prefix that will be prepended to all database tables
+ * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.
+ *
+ * @param string $prefix Prefix that will be prepended.
+ * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.
+ * @return boolean TRUE if successful, else FALSE
+ */
+ function SetPrefix( $prefix = '', $underscore = TRUE ) {
+ switch( TRUE ) {
+ // clear prefix
+ case empty( $prefix ):
+ logMsg( 'Cleared prefix' );
+ $this->objectPrefix = '';
+ return TRUE;
+ // prefix too long
+ case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:
+ // prefix contains invalid characters
+ case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):
+ logMsg( 'Invalid prefix: ' . $prefix );
+ return FALSE;
+ }
+
+ if( $underscore AND substr( $prefix, -1 ) != '_' ) {
+ $prefix .= '_';
+ }
+
+ // prefix valid
+ logMsg( 'Set prefix: ' . $prefix );
+ $this->objectPrefix = $prefix;
+ return TRUE;
+ }
+
+ /**
+ * Returns an object name with the current prefix prepended.
+ *
+ * @param string $name Name
+ * @return string Prefixed name
+ *
+ * @access private
+ */
+ function prefix( $name = '' ) {
+ // if prefix is set
+ if( !empty( $this->objectPrefix ) ) {
+ // Prepend the object prefix to the table name
+ // prepend after quote if used
+ return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );
+ }
+
+ // No prefix set. Use name provided.
+ return $name;
+ }
+
+ /**
+ * Checks if element references a specific platform
+ *
+ * @param string $platform Requested platform
+ * @returns boolean TRUE if platform check succeeds
+ *
+ * @access private
+ */
+ function supportedPlatform( $platform = NULL ) {
+ $regex = '/^(\w*\|)*' . $this->db->databaseType . '(\|\w*)*$/';
+
+ if( !isset( $platform ) OR preg_match( $regex, $platform ) ) {
+ logMsg( "Platform $platform is supported" );
+ return TRUE;
+ } else {
+ logMsg( "Platform $platform is NOT supported" );
+ return FALSE;
+ }
+ }
+
+ /**
+ * Clears the array of generated SQL.
+ *
+ * @access private
+ */
+ function clearSQL() {
+ $this->sqlArray = array();
+ }
+
+ /**
+ * Adds SQL into the SQL array.
+ *
+ * @param mixed $sql SQL to Add
+ * @return boolean TRUE if successful, else FALSE.
+ *
+ * @access private
+ */
+ function addSQL( $sql = NULL ) {
+ if( is_array( $sql ) ) {
+ foreach( $sql as $line ) {
+ $this->addSQL( $line );
+ }
+
+ return TRUE;
+ }
+
+ if( is_string( $sql ) ) {
+ $this->sqlArray[] = $sql;
+
+ // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.
+ if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {
+ $saved = $this->db->debug;
+ $this->db->debug = $this->debug;
+ $ok = $this->db->Execute( $sql );
+ $this->db->debug = $saved;
+
+ if( !$ok ) {
+ if( $this->debug ) {
+ ADOConnection::outp( $this->db->ErrorMsg() );
+ }
+
+ $this->success = 1;
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Gets the SQL array in the specified format.
+ *
+ * @param string $format Format
+ * @return mixed SQL
+ *
+ * @access private
+ */
+ function getSQL( $format = NULL, $sqlArray = NULL ) {
+ if( !is_array( $sqlArray ) ) {
+ $sqlArray = $this->sqlArray;
+ }
+
+ if( !is_array( $sqlArray ) ) {
+ return FALSE;
+ }
+
+ switch( strtolower( $format ) ) {
+ case 'string':
+ case 'text':
+ return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';
+ case'html':
+ return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';
+ }
+
+ return $this->sqlArray;
+ }
+
+ /**
+ * Destroys an adoSchema object.
+ *
+ * Call this method to clean up after an adoSchema object that is no longer in use.
+ * @deprecated adoSchema now cleans up automatically.
+ */
+ function Destroy() {
+ set_magic_quotes_runtime( $this->mgq );
+ unset( $this );
+ }
+}
+
+/**
+* Message logging function
+*
+* @access private
+*/
+function logMsg( $msg, $title = NULL, $force = FALSE ) {
+ if( XMLS_DEBUG or $force ) {
+ echo '<pre>';
+
+ if( isset( $title ) ) {
+ echo '<h3>' . htmlentities( $title ) . '</h3>';
+ }
+
+ if( is_object( $this ) ) {
+ echo '[' . get_class( $this ) . '] ';
+ }
+
+ print_r( $msg );
+
+ echo '</pre>';
+ }
+}
?>
\ No newline at end of file
-<?php\r
-// Copyright (c) 2004-2005 ars Cognita Inc., all rights reserved\r
-/* ******************************************************************************\r
- Released under both BSD license and Lesser GPL library license. \r
- Whenever there is any discrepancy between the two licenses, \r
- the BSD license will take precedence. \r
-*******************************************************************************/\r
-/**\r
- * xmlschema is a class that allows the user to quickly and easily\r
- * build a database on any ADOdb-supported platform using a simple\r
- * XML schema.\r
- *\r
- * Last Editor: $Author$\r
- * @author Richard Tango-Lowy & Dan Cech\r
- * @version $Revision$\r
- *\r
- * @package axmls\r
- * @tutorial getting_started.pkg\r
- */\r
- \r
-function _file_get_contents($file) \r
-{\r
- if (function_exists('file_get_contents')) return file_get_contents($file);\r
- \r
- $f = fopen($file,'r');\r
- if (!$f) return '';\r
- $t = '';\r
- \r
- while ($s = fread($f,100000)) $t .= $s;\r
- fclose($f);\r
- return $t;\r
-}\r
-\r
-\r
-/**\r
-* Debug on or off\r
-*/\r
-if( !defined( 'XMLS_DEBUG' ) ) {\r
- define( 'XMLS_DEBUG', FALSE );\r
-}\r
-\r
-/**\r
-* Default prefix key\r
-*/\r
-if( !defined( 'XMLS_PREFIX' ) ) {\r
- define( 'XMLS_PREFIX', '%%P' );\r
-}\r
-\r
-/**\r
-* Maximum length allowed for object prefix\r
-*/\r
-if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {\r
- define( 'XMLS_PREFIX_MAXLEN', 10 );\r
-}\r
-\r
-/**\r
-* Execute SQL inline as it is generated\r
-*/\r
-if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {\r
- define( 'XMLS_EXECUTE_INLINE', FALSE );\r
-}\r
-\r
-/**\r
-* Continue SQL Execution if an error occurs?\r
-*/\r
-if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {\r
- define( 'XMLS_CONTINUE_ON_ERROR', FALSE );\r
-}\r
-\r
-/**\r
-* Current Schema Version\r
-*/\r
-if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {\r
- define( 'XMLS_SCHEMA_VERSION', '0.3' );\r
-}\r
-\r
-/**\r
-* Default Schema Version. Used for Schemas without an explicit version set.\r
-*/\r
-if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {\r
- define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );\r
-}\r
-\r
-/**\r
-* How to handle data rows that already exist in a database during and upgrade.\r
-* Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing\r
-* rows) and IGNORE (ignores existing rows).\r
-*/\r
-if( !defined( 'XMLS_MODE_INSERT' ) ) {\r
- define( 'XMLS_MODE_INSERT', 0 );\r
-}\r
-if( !defined( 'XMLS_MODE_UPDATE' ) ) {\r
- define( 'XMLS_MODE_UPDATE', 1 );\r
-}\r
-if( !defined( 'XMLS_MODE_IGNORE' ) ) {\r
- define( 'XMLS_MODE_IGNORE', 2 );\r
-}\r
-if( !defined( 'XMLS_EXISTING_DATA' ) ) {\r
- define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT );\r
-}\r
-\r
-/**\r
-* Default Schema Version. Used for Schemas without an explicit version set.\r
-*/\r
-if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {\r
- define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );\r
-}\r
-\r
-/**\r
-* Include the main ADODB library\r
-*/\r
-if( !defined( '_ADODB_LAYER' ) ) {\r
- require( 'adodb.inc.php' );\r
- require( 'adodb-datadict.inc.php' );\r
-}\r
-\r
-/**\r
-* Abstract DB Object. This class provides basic methods for database objects, such\r
-* as tables and indexes.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbObject {\r
- \r
- /**\r
- * var object Parent\r
- */\r
- var $parent;\r
- \r
- /**\r
- * var string current element\r
- */\r
- var $currentElement;\r
- \r
- /**\r
- * NOP\r
- */\r
- function dbObject( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- \r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- \r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- \r
- }\r
- \r
- function create() {\r
- return array();\r
- }\r
- \r
- /**\r
- * Destroys the object\r
- */\r
- function destroy() {\r
- unset( $this );\r
- }\r
- \r
- /**\r
- * Checks whether the specified RDBMS is supported by the current\r
- * database object or its ranking ancestor.\r
- *\r
- * @param string $platform RDBMS platform name (from ADODB platform list).\r
- * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.\r
- */\r
- function supportedPlatform( $platform = NULL ) {\r
- return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;\r
- }\r
- \r
- /**\r
- * Returns the prefix set by the ranking ancestor of the database object.\r
- *\r
- * @param string $name Prefix string.\r
- * @return string Prefix.\r
- */\r
- function prefix( $name = '' ) {\r
- return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;\r
- }\r
- \r
- /**\r
- * Extracts a field ID from the specified field.\r
- *\r
- * @param string $field Field.\r
- * @return string Field ID.\r
- */\r
- function FieldID( $field ) {\r
- return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );\r
- }\r
-}\r
-\r
-/**\r
-* Creates a table object in ADOdb's datadict format\r
-*\r
-* This class stores information about a database table. As charactaristics\r
-* of the table are loaded from the external source, methods and properties\r
-* of this class are used to build up the table description in ADOdb's\r
-* datadict format.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbTable extends dbObject {\r
- \r
- /**\r
- * @var string Table name\r
- */\r
- var $name;\r
- \r
- /**\r
- * @var array Field specifier: Meta-information about each field\r
- */\r
- var $fields = array();\r
- \r
- /**\r
- * @var array List of table indexes.\r
- */\r
- var $indexes = array();\r
- \r
- /**\r
- * @var array Table options: Table-level options\r
- */\r
- var $opts = array();\r
- \r
- /**\r
- * @var string Field index: Keeps track of which field is currently being processed\r
- */\r
- var $current_field;\r
- \r
- /**\r
- * @var boolean Mark table for destruction\r
- * @access private\r
- */\r
- var $drop_table;\r
- \r
- /**\r
- * @var boolean Mark field for destruction (not yet implemented)\r
- * @access private\r
- */\r
- var $drop_field = array();\r
- \r
- /**\r
- * @var array Platform-specific options\r
- * @access private\r
- */\r
- var $currentPlatform = true;\r
- \r
- \r
- /**\r
- * Iniitializes a new table object.\r
- *\r
- * @param string $prefix DB Object prefix\r
- * @param array $attributes Array of table attributes.\r
- */\r
- function dbTable( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- $this->name = $this->prefix($attributes['NAME']);\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements. Elements currently \r
- * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT. \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'INDEX':\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- xml_set_object( $parser, $this->addIndex( $attributes ) );\r
- }\r
- break;\r
- case 'DATA':\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- xml_set_object( $parser, $this->addData( $attributes ) );\r
- }\r
- break;\r
- case 'DROP':\r
- $this->drop();\r
- break;\r
- case 'FIELD':\r
- // Add a field\r
- $fieldName = $attributes['NAME'];\r
- $fieldType = $attributes['TYPE'];\r
- $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;\r
- $fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;\r
- \r
- $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );\r
- break;\r
- case 'KEY':\r
- case 'NOTNULL':\r
- case 'AUTOINCREMENT':\r
- case 'DEFDATE':\r
- case 'DEFTIMESTAMP':\r
- case 'UNSIGNED':\r
- // Add a field option\r
- $this->addFieldOpt( $this->current_field, $this->currentElement );\r
- break;\r
- case 'DEFAULT':\r
- // Add a field option to the table object\r
- \r
- // Work around ADOdb datadict issue that misinterprets empty strings.\r
- if( $attributes['VALUE'] == '' ) {\r
- $attributes['VALUE'] = " '' ";\r
- }\r
- \r
- $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );\r
- break;\r
- case 'OPT':\r
- case 'CONSTRAINT':\r
- // Accept platform-specific options\r
- $this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) );\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Table/field constraint\r
- case 'CONSTRAINT':\r
- if( isset( $this->current_field ) ) {\r
- $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );\r
- } else {\r
- $this->addTableOpt( $cdata );\r
- }\r
- break;\r
- // Table/field option\r
- case 'OPT':\r
- if( isset( $this->current_field ) ) {\r
- $this->addFieldOpt( $this->current_field, $cdata );\r
- } else {\r
- $this->addTableOpt( $cdata );\r
- }\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'TABLE':\r
- $this->parent->addSQL( $this->create( $this->parent ) );\r
- xml_set_object( $parser, $this->parent );\r
- $this->destroy();\r
- break;\r
- case 'FIELD':\r
- unset($this->current_field);\r
- break;\r
- case 'OPT':\r
- case 'CONSTRAINT':\r
- $this->currentPlatform = true;\r
- break;\r
- default:\r
-\r
- }\r
- }\r
- \r
- /**\r
- * Adds an index to a table object\r
- *\r
- * @param array $attributes Index attributes\r
- * @return object dbIndex object\r
- */\r
- function addIndex( $attributes ) {\r
- $name = strtoupper( $attributes['NAME'] );\r
- $this->indexes[$name] = new dbIndex( $this, $attributes );\r
- return $this->indexes[$name];\r
- }\r
- \r
- /**\r
- * Adds data to a table object\r
- *\r
- * @param array $attributes Data attributes\r
- * @return object dbData object\r
- */\r
- function addData( $attributes ) {\r
- if( !isset( $this->data ) ) {\r
- $this->data = new dbData( $this, $attributes );\r
- }\r
- return $this->data;\r
- }\r
- \r
- /**\r
- * Adds a field to a table object\r
- *\r
- * $name is the name of the table to which the field should be added. \r
- * $type is an ADODB datadict field type. The following field types\r
- * are supported as of ADODB 3.40:\r
- * - C: varchar\r
- * - X: CLOB (character large object) or largest varchar size\r
- * if CLOB is not supported\r
- * - C2: Multibyte varchar\r
- * - X2: Multibyte CLOB\r
- * - B: BLOB (binary large object)\r
- * - D: Date (some databases do not support this, and we return a datetime type)\r
- * - T: Datetime or Timestamp\r
- * - L: Integer field suitable for storing booleans (0 or 1)\r
- * - I: Integer (mapped to I4)\r
- * - I1: 1-byte integer\r
- * - I2: 2-byte integer\r
- * - I4: 4-byte integer\r
- * - I8: 8-byte integer\r
- * - F: Floating point number\r
- * - N: Numeric or decimal number\r
- *\r
- * @param string $name Name of the table to which the field will be added.\r
- * @param string $type ADODB datadict field type.\r
- * @param string $size Field size\r
- * @param array $opts Field options array\r
- * @return array Field specifier array\r
- */\r
- function addField( $name, $type, $size = NULL, $opts = NULL ) {\r
- $field_id = $this->FieldID( $name );\r
- \r
- // Set the field index so we know where we are\r
- $this->current_field = $field_id;\r
- \r
- // Set the field name (required)\r
- $this->fields[$field_id]['NAME'] = $name;\r
- \r
- // Set the field type (required)\r
- $this->fields[$field_id]['TYPE'] = $type;\r
- \r
- // Set the field size (optional)\r
- if( isset( $size ) ) {\r
- $this->fields[$field_id]['SIZE'] = $size;\r
- }\r
- \r
- // Set the field options\r
- if( isset( $opts ) ) {\r
- $this->fields[$field_id]['OPTS'] = array($opts);\r
- } else {\r
- $this->fields[$field_id]['OPTS'] = array();\r
- }\r
- }\r
- \r
- /**\r
- * Adds a field option to the current field specifier\r
- *\r
- * This method adds a field option allowed by the ADOdb datadict \r
- * and appends it to the given field.\r
- *\r
- * @param string $field Field name\r
- * @param string $opt ADOdb field option\r
- * @param mixed $value Field option value\r
- * @return array Field specifier array\r
- */\r
- function addFieldOpt( $field, $opt, $value = NULL ) {\r
- if( $this->currentPlatform ) {\r
- if( !isset( $value ) ) {\r
- $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;\r
- // Add the option and value\r
- } else {\r
- $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );\r
- }\r
- }\r
- }\r
- \r
- /**\r
- * Adds an option to the table\r
- *\r
- * This method takes a comma-separated list of table-level options\r
- * and appends them to the table object.\r
- *\r
- * @param string $opt Table option\r
- * @return array Options\r
- */\r
- function addTableOpt( $opt ) {\r
- if( $this->currentPlatform ) {\r
- $this->opts[] = $opt;\r
- }\r
- return $this->opts;\r
- }\r
- \r
- /**\r
- * Generates the SQL that will create the table in the database\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Array containing table creation SQL\r
- */\r
- function create( &$xmls ) {\r
- $sql = array();\r
- \r
- // drop any existing indexes\r
- if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {\r
- foreach( $legacy_indexes as $index => $index_details ) {\r
- $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );\r
- }\r
- }\r
- \r
- // remove fields to be dropped from table object\r
- foreach( $this->drop_field as $field ) {\r
- unset( $this->fields[$field] );\r
- }\r
- \r
- // if table exists\r
- if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {\r
- // drop table\r
- if( $this->drop_table ) {\r
- $sql[] = $xmls->dict->DropTableSQL( $this->name );\r
- \r
- return $sql;\r
- }\r
- \r
- // drop any existing fields not in schema\r
- foreach( $legacy_fields as $field_id => $field ) {\r
- if( !isset( $this->fields[$field_id] ) ) {\r
- $sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name );\r
- }\r
- }\r
- // if table doesn't exist\r
- } else {\r
- if( $this->drop_table ) {\r
- return $sql;\r
- }\r
- \r
- $legacy_fields = array();\r
- }\r
- \r
- // Loop through the field specifier array, building the associative array for the field options\r
- $fldarray = array();\r
- \r
- foreach( $this->fields as $field_id => $finfo ) {\r
- // Set an empty size if it isn't supplied\r
- if( !isset( $finfo['SIZE'] ) ) {\r
- $finfo['SIZE'] = '';\r
- }\r
- \r
- // Initialize the field array with the type and size\r
- $fldarray[$field_id] = array(\r
- 'NAME' => $finfo['NAME'],\r
- 'TYPE' => $finfo['TYPE'],\r
- 'SIZE' => $finfo['SIZE']\r
- );\r
- \r
- // Loop through the options array and add the field options. \r
- if( isset( $finfo['OPTS'] ) ) {\r
- foreach( $finfo['OPTS'] as $opt ) {\r
- // Option has an argument.\r
- if( is_array( $opt ) ) {\r
- $key = key( $opt );\r
- $value = $opt[key( $opt )];\r
- @$fldarray[$field_id][$key] .= $value;\r
- // Option doesn't have arguments\r
- } else {\r
- $fldarray[$field_id][$opt] = $opt;\r
- }\r
- }\r
- }\r
- }\r
- \r
- if( empty( $legacy_fields ) ) {\r
- // Create the new table\r
- $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );\r
- logMsg( end( $sql ), 'Generated CreateTableSQL' );\r
- } else {\r
- // Upgrade an existing table\r
- logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );\r
- switch( $xmls->upgrade ) {\r
- // Use ChangeTableSQL\r
- case 'ALTER':\r
- logMsg( 'Generated ChangeTableSQL (ALTERing table)' );\r
- $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );\r
- break;\r
- case 'REPLACE':\r
- logMsg( 'Doing upgrade REPLACE (testing)' );\r
- $sql[] = $xmls->dict->DropTableSQL( $this->name );\r
- $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );\r
- break;\r
- // ignore table\r
- default:\r
- return array();\r
- }\r
- }\r
- \r
- foreach( $this->indexes as $index ) {\r
- $sql[] = $index->create( $xmls );\r
- }\r
- \r
- if( isset( $this->data ) ) {\r
- $sql[] = $this->data->create( $xmls );\r
- }\r
- \r
- return $sql;\r
- }\r
- \r
- /**\r
- * Marks a field or table for destruction\r
- */\r
- function drop() {\r
- if( isset( $this->current_field ) ) {\r
- // Drop the current field\r
- logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );\r
- // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );\r
- $this->drop_field[$this->current_field] = $this->current_field;\r
- } else {\r
- // Drop the current table\r
- logMsg( "Dropping table '{$this->name}'" );\r
- // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );\r
- $this->drop_table = TRUE;\r
- }\r
- }\r
-}\r
-\r
-/**\r
-* Creates an index object in ADOdb's datadict format\r
-*\r
-* This class stores information about a database index. As charactaristics\r
-* of the index are loaded from the external source, methods and properties\r
-* of this class are used to build up the index description in ADOdb's\r
-* datadict format.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbIndex extends dbObject {\r
- \r
- /**\r
- * @var string Index name\r
- */\r
- var $name;\r
- \r
- /**\r
- * @var array Index options: Index-level options\r
- */\r
- var $opts = array();\r
- \r
- /**\r
- * @var array Indexed fields: Table columns included in this index\r
- */\r
- var $columns = array();\r
- \r
- /**\r
- * @var boolean Mark index for destruction\r
- * @access private\r
- */\r
- var $drop = FALSE;\r
- \r
- /**\r
- * Initializes the new dbIndex object.\r
- *\r
- * @param object $parent Parent object\r
- * @param array $attributes Attributes\r
- *\r
- * @internal\r
- */\r
- function dbIndex( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- \r
- $this->name = $this->prefix ($attributes['NAME']);\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * Processes XML opening tags. \r
- * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'DROP':\r
- $this->drop();\r
- break;\r
- case 'CLUSTERED':\r
- case 'BITMAP':\r
- case 'UNIQUE':\r
- case 'FULLTEXT':\r
- case 'HASH':\r
- // Add index Option\r
- $this->addIndexOpt( $this->currentElement );\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * Processes XML cdata.\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Index field name\r
- case 'COL':\r
- $this->addField( $cdata );\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'INDEX':\r
- xml_set_object( $parser, $this->parent );\r
- break;\r
- }\r
- }\r
- \r
- /**\r
- * Adds a field to the index\r
- *\r
- * @param string $name Field name\r
- * @return string Field list\r
- */\r
- function addField( $name ) {\r
- $this->columns[$this->FieldID( $name )] = $name;\r
- \r
- // Return the field list\r
- return $this->columns;\r
- }\r
- \r
- /**\r
- * Adds options to the index\r
- *\r
- * @param string $opt Comma-separated list of index options.\r
- * @return string Option list\r
- */\r
- function addIndexOpt( $opt ) {\r
- $this->opts[] = $opt;\r
- \r
- // Return the options list\r
- return $this->opts;\r
- }\r
- \r
- /**\r
- * Generates the SQL that will create the index in the database\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Array containing index creation SQL\r
- */\r
- function create( &$xmls ) {\r
- if( $this->drop ) {\r
- return NULL;\r
- }\r
- \r
- // eliminate any columns that aren't in the table\r
- foreach( $this->columns as $id => $col ) {\r
- if( !isset( $this->parent->fields[$id] ) ) {\r
- unset( $this->columns[$id] );\r
- }\r
- }\r
- \r
- return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );\r
- }\r
- \r
- /**\r
- * Marks an index for destruction\r
- */\r
- function drop() {\r
- $this->drop = TRUE;\r
- }\r
-}\r
-\r
-/**\r
-* Creates a data object in ADOdb's datadict format\r
-*\r
-* This class stores information about table data, and is called\r
-* when we need to load field data into a table.\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbData extends dbObject {\r
- \r
- var $data = array();\r
- \r
- var $row;\r
- \r
- /**\r
- * Initializes the new dbData object.\r
- *\r
- * @param object $parent Parent object\r
- * @param array $attributes Attributes\r
- *\r
- * @internal\r
- */\r
- function dbData( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * Processes XML opening tags. \r
- * Elements currently processed are: ROW and F (field). \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'ROW':\r
- $this->row = count( $this->data );\r
- $this->data[$this->row] = array();\r
- break;\r
- case 'F':\r
- $this->addField($attributes);\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * Processes XML cdata.\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Index field name\r
- case 'F':\r
- $this->addData( $cdata );\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'DATA':\r
- xml_set_object( $parser, $this->parent );\r
- break;\r
- }\r
- }\r
- \r
- /**\r
- * Adds a field to the insert\r
- *\r
- * @param string $name Field name\r
- * @return string Field list\r
- */\r
- function addField( $attributes ) {\r
- // check we're in a valid row\r
- if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) {\r
- return;\r
- }\r
- \r
- // Set the field index so we know where we are\r
- if( isset( $attributes['NAME'] ) ) {\r
- $this->current_field = $this->FieldID( $attributes['NAME'] );\r
- } else {\r
- $this->current_field = count( $this->data[$this->row] );\r
- }\r
- \r
- // initialise data\r
- if( !isset( $this->data[$this->row][$this->current_field] ) ) {\r
- $this->data[$this->row][$this->current_field] = '';\r
- }\r
- }\r
- \r
- /**\r
- * Adds options to the index\r
- *\r
- * @param string $opt Comma-separated list of index options.\r
- * @return string Option list\r
- */\r
- function addData( $cdata ) {\r
- // check we're in a valid field\r
- if ( isset( $this->data[$this->row][$this->current_field] ) ) {\r
- // add data to field\r
- $this->data[$this->row][$this->current_field] .= $cdata;\r
- }\r
- }\r
- \r
- /**\r
- * Generates the SQL that will add/update the data in the database\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Array containing index creation SQL\r
- */\r
- function create( &$xmls ) {\r
- $table = $xmls->dict->TableName($this->parent->name);\r
- $table_field_count = count($this->parent->fields);\r
- $tables = $xmls->db->MetaTables(); \r
- $sql = array();\r
- \r
- $ukeys = $xmls->db->MetaPrimaryKeys( $table );\r
- if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) {\r
- foreach( $this->parent->indexes as $indexObj ) {\r
- if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name;\r
- }\r
- }\r
- \r
- // eliminate any columns that aren't in the table\r
- foreach( $this->data as $row ) {\r
- $table_fields = $this->parent->fields;\r
- $fields = array();\r
- $rawfields = array(); // Need to keep some of the unprocessed data on hand.\r
- \r
- foreach( $row as $field_id => $field_data ) {\r
- if( !array_key_exists( $field_id, $table_fields ) ) {\r
- if( is_numeric( $field_id ) ) {\r
- $field_id = reset( array_keys( $table_fields ) );\r
- } else {\r
- continue;\r
- }\r
- }\r
- \r
- $name = $table_fields[$field_id]['NAME'];\r
- \r
- switch( $table_fields[$field_id]['TYPE'] ) {\r
- case 'I':\r
- case 'I1':\r
- case 'I2':\r
- case 'I4':\r
- case 'I8':\r
- $fields[$name] = intval($field_data);\r
- break;\r
- case 'C':\r
- case 'C2':\r
- case 'X':\r
- case 'X2':\r
- default:\r
- $fields[$name] = $xmls->db->qstr( $field_data );\r
- $rawfields[$name] = $field_data;\r
- }\r
- \r
- unset($table_fields[$field_id]);\r
- \r
- }\r
- \r
- // check that at least 1 column is specified\r
- if( empty( $fields ) ) {\r
- continue;\r
- }\r
- \r
- // check that no required columns are missing\r
- if( count( $fields ) < $table_field_count ) {\r
- foreach( $table_fields as $field ) {\r
- if( isset( $field['OPTS'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {\r
- continue(2);\r
- }\r
- }\r
- }\r
- \r
- // The rest of this method deals with updating existing data records.\r
- \r
- if( !in_array( $table, $tables ) or ( $mode = $xmls->existingData() ) == XMLS_MODE_INSERT ) {\r
- // Table doesn't yet exist, so it's safe to insert.\r
- logMsg( "$table doesn't exist, inserting or mode is INSERT" );\r
- $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';\r
- continue;\r
- }\r
- \r
- // Prepare to test for potential violations. Get primary keys and unique indexes\r
- $mfields = array_merge( $fields, $rawfields );\r
- $keyFields = array_intersect( $ukeys, array_keys( $mfields ) );\r
- \r
- if( empty( $ukeys ) or count( $keyFields ) == 0 ) {\r
- // No unique keys in schema, so safe to insert\r
- logMsg( "Either schema or data has no unique keys, so safe to insert" );\r
- $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';\r
- continue;\r
- }\r
- \r
- // Select record containing matching unique keys.\r
- $where = '';\r
- foreach( $ukeys as $key ) {\r
- if( isset( $mfields[$key] ) and $mfields[$key] ) {\r
- if( $where ) $where .= ' AND ';\r
- $where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] );\r
- }\r
- }\r
- $records = $xmls->db->Execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where );\r
- switch( $records->RecordCount() ) {\r
- case 0:\r
- // No matching record, so safe to insert.\r
- logMsg( "No matching records. Inserting new row with unique data" );\r
- $sql[] = $xmls->db->GetInsertSQL( $records, $mfields );\r
- break;\r
- case 1:\r
- // Exactly one matching record, so we can update if the mode permits.\r
- logMsg( "One matching record..." );\r
- if( $mode == XMLS_MODE_UPDATE ) {\r
- logMsg( "...Updating existing row from unique data" );\r
- $sql[] = $xmls->db->GetUpdateSQL( $records, $mfields );\r
- }\r
- break;\r
- default:\r
- // More than one matching record; the result is ambiguous, so we must ignore the row.\r
- logMsg( "More than one matching record. Ignoring row." );\r
- }\r
- }\r
- return $sql;\r
- }\r
-}\r
-\r
-/**\r
-* Creates the SQL to execute a list of provided SQL queries\r
-*\r
-* @package axmls\r
-* @access private\r
-*/\r
-class dbQuerySet extends dbObject {\r
- \r
- /**\r
- * @var array List of SQL queries\r
- */\r
- var $queries = array();\r
- \r
- /**\r
- * @var string String used to build of a query line by line\r
- */\r
- var $query;\r
- \r
- /**\r
- * @var string Query prefix key\r
- */\r
- var $prefixKey = '';\r
- \r
- /**\r
- * @var boolean Auto prefix enable (TRUE)\r
- */\r
- var $prefixMethod = 'AUTO';\r
- \r
- /**\r
- * Initializes the query set.\r
- *\r
- * @param object $parent Parent object\r
- * @param array $attributes Attributes\r
- */\r
- function dbQuerySet( &$parent, $attributes = NULL ) {\r
- $this->parent = $parent;\r
- \r
- // Overrides the manual prefix key\r
- if( isset( $attributes['KEY'] ) ) {\r
- $this->prefixKey = $attributes['KEY'];\r
- }\r
- \r
- $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';\r
- \r
- // Enables or disables automatic prefix prepending\r
- switch( $prefixMethod ) {\r
- case 'AUTO':\r
- $this->prefixMethod = 'AUTO';\r
- break;\r
- case 'MANUAL':\r
- $this->prefixMethod = 'MANUAL';\r
- break;\r
- case 'NONE':\r
- $this->prefixMethod = 'NONE';\r
- break;\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements. Elements currently \r
- * processed are: QUERY. \r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- $this->currentElement = strtoupper( $tag );\r
- \r
- switch( $this->currentElement ) {\r
- case 'QUERY':\r
- // Create a new query in a SQL queryset.\r
- // Ignore this query set if a platform is specified and it's different than the \r
- // current connection platform.\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- $this->newQuery();\r
- } else {\r
- $this->discardQuery();\r
- }\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- switch( $this->currentElement ) {\r
- // Line of queryset SQL data\r
- case 'QUERY':\r
- $this->buildQuery( $cdata );\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- $this->currentElement = '';\r
- \r
- switch( strtoupper( $tag ) ) {\r
- case 'QUERY':\r
- // Add the finished query to the open query set.\r
- $this->addQuery();\r
- break;\r
- case 'SQL':\r
- $this->parent->addSQL( $this->create( $this->parent ) );\r
- xml_set_object( $parser, $this->parent );\r
- $this->destroy();\r
- break;\r
- default:\r
- \r
- }\r
- }\r
- \r
- /**\r
- * Re-initializes the query.\r
- *\r
- * @return boolean TRUE\r
- */\r
- function newQuery() {\r
- $this->query = '';\r
- \r
- return TRUE;\r
- }\r
- \r
- /**\r
- * Discards the existing query.\r
- *\r
- * @return boolean TRUE\r
- */\r
- function discardQuery() {\r
- unset( $this->query );\r
- \r
- return TRUE;\r
- }\r
- \r
- /** \r
- * Appends a line to a query that is being built line by line\r
- *\r
- * @param string $data Line of SQL data or NULL to initialize a new query\r
- * @return string SQL query string.\r
- */\r
- function buildQuery( $sql = NULL ) {\r
- if( !isset( $this->query ) OR empty( $sql ) ) {\r
- return FALSE;\r
- }\r
- \r
- $this->query .= $sql;\r
- \r
- return $this->query;\r
- }\r
- \r
- /**\r
- * Adds a completed query to the query list\r
- *\r
- * @return string SQL of added query\r
- */\r
- function addQuery() {\r
- if( !isset( $this->query ) ) {\r
- return FALSE;\r
- }\r
- \r
- $this->queries[] = $return = trim($this->query);\r
- \r
- unset( $this->query );\r
- \r
- return $return;\r
- }\r
- \r
- /**\r
- * Creates and returns the current query set\r
- *\r
- * @param object $xmls adoSchema object\r
- * @return array Query set\r
- */\r
- function create( &$xmls ) {\r
- foreach( $this->queries as $id => $query ) {\r
- switch( $this->prefixMethod ) {\r
- case 'AUTO':\r
- // Enable auto prefix replacement\r
- \r
- // Process object prefix.\r
- // Evaluate SQL statements to prepend prefix to objects\r
- $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
- $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
- $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );\r
- \r
- // SELECT statements aren't working yet\r
- #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );\r
- \r
- case 'MANUAL':\r
- // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.\r
- // If prefixKey is not set, we use the default constant XMLS_PREFIX\r
- if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {\r
- // Enable prefix override\r
- $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );\r
- } else {\r
- // Use default replacement\r
- $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );\r
- }\r
- }\r
- \r
- $this->queries[$id] = trim( $query );\r
- }\r
- \r
- // Return the query set array\r
- return $this->queries;\r
- }\r
- \r
- /**\r
- * Rebuilds the query with the prefix attached to any objects\r
- *\r
- * @param string $regex Regex used to add prefix\r
- * @param string $query SQL query string\r
- * @param string $prefix Prefix to be appended to tables, indices, etc.\r
- * @return string Prefixed SQL query string.\r
- */\r
- function prefixQuery( $regex, $query, $prefix = NULL ) {\r
- if( !isset( $prefix ) ) {\r
- return $query;\r
- }\r
- \r
- if( preg_match( $regex, $query, $match ) ) {\r
- $preamble = $match[1];\r
- $postamble = $match[5];\r
- $objectList = explode( ',', $match[3] );\r
- // $prefix = $prefix . '_';\r
- \r
- $prefixedList = '';\r
- \r
- foreach( $objectList as $object ) {\r
- if( $prefixedList !== '' ) {\r
- $prefixedList .= ', ';\r
- }\r
- \r
- $prefixedList .= $prefix . trim( $object );\r
- }\r
- \r
- $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;\r
- }\r
- \r
- return $query;\r
- }\r
-}\r
-\r
-/**\r
-* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements\r
-* \r
-* This class is used to load and parse the XML file, to create an array of SQL statements\r
-* that can be used to build a database, and to build the database using the SQL array.\r
-*\r
-* @tutorial getting_started.pkg\r
-*\r
-* @author Richard Tango-Lowy & Dan Cech\r
-* @version $Revision$\r
-*\r
-* @package axmls\r
-*/\r
-class adoSchema {\r
- \r
- /**\r
- * @var array Array containing SQL queries to generate all objects\r
- * @access private\r
- */\r
- var $sqlArray;\r
- \r
- /**\r
- * @var object ADOdb connection object\r
- * @access private\r
- */\r
- var $db;\r
- \r
- /**\r
- * @var object ADOdb Data Dictionary\r
- * @access private\r
- */\r
- var $dict;\r
- \r
- /**\r
- * @var string Current XML element\r
- * @access private\r
- */\r
- var $currentElement = '';\r
- \r
- /**\r
- * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database\r
- * @access private\r
- */\r
- var $upgrade = '';\r
- \r
- /**\r
- * @var string Optional object prefix\r
- * @access private\r
- */\r
- var $objectPrefix = '';\r
- \r
- /**\r
- * @var long Original Magic Quotes Runtime value\r
- * @access private\r
- */\r
- var $mgq;\r
- \r
- /**\r
- * @var long System debug\r
- * @access private\r
- */\r
- var $debug;\r
- \r
- /**\r
- * @var string Regular expression to find schema version\r
- * @access private\r
- */\r
- var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';\r
- \r
- /**\r
- * @var string Current schema version\r
- * @access private\r
- */\r
- var $schemaVersion;\r
- \r
- /**\r
- * @var int Success of last Schema execution\r
- */\r
- var $success;\r
- \r
- /**\r
- * @var bool Execute SQL inline as it is generated\r
- */\r
- var $executeInline;\r
- \r
- /**\r
- * @var bool Continue SQL execution if errors occur\r
- */\r
- var $continueOnError;\r
- \r
- /**\r
- * @var int How to handle existing data rows (insert, update, or ignore)\r
- */\r
- var $existingData;\r
- \r
- /**\r
- * Creates an adoSchema object\r
- *\r
- * Creating an adoSchema object is the first step in processing an XML schema.\r
- * The only parameter is an ADOdb database connection object, which must already\r
- * have been created.\r
- *\r
- * @param object $db ADOdb database connection object.\r
- */\r
- function adoSchema( &$db ) {\r
- // Initialize the environment\r
- $this->mgq = get_magic_quotes_runtime();\r
- set_magic_quotes_runtime(0);\r
- \r
- $this->db = $db;\r
- $this->debug = $this->db->debug;\r
- $this->dict = NewDataDictionary( $this->db );\r
- $this->sqlArray = array();\r
- $this->schemaVersion = XMLS_SCHEMA_VERSION;\r
- $this->executeInline( XMLS_EXECUTE_INLINE );\r
- $this->continueOnError( XMLS_CONTINUE_ON_ERROR );\r
- $this->existingData( XMLS_EXISTING_DATA );\r
- $this->setUpgradeMethod();\r
- }\r
- \r
- /**\r
- * Sets the method to be used for upgrading an existing database\r
- *\r
- * Use this method to specify how existing database objects should be upgraded.\r
- * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to\r
- * alter each database object directly, REPLACE attempts to rebuild each object\r
- * from scratch, BEST attempts to determine the best upgrade method for each\r
- * object, and NONE disables upgrading.\r
- *\r
- * This method is not yet used by AXMLS, but exists for backward compatibility.\r
- * The ALTER method is automatically assumed when the adoSchema object is\r
- * instantiated; other upgrade methods are not currently supported.\r
- *\r
- * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)\r
- * @returns string Upgrade method used\r
- */\r
- function SetUpgradeMethod( $method = '' ) {\r
- if( !is_string( $method ) ) {\r
- return FALSE;\r
- }\r
- \r
- $method = strtoupper( $method );\r
- \r
- // Handle the upgrade methods\r
- switch( $method ) {\r
- case 'ALTER':\r
- $this->upgrade = $method;\r
- break;\r
- case 'REPLACE':\r
- $this->upgrade = $method;\r
- break;\r
- case 'BEST':\r
- $this->upgrade = 'ALTER';\r
- break;\r
- case 'NONE':\r
- $this->upgrade = 'NONE';\r
- break;\r
- default:\r
- // Use default if no legitimate method is passed.\r
- $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;\r
- }\r
- \r
- return $this->upgrade;\r
- }\r
- \r
- /**\r
- * Specifies how to handle existing data row when there is a unique key conflict.\r
- *\r
- * The existingData setting specifies how the parser should handle existing rows\r
- * when a unique key violation occurs during the insert. This can happen when inserting\r
- * data into an existing table with one or more primary keys or unique indexes.\r
- * The existingData method takes one of three options: XMLS_MODE_INSERT attempts\r
- * to always insert the data as a new row. In the event of a unique key violation,\r
- * the database will generate an error. XMLS_MODE_UPDATE attempts to update the \r
- * any existing rows with the new data based upon primary or unique key fields in\r
- * the schema. If the data row in the schema specifies no unique fields, the row\r
- * data will be inserted as a new row. XMLS_MODE_IGNORE specifies that any data rows\r
- * that would result in a unique key violation be ignored; no inserts or updates will\r
- * take place. For backward compatibility, the default setting is XMLS_MODE_INSERT,\r
- * but XMLS_MODE_UPDATE will generally be the most appropriate setting.\r
- *\r
- * @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE\r
- * @return int current mode\r
- */\r
- function ExistingData( $mode = NULL ) {\r
- if( is_int( $mode ) ) {\r
- switch( $mode ) {\r
- case XMLS_MODE_UPDATE:\r
- $mode = XMLS_MODE_UPDATE;\r
- break;\r
- case XMLS_MODE_IGNORE:\r
- $mode = XMLS_MODE_IGNORE;\r
- break;\r
- case XMLS_MODE_INSERT:\r
- $mode = XMLS_MODE_INSERT;\r
- break;\r
- default:\r
- $mode = XMLS_EXISITNG_DATA;\r
- break;\r
- }\r
- $this->existingData = $mode;\r
- }\r
- \r
- return $this->existingData;\r
- }\r
- \r
- /**\r
- * Enables/disables inline SQL execution.\r
- *\r
- * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),\r
- * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode\r
- * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()\r
- * to apply the schema to the database.\r
- *\r
- * @param bool $mode execute\r
- * @return bool current execution mode\r
- *\r
- * @see ParseSchema(), ExecuteSchema()\r
- */\r
- function ExecuteInline( $mode = NULL ) {\r
- if( is_bool( $mode ) ) {\r
- $this->executeInline = $mode;\r
- }\r
- \r
- return $this->executeInline;\r
- }\r
- \r
- /**\r
- * Enables/disables SQL continue on error.\r
- *\r
- * Call this method to enable or disable continuation of SQL execution if an error occurs.\r
- * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.\r
- * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing\r
- * of the schema will continue.\r
- *\r
- * @param bool $mode execute\r
- * @return bool current continueOnError mode\r
- *\r
- * @see addSQL(), ExecuteSchema()\r
- */\r
- function ContinueOnError( $mode = NULL ) {\r
- if( is_bool( $mode ) ) {\r
- $this->continueOnError = $mode;\r
- }\r
- \r
- return $this->continueOnError;\r
- }\r
- \r
- /**\r
- * Loads an XML schema from a file and converts it to SQL.\r
- *\r
- * Call this method to load the specified schema (see the DTD for the proper format) from\r
- * the filesystem and generate the SQL necessary to create the database\r
- * described. This method automatically converts the schema to the latest\r
- * axmls schema version.\r
- * @see ParseSchemaString()\r
- *\r
- * @param string $file Name of XML schema file.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute\r
- */\r
- function ParseSchema( $filename, $returnSchema = FALSE ) {\r
- return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );\r
- }\r
- \r
- /**\r
- * Loads an XML schema from a file and converts it to SQL.\r
- *\r
- * Call this method to load the specified schema directly from a file (see\r
- * the DTD for the proper format) and generate the SQL necessary to create\r
- * the database described by the schema. Use this method when you are dealing\r
- * with large schema files. Otherwise, ParseSchema() is faster.\r
- * This method does not automatically convert the schema to the latest axmls\r
- * schema version. You must convert the schema manually using either the\r
- * ConvertSchemaFile() or ConvertSchemaString() method.\r
- * @see ParseSchema()\r
- * @see ConvertSchemaFile()\r
- * @see ConvertSchemaString()\r
- *\r
- * @param string $file Name of XML schema file.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute.\r
- *\r
- * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()\r
- * @see ParseSchema(), ParseSchemaString()\r
- */\r
- function ParseSchemaFile( $filename, $returnSchema = FALSE ) {\r
- // Open the file\r
- if( !($fp = fopen( $filename, 'r' )) ) {\r
- logMsg( 'Unable to open file' );\r
- return FALSE;\r
- }\r
- \r
- // do version detection here\r
- if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {\r
- logMsg( 'Invalid Schema Version' );\r
- return FALSE;\r
- }\r
- \r
- if( $returnSchema ) {\r
- $xmlstring = '';\r
- while( $data = fread( $fp, 4096 ) ) {\r
- $xmlstring .= $data . "\n";\r
- }\r
- return $xmlstring;\r
- }\r
- \r
- $this->success = 2;\r
- \r
- $xmlParser = $this->create_parser();\r
- \r
- // Process the file\r
- while( $data = fread( $fp, 4096 ) ) {\r
- if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {\r
- die( sprintf(\r
- "XML error: %s at line %d",\r
- xml_error_string( xml_get_error_code( $xmlParser) ),\r
- xml_get_current_line_number( $xmlParser)\r
- ) );\r
- }\r
- }\r
- \r
- xml_parser_free( $xmlParser );\r
- \r
- return $this->sqlArray;\r
- }\r
- \r
- /**\r
- * Converts an XML schema string to SQL.\r
- *\r
- * Call this method to parse a string containing an XML schema (see the DTD for the proper format)\r
- * and generate the SQL necessary to create the database described by the schema. \r
- * @see ParseSchema()\r
- *\r
- * @param string $xmlstring XML schema string.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute.\r
- */\r
- function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {\r
- if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {\r
- logMsg( 'Empty or Invalid Schema' );\r
- return FALSE;\r
- }\r
- \r
- // do version detection here\r
- if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {\r
- logMsg( 'Invalid Schema Version' );\r
- return FALSE;\r
- }\r
- \r
- if( $returnSchema ) {\r
- return $xmlstring;\r
- }\r
- \r
- $this->success = 2;\r
- \r
- $xmlParser = $this->create_parser();\r
- \r
- if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {\r
- die( sprintf(\r
- "XML error: %s at line %d",\r
- xml_error_string( xml_get_error_code( $xmlParser) ),\r
- xml_get_current_line_number( $xmlParser)\r
- ) );\r
- }\r
- \r
- xml_parser_free( $xmlParser );\r
- \r
- return $this->sqlArray;\r
- }\r
- \r
- /**\r
- * Loads an XML schema from a file and converts it to uninstallation SQL.\r
- *\r
- * Call this method to load the specified schema (see the DTD for the proper format) from\r
- * the filesystem and generate the SQL necessary to remove the database described.\r
- * @see RemoveSchemaString()\r
- *\r
- * @param string $file Name of XML schema file.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute\r
- */\r
- function RemoveSchema( $filename, $returnSchema = FALSE ) {\r
- return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );\r
- }\r
- \r
- /**\r
- * Converts an XML schema string to uninstallation SQL.\r
- *\r
- * Call this method to parse a string containing an XML schema (see the DTD for the proper format)\r
- * and generate the SQL necessary to uninstall the database described by the schema. \r
- * @see RemoveSchema()\r
- *\r
- * @param string $schema XML schema string.\r
- * @param bool $returnSchema Return schema rather than parsing.\r
- * @return array Array of SQL queries, ready to execute.\r
- */\r
- function RemoveSchemaString( $schema, $returnSchema = FALSE ) {\r
- \r
- // grab current version\r
- if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {\r
- return FALSE;\r
- }\r
- \r
- return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );\r
- }\r
- \r
- /**\r
- * Applies the current XML schema to the database (post execution).\r
- *\r
- * Call this method to apply the current schema (generally created by calling \r
- * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes, \r
- * and executing other SQL specified in the schema) after parsing.\r
- * @see ParseSchema(), ParseSchemaString(), ExecuteInline()\r
- *\r
- * @param array $sqlArray Array of SQL statements that will be applied rather than\r
- * the current schema.\r
- * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.\r
- * @returns integer 0 if failure, 1 if errors, 2 if successful.\r
- */\r
- function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) {\r
- if( !is_bool( $continueOnErr ) ) {\r
- $continueOnErr = $this->ContinueOnError();\r
- }\r
- \r
- if( !isset( $sqlArray ) ) {\r
- $sqlArray = $this->sqlArray;\r
- }\r
- \r
- if( !is_array( $sqlArray ) ) {\r
- $this->success = 0;\r
- } else {\r
- $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );\r
- }\r
- \r
- return $this->success;\r
- }\r
- \r
- /**\r
- * Returns the current SQL array. \r
- *\r
- * Call this method to fetch the array of SQL queries resulting from \r
- * ParseSchema() or ParseSchemaString(). \r
- *\r
- * @param string $format Format: HTML, TEXT, or NONE (PHP array)\r
- * @return array Array of SQL statements or FALSE if an error occurs\r
- */\r
- function PrintSQL( $format = 'NONE' ) {\r
- $sqlArray = null;\r
- return $this->getSQL( $format, $sqlArray );\r
- }\r
- \r
- /**\r
- * Saves the current SQL array to the local filesystem as a list of SQL queries.\r
- *\r
- * Call this method to save the array of SQL queries (generally resulting from a\r
- * parsed XML schema) to the filesystem.\r
- *\r
- * @param string $filename Path and name where the file should be saved.\r
- * @return boolean TRUE if save is successful, else FALSE. \r
- */\r
- function SaveSQL( $filename = './schema.sql' ) {\r
- \r
- if( !isset( $sqlArray ) ) {\r
- $sqlArray = $this->sqlArray;\r
- }\r
- if( !isset( $sqlArray ) ) {\r
- return FALSE;\r
- }\r
- \r
- $fp = fopen( $filename, "w" );\r
- \r
- foreach( $sqlArray as $key => $query ) {\r
- fwrite( $fp, $query . ";\n" );\r
- }\r
- fclose( $fp );\r
- }\r
- \r
- /**\r
- * Create an xml parser\r
- *\r
- * @return object PHP XML parser object\r
- *\r
- * @access private\r
- */\r
- function create_parser() {\r
- // Create the parser\r
- $xmlParser = xml_parser_create();\r
- xml_set_object( $xmlParser, $this );\r
- \r
- // Initialize the XML callback functions\r
- xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );\r
- xml_set_character_data_handler( $xmlParser, '_tag_cdata' );\r
- \r
- return $xmlParser;\r
- }\r
- \r
- /**\r
- * XML Callback to process start elements\r
- *\r
- * @access private\r
- */\r
- function _tag_open( &$parser, $tag, $attributes ) {\r
- switch( strtoupper( $tag ) ) {\r
- case 'TABLE':\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- $this->obj = new dbTable( $this, $attributes );\r
- xml_set_object( $parser, $this->obj );\r
- }\r
- break;\r
- case 'SQL':\r
- if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {\r
- $this->obj = new dbQuerySet( $this, $attributes );\r
- xml_set_object( $parser, $this->obj );\r
- }\r
- break;\r
- default:\r
- // print_r( array( $tag, $attributes ) );\r
- }\r
- \r
- }\r
- \r
- /**\r
- * XML Callback to process CDATA elements\r
- *\r
- * @access private\r
- */\r
- function _tag_cdata( &$parser, $cdata ) {\r
- }\r
- \r
- /**\r
- * XML Callback to process end elements\r
- *\r
- * @access private\r
- * @internal\r
- */\r
- function _tag_close( &$parser, $tag ) {\r
- \r
- }\r
- \r
- /**\r
- * Converts an XML schema string to the specified DTD version.\r
- *\r
- * Call this method to convert a string containing an XML schema to a different AXMLS\r
- * DTD version. For instance, to convert a schema created for an pre-1.0 version for \r
- * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version \r
- * parameter is specified, the schema will be converted to the current DTD version. \r
- * If the newFile parameter is provided, the converted schema will be written to the specified\r
- * file.\r
- * @see ConvertSchemaFile()\r
- *\r
- * @param string $schema String containing XML schema that will be converted.\r
- * @param string $newVersion DTD version to convert to.\r
- * @param string $newFile File name of (converted) output file.\r
- * @return string Converted XML schema or FALSE if an error occurs.\r
- */\r
- function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {\r
- \r
- // grab current version\r
- if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {\r
- return FALSE;\r
- }\r
- \r
- if( !isset ($newVersion) ) {\r
- $newVersion = $this->schemaVersion;\r
- }\r
- \r
- if( $version == $newVersion ) {\r
- $result = $schema;\r
- } else {\r
- $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);\r
- }\r
- \r
- if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {\r
- fwrite( $fp, $result );\r
- fclose( $fp );\r
- }\r
- \r
- return $result;\r
- }\r
-\r
- /*\r
- // compat for pre-4.3 - jlim\r
- function _file_get_contents($path)\r
- {\r
- if (function_exists('file_get_contents')) return file_get_contents($path);\r
- return join('',file($path));\r
- }*/\r
- \r
- /**\r
- * Converts an XML schema file to the specified DTD version.\r
- *\r
- * Call this method to convert the specified XML schema file to a different AXMLS\r
- * DTD version. For instance, to convert a schema created for an pre-1.0 version for \r
- * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version \r
- * parameter is specified, the schema will be converted to the current DTD version. \r
- * If the newFile parameter is provided, the converted schema will be written to the specified\r
- * file.\r
- * @see ConvertSchemaString()\r
- *\r
- * @param string $filename Name of XML schema file that will be converted.\r
- * @param string $newVersion DTD version to convert to.\r
- * @param string $newFile File name of (converted) output file.\r
- * @return string Converted XML schema or FALSE if an error occurs.\r
- */\r
- function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {\r
- \r
- // grab current version\r
- if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {\r
- return FALSE;\r
- }\r
- \r
- if( !isset ($newVersion) ) {\r
- $newVersion = $this->schemaVersion;\r
- }\r
- \r
- if( $version == $newVersion ) {\r
- $result = _file_get_contents( $filename );\r
- \r
- // remove unicode BOM if present\r
- if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {\r
- $result = substr( $result, 3 );\r
- }\r
- } else {\r
- $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );\r
- }\r
- \r
- if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {\r
- fwrite( $fp, $result );\r
- fclose( $fp );\r
- }\r
- \r
- return $result;\r
- }\r
- \r
- function TransformSchema( $schema, $xsl, $schematype='string' )\r
- {\r
- // Fail if XSLT extension is not available\r
- if( ! function_exists( 'xslt_create' ) ) {\r
- return FALSE;\r
- }\r
- \r
- $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';\r
- \r
- // look for xsl\r
- if( !is_readable( $xsl_file ) ) {\r
- return FALSE;\r
- }\r
- \r
- switch( $schematype )\r
- {\r
- case 'file':\r
- if( !is_readable( $schema ) ) {\r
- return FALSE;\r
- }\r
- \r
- $schema = _file_get_contents( $schema );\r
- break;\r
- case 'string':\r
- default:\r
- if( !is_string( $schema ) ) {\r
- return FALSE;\r
- }\r
- }\r
- \r
- $arguments = array (\r
- '/_xml' => $schema,\r
- '/_xsl' => _file_get_contents( $xsl_file )\r
- );\r
- \r
- // create an XSLT processor\r
- $xh = xslt_create ();\r
- \r
- // set error handler\r
- xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));\r
- \r
- // process the schema\r
- $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments); \r
- \r
- xslt_free ($xh);\r
- \r
- return $result;\r
- }\r
- \r
- /**\r
- * Processes XSLT transformation errors\r
- *\r
- * @param object $parser XML parser object\r
- * @param integer $errno Error number\r
- * @param integer $level Error level\r
- * @param array $fields Error information fields\r
- *\r
- * @access private\r
- */\r
- function xslt_error_handler( $parser, $errno, $level, $fields ) {\r
- if( is_array( $fields ) ) {\r
- $msg = array(\r
- 'Message Type' => ucfirst( $fields['msgtype'] ),\r
- 'Message Code' => $fields['code'],\r
- 'Message' => $fields['msg'],\r
- 'Error Number' => $errno,\r
- 'Level' => $level\r
- );\r
- \r
- switch( $fields['URI'] ) {\r
- case 'arg:/_xml':\r
- $msg['Input'] = 'XML';\r
- break;\r
- case 'arg:/_xsl':\r
- $msg['Input'] = 'XSL';\r
- break;\r
- default:\r
- $msg['Input'] = $fields['URI'];\r
- }\r
- \r
- $msg['Line'] = $fields['line'];\r
- } else {\r
- $msg = array(\r
- 'Message Type' => 'Error',\r
- 'Error Number' => $errno,\r
- 'Level' => $level,\r
- 'Fields' => var_export( $fields, TRUE )\r
- );\r
- }\r
- \r
- $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"\r
- . '<table>' . "\n";\r
- \r
- foreach( $msg as $label => $details ) {\r
- $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";\r
- }\r
- \r
- $error_details .= '</table>';\r
- \r
- trigger_error( $error_details, E_USER_ERROR );\r
- }\r
- \r
- /**\r
- * Returns the AXMLS Schema Version of the requested XML schema file.\r
- *\r
- * Call this method to obtain the AXMLS DTD version of the requested XML schema file.\r
- * @see SchemaStringVersion()\r
- *\r
- * @param string $filename AXMLS schema file\r
- * @return string Schema version number or FALSE on error\r
- */\r
- function SchemaFileVersion( $filename ) {\r
- // Open the file\r
- if( !($fp = fopen( $filename, 'r' )) ) {\r
- // die( 'Unable to open file' );\r
- return FALSE;\r
- }\r
- \r
- // Process the file\r
- while( $data = fread( $fp, 4096 ) ) {\r
- if( preg_match( $this->versionRegex, $data, $matches ) ) {\r
- return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;\r
- }\r
- }\r
- \r
- return FALSE;\r
- }\r
- \r
- /**\r
- * Returns the AXMLS Schema Version of the provided XML schema string.\r
- *\r
- * Call this method to obtain the AXMLS DTD version of the provided XML schema string.\r
- * @see SchemaFileVersion()\r
- *\r
- * @param string $xmlstring XML schema string\r
- * @return string Schema version number or FALSE on error\r
- */\r
- function SchemaStringVersion( $xmlstring ) {\r
- if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {\r
- return FALSE;\r
- }\r
- \r
- if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {\r
- return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;\r
- }\r
- \r
- return FALSE;\r
- }\r
- \r
- /**\r
- * Extracts an XML schema from an existing database.\r
- *\r
- * Call this method to create an XML schema string from an existing database.\r
- * If the data parameter is set to TRUE, AXMLS will include the data from the database\r
- * in the schema. \r
- *\r
- * @param boolean $data Include data in schema dump\r
- * @indent string indentation to use\r
- * @prefix string extract only tables with given prefix\r
- * @stripprefix strip prefix string when storing in XML schema\r
- * @return string Generated XML schema\r
- */\r
- function ExtractSchema( $data = FALSE, $indent = ' ', $prefix = '' , $stripprefix=false) {\r
- $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );\r
- \r
- $schema = '<?xml version="1.0"?>' . "\n"\r
- . '<schema version="' . $this->schemaVersion . '">' . "\n";\r
- \r
- if( is_array( $tables = $this->db->MetaTables( 'TABLES' , ($prefix) ? $prefix.'%' : '') ) ) {\r
- foreach( $tables as $table ) {\r
- if ($stripprefix) $table = str_replace(str_replace('\\_', '_', $pfx ), '', $table);\r
- $schema .= $indent . '<table name="' . htmlentities( $table ) . '">' . "\n";\r
- \r
- // grab details from database\r
- $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE -1' );\r
- $fields = $this->db->MetaColumns( $table );\r
- $indexes = $this->db->MetaIndexes( $table );\r
- \r
- if( is_array( $fields ) ) {\r
- foreach( $fields as $details ) {\r
- $extra = '';\r
- $content = array();\r
- \r
- if( isset($details->max_length) && $details->max_length > 0 ) {\r
- $extra .= ' size="' . $details->max_length . '"';\r
- }\r
- \r
- if( isset($details->primary_key) && $details->primary_key ) {\r
- $content[] = '<KEY/>';\r
- } elseif( isset($details->not_null) && $details->not_null ) {\r
- $content[] = '<NOTNULL/>';\r
- }\r
- \r
- if( isset($details->has_default) && $details->has_default ) {\r
- $content[] = '<DEFAULT value="' . htmlentities( $details->default_value ) . '"/>';\r
- }\r
- \r
- if( isset($details->auto_increment) && $details->auto_increment ) {\r
- $content[] = '<AUTOINCREMENT/>';\r
- }\r
- \r
- if( isset($details->unsigned) && $details->unsigned ) {\r
- $content[] = '<UNSIGNED/>';\r
- }\r
- \r
- // this stops the creation of 'R' columns,\r
- // AUTOINCREMENT is used to create auto columns\r
- $details->primary_key = 0;\r
- $type = $rs->MetaType( $details );\r
- \r
- $schema .= str_repeat( $indent, 2 ) . '<field name="' . htmlentities( $details->name ) . '" type="' . $type . '"' . $extra;\r
- \r
- if( !empty( $content ) ) {\r
- $schema .= ">\n" . str_repeat( $indent, 3 )\r
- . implode( "\n" . str_repeat( $indent, 3 ), $content ) . "\n"\r
- . str_repeat( $indent, 2 ) . '</field>' . "\n";\r
- } else {\r
- $schema .= "/>\n";\r
- }\r
- }\r
- }\r
- \r
- if( is_array( $indexes ) ) {\r
- foreach( $indexes as $index => $details ) {\r
- $schema .= str_repeat( $indent, 2 ) . '<index name="' . $index . '">' . "\n";\r
- \r
- if( $details['unique'] ) {\r
- $schema .= str_repeat( $indent, 3 ) . '<UNIQUE/>' . "\n";\r
- }\r
- \r
- foreach( $details['columns'] as $column ) {\r
- $schema .= str_repeat( $indent, 3 ) . '<col>' . htmlentities( $column ) . '</col>' . "\n";\r
- }\r
- \r
- $schema .= str_repeat( $indent, 2 ) . '</index>' . "\n";\r
- }\r
- }\r
- \r
- if( $data ) {\r
- $rs = $this->db->Execute( 'SELECT * FROM ' . $table );\r
- \r
- if( is_object( $rs ) && !$rs->EOF ) {\r
- $schema .= str_repeat( $indent, 2 ) . "<data>\n";\r
- \r
- while( $row = $rs->FetchRow() ) {\r
- foreach( $row as $key => $val ) {\r
- if ( $val != htmlentities( $val ) ) {\r
- $row[$key] = '<![CDATA[' . $val . ']]>';\r
- }\r
- }\r
- \r
- $schema .= str_repeat( $indent, 3 ) . '<row><f>' . implode( '</f><f>', $row ) . "</f></row>\n";\r
- }\r
- \r
- $schema .= str_repeat( $indent, 2 ) . "</data>\n";\r
- }\r
- }\r
- \r
- $schema .= $indent . "</table>\n";\r
- }\r
- }\r
- \r
- $this->db->SetFetchMode( $old_mode );\r
- \r
- $schema .= '</schema>';\r
- return $schema;\r
- }\r
- \r
- /**\r
- * Sets a prefix for database objects\r
- *\r
- * Call this method to set a standard prefix that will be prepended to all database tables \r
- * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.\r
- *\r
- * @param string $prefix Prefix that will be prepended.\r
- * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.\r
- * @return boolean TRUE if successful, else FALSE\r
- */\r
- function SetPrefix( $prefix = '', $underscore = TRUE ) {\r
- switch( TRUE ) {\r
- // clear prefix\r
- case empty( $prefix ):\r
- logMsg( 'Cleared prefix' );\r
- $this->objectPrefix = '';\r
- return TRUE;\r
- // prefix too long\r
- case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:\r
- // prefix contains invalid characters\r
- case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):\r
- logMsg( 'Invalid prefix: ' . $prefix );\r
- return FALSE;\r
- }\r
- \r
- if( $underscore AND substr( $prefix, -1 ) != '_' ) {\r
- $prefix .= '_';\r
- }\r
- \r
- // prefix valid\r
- logMsg( 'Set prefix: ' . $prefix );\r
- $this->objectPrefix = $prefix;\r
- return TRUE;\r
- }\r
- \r
- /**\r
- * Returns an object name with the current prefix prepended.\r
- *\r
- * @param string $name Name\r
- * @return string Prefixed name\r
- *\r
- * @access private\r
- */\r
- function prefix( $name = '' ) {\r
- // if prefix is set\r
- if( !empty( $this->objectPrefix ) ) {\r
- // Prepend the object prefix to the table name\r
- // prepend after quote if used\r
- return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );\r
- }\r
- \r
- // No prefix set. Use name provided.\r
- return $name;\r
- }\r
- \r
- /**\r
- * Checks if element references a specific platform\r
- *\r
- * @param string $platform Requested platform\r
- * @returns boolean TRUE if platform check succeeds\r
- *\r
- * @access private\r
- */\r
- function supportedPlatform( $platform = NULL ) {\r
- if( !empty( $platform ) ) {\r
- $regex = '/(^|\|)' . $this->db->databaseType . '(\||$)/i';\r
- \r
- if( preg_match( '/^- /', $platform ) ) {\r
- if (preg_match ( $regex, substr( $platform, 2 ) ) ) {\r
- logMsg( 'Platform ' . $platform . ' is NOT supported' );\r
- return FALSE;\r
- }\r
- } else {\r
- if( !preg_match ( $regex, $platform ) ) {\r
- logMsg( 'Platform ' . $platform . ' is NOT supported' );\r
- return FALSE;\r
- }\r
- }\r
- }\r
- \r
- logMsg( 'Platform ' . $platform . ' is supported' );\r
- return TRUE;\r
- }\r
- \r
- /**\r
- * Clears the array of generated SQL.\r
- *\r
- * @access private\r
- */\r
- function clearSQL() {\r
- $this->sqlArray = array();\r
- }\r
- \r
- /**\r
- * Adds SQL into the SQL array.\r
- *\r
- * @param mixed $sql SQL to Add\r
- * @return boolean TRUE if successful, else FALSE.\r
- *\r
- * @access private\r
- */ \r
- function addSQL( $sql = NULL ) {\r
- if( is_array( $sql ) ) {\r
- foreach( $sql as $line ) {\r
- $this->addSQL( $line );\r
- }\r
- \r
- return TRUE;\r
- }\r
- \r
- if( is_string( $sql ) ) {\r
- $this->sqlArray[] = $sql;\r
- \r
- // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.\r
- if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {\r
- $saved = $this->db->debug;\r
- $this->db->debug = $this->debug;\r
- $ok = $this->db->Execute( $sql );\r
- $this->db->debug = $saved;\r
- \r
- if( !$ok ) {\r
- if( $this->debug ) {\r
- ADOConnection::outp( $this->db->ErrorMsg() );\r
- }\r
- \r
- $this->success = 1;\r
- }\r
- }\r
- \r
- return TRUE;\r
- }\r
- \r
- return FALSE;\r
- }\r
- \r
- /**\r
- * Gets the SQL array in the specified format.\r
- *\r
- * @param string $format Format\r
- * @return mixed SQL\r
- * \r
- * @access private\r
- */\r
- function getSQL( $format = NULL, $sqlArray = NULL ) {\r
- if( !is_array( $sqlArray ) ) {\r
- $sqlArray = $this->sqlArray;\r
- }\r
- \r
- if( !is_array( $sqlArray ) ) {\r
- return FALSE;\r
- }\r
- \r
- switch( strtolower( $format ) ) {\r
- case 'string':\r
- case 'text':\r
- return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';\r
- case'html':\r
- return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';\r
- }\r
- \r
- return $this->sqlArray;\r
- }\r
- \r
- /**\r
- * Destroys an adoSchema object.\r
- *\r
- * Call this method to clean up after an adoSchema object that is no longer in use.\r
- * @deprecated adoSchema now cleans up automatically.\r
- */\r
- function Destroy() {\r
- set_magic_quotes_runtime( $this->mgq );\r
- unset( $this );\r
- }\r
-}\r
-\r
-/**\r
-* Message logging function\r
-*\r
-* @access private\r
-*/\r
-function logMsg( $msg, $title = NULL, $force = FALSE ) {\r
- if( XMLS_DEBUG or $force ) {\r
- echo '<pre>';\r
- \r
- if( isset( $title ) ) {\r
- echo '<h3>' . htmlentities( $title ) . '</h3>';\r
- }\r
- \r
- if( @is_object( $this ) ) {\r
- echo '[' . get_class( $this ) . '] ';\r
- }\r
- \r
- print_r( $msg );\r
- \r
- echo '</pre>';\r
- }\r
-}\r
+<?php
+// Copyright (c) 2004-2005 ars Cognita Inc., 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.
+*******************************************************************************/
+/**
+ * xmlschema is a class that allows the user to quickly and easily
+ * build a database on any ADOdb-supported platform using a simple
+ * XML schema.
+ *
+ * Last Editor: $Author$
+ * @author Richard Tango-Lowy & Dan Cech
+ * @version $Revision$
+ *
+ * @package axmls
+ * @tutorial getting_started.pkg
+ */
+
+function _file_get_contents($file)
+{
+ if (function_exists('file_get_contents')) return file_get_contents($file);
+
+ $f = fopen($file,'r');
+ if (!$f) return '';
+ $t = '';
+
+ while ($s = fread($f,100000)) $t .= $s;
+ fclose($f);
+ return $t;
+}
+
+
+/**
+* Debug on or off
+*/
+if( !defined( 'XMLS_DEBUG' ) ) {
+ define( 'XMLS_DEBUG', FALSE );
+}
+
+/**
+* Default prefix key
+*/
+if( !defined( 'XMLS_PREFIX' ) ) {
+ define( 'XMLS_PREFIX', '%%P' );
+}
+
+/**
+* Maximum length allowed for object prefix
+*/
+if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
+ define( 'XMLS_PREFIX_MAXLEN', 10 );
+}
+
+/**
+* Execute SQL inline as it is generated
+*/
+if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
+ define( 'XMLS_EXECUTE_INLINE', FALSE );
+}
+
+/**
+* Continue SQL Execution if an error occurs?
+*/
+if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
+ define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
+}
+
+/**
+* Current Schema Version
+*/
+if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
+ define( 'XMLS_SCHEMA_VERSION', '0.3' );
+}
+
+/**
+* Default Schema Version. Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
+ define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
+}
+
+/**
+* How to handle data rows that already exist in a database during and upgrade.
+* Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing
+* rows) and IGNORE (ignores existing rows).
+*/
+if( !defined( 'XMLS_MODE_INSERT' ) ) {
+ define( 'XMLS_MODE_INSERT', 0 );
+}
+if( !defined( 'XMLS_MODE_UPDATE' ) ) {
+ define( 'XMLS_MODE_UPDATE', 1 );
+}
+if( !defined( 'XMLS_MODE_IGNORE' ) ) {
+ define( 'XMLS_MODE_IGNORE', 2 );
+}
+if( !defined( 'XMLS_EXISTING_DATA' ) ) {
+ define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT );
+}
+
+/**
+* Default Schema Version. Used for Schemas without an explicit version set.
+*/
+if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
+ define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
+}
+
+/**
+* Include the main ADODB library
+*/
+if( !defined( '_ADODB_LAYER' ) ) {
+ require( 'adodb.inc.php' );
+ require( 'adodb-datadict.inc.php' );
+}
+
+/**
+* Abstract DB Object. This class provides basic methods for database objects, such
+* as tables and indexes.
+*
+* @package axmls
+* @access private
+*/
+class dbObject {
+
+ /**
+ * var object Parent
+ */
+ var $parent;
+
+ /**
+ * var string current element
+ */
+ var $currentElement;
+
+ /**
+ * NOP
+ */
+ function dbObject( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+
+ }
+
+ function create() {
+ return array();
+ }
+
+ /**
+ * Destroys the object
+ */
+ function destroy() {
+ unset( $this );
+ }
+
+ /**
+ * Checks whether the specified RDBMS is supported by the current
+ * database object or its ranking ancestor.
+ *
+ * @param string $platform RDBMS platform name (from ADODB platform list).
+ * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
+ */
+ function supportedPlatform( $platform = NULL ) {
+ return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
+ }
+
+ /**
+ * Returns the prefix set by the ranking ancestor of the database object.
+ *
+ * @param string $name Prefix string.
+ * @return string Prefix.
+ */
+ function prefix( $name = '' ) {
+ return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
+ }
+
+ /**
+ * Extracts a field ID from the specified field.
+ *
+ * @param string $field Field.
+ * @return string Field ID.
+ */
+ function FieldID( $field ) {
+ return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
+ }
+}
+
+/**
+* Creates a table object in ADOdb's datadict format
+*
+* This class stores information about a database table. As charactaristics
+* of the table are loaded from the external source, methods and properties
+* of this class are used to build up the table description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbTable extends dbObject {
+
+ /**
+ * @var string Table name
+ */
+ var $name;
+
+ /**
+ * @var array Field specifier: Meta-information about each field
+ */
+ var $fields = array();
+
+ /**
+ * @var array List of table indexes.
+ */
+ var $indexes = array();
+
+ /**
+ * @var array Table options: Table-level options
+ */
+ var $opts = array();
+
+ /**
+ * @var string Field index: Keeps track of which field is currently being processed
+ */
+ var $current_field;
+
+ /**
+ * @var boolean Mark table for destruction
+ * @access private
+ */
+ var $drop_table;
+
+ /**
+ * @var boolean Mark field for destruction (not yet implemented)
+ * @access private
+ */
+ var $drop_field = array();
+
+ /**
+ * @var array Platform-specific options
+ * @access private
+ */
+ var $currentPlatform = true;
+
+
+ /**
+ * Iniitializes a new table object.
+ *
+ * @param string $prefix DB Object prefix
+ * @param array $attributes Array of table attributes.
+ */
+ function dbTable( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+ $this->name = $this->prefix($attributes['NAME']);
+ }
+
+ /**
+ * XML Callback to process start elements. Elements currently
+ * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT.
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'INDEX':
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ xml_set_object( $parser, $this->addIndex( $attributes ) );
+ }
+ break;
+ case 'DATA':
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ xml_set_object( $parser, $this->addData( $attributes ) );
+ }
+ break;
+ case 'DROP':
+ $this->drop();
+ break;
+ case 'FIELD':
+ // Add a field
+ $fieldName = $attributes['NAME'];
+ $fieldType = $attributes['TYPE'];
+ $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
+ $fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
+
+ $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
+ break;
+ case 'KEY':
+ case 'NOTNULL':
+ case 'AUTOINCREMENT':
+ case 'DEFDATE':
+ case 'DEFTIMESTAMP':
+ case 'UNSIGNED':
+ // Add a field option
+ $this->addFieldOpt( $this->current_field, $this->currentElement );
+ break;
+ case 'DEFAULT':
+ // Add a field option to the table object
+
+ // Work around ADOdb datadict issue that misinterprets empty strings.
+ if( $attributes['VALUE'] == '' ) {
+ $attributes['VALUE'] = " '' ";
+ }
+
+ $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
+ break;
+ case 'OPT':
+ case 'CONSTRAINT':
+ // Accept platform-specific options
+ $this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) );
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Table/field constraint
+ case 'CONSTRAINT':
+ if( isset( $this->current_field ) ) {
+ $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
+ } else {
+ $this->addTableOpt( $cdata );
+ }
+ break;
+ // Table/field option
+ case 'OPT':
+ if( isset( $this->current_field ) ) {
+ $this->addFieldOpt( $this->current_field, $cdata );
+ } else {
+ $this->addTableOpt( $cdata );
+ }
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'TABLE':
+ $this->parent->addSQL( $this->create( $this->parent ) );
+ xml_set_object( $parser, $this->parent );
+ $this->destroy();
+ break;
+ case 'FIELD':
+ unset($this->current_field);
+ break;
+ case 'OPT':
+ case 'CONSTRAINT':
+ $this->currentPlatform = true;
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * Adds an index to a table object
+ *
+ * @param array $attributes Index attributes
+ * @return object dbIndex object
+ */
+ function addIndex( $attributes ) {
+ $name = strtoupper( $attributes['NAME'] );
+ $this->indexes[$name] = new dbIndex( $this, $attributes );
+ return $this->indexes[$name];
+ }
+
+ /**
+ * Adds data to a table object
+ *
+ * @param array $attributes Data attributes
+ * @return object dbData object
+ */
+ function addData( $attributes ) {
+ if( !isset( $this->data ) ) {
+ $this->data = new dbData( $this, $attributes );
+ }
+ return $this->data;
+ }
+
+ /**
+ * Adds a field to a table object
+ *
+ * $name is the name of the table to which the field should be added.
+ * $type is an ADODB datadict field type. The following field types
+ * are supported as of ADODB 3.40:
+ * - C: varchar
+ * - X: CLOB (character large object) or largest varchar size
+ * if CLOB is not supported
+ * - C2: Multibyte varchar
+ * - X2: Multibyte CLOB
+ * - B: BLOB (binary large object)
+ * - D: Date (some databases do not support this, and we return a datetime type)
+ * - T: Datetime or Timestamp
+ * - L: Integer field suitable for storing booleans (0 or 1)
+ * - I: Integer (mapped to I4)
+ * - I1: 1-byte integer
+ * - I2: 2-byte integer
+ * - I4: 4-byte integer
+ * - I8: 8-byte integer
+ * - F: Floating point number
+ * - N: Numeric or decimal number
+ *
+ * @param string $name Name of the table to which the field will be added.
+ * @param string $type ADODB datadict field type.
+ * @param string $size Field size
+ * @param array $opts Field options array
+ * @return array Field specifier array
+ */
+ function addField( $name, $type, $size = NULL, $opts = NULL ) {
+ $field_id = $this->FieldID( $name );
+
+ // Set the field index so we know where we are
+ $this->current_field = $field_id;
+
+ // Set the field name (required)
+ $this->fields[$field_id]['NAME'] = $name;
+
+ // Set the field type (required)
+ $this->fields[$field_id]['TYPE'] = $type;
+
+ // Set the field size (optional)
+ if( isset( $size ) ) {
+ $this->fields[$field_id]['SIZE'] = $size;
+ }
+
+ // Set the field options
+ if( isset( $opts ) ) {
+ $this->fields[$field_id]['OPTS'] = array($opts);
+ } else {
+ $this->fields[$field_id]['OPTS'] = array();
+ }
+ }
+
+ /**
+ * Adds a field option to the current field specifier
+ *
+ * This method adds a field option allowed by the ADOdb datadict
+ * and appends it to the given field.
+ *
+ * @param string $field Field name
+ * @param string $opt ADOdb field option
+ * @param mixed $value Field option value
+ * @return array Field specifier array
+ */
+ function addFieldOpt( $field, $opt, $value = NULL ) {
+ if( $this->currentPlatform ) {
+ if( !isset( $value ) ) {
+ $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
+ // Add the option and value
+ } else {
+ $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
+ }
+ }
+ }
+
+ /**
+ * Adds an option to the table
+ *
+ * This method takes a comma-separated list of table-level options
+ * and appends them to the table object.
+ *
+ * @param string $opt Table option
+ * @return array Options
+ */
+ function addTableOpt( $opt ) {
+ if(isset($this->currentPlatform)) {
+ $this->opts[$this->parent->db->databaseType] = $opt;
+ }
+ return $this->opts;
+ }
+
+
+ /**
+ * Generates the SQL that will create the table in the database
+ *
+ * @param object $xmls adoSchema object
+ * @return array Array containing table creation SQL
+ */
+ function create( &$xmls ) {
+ $sql = array();
+
+ // drop any existing indexes
+ if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
+ foreach( $legacy_indexes as $index => $index_details ) {
+ $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
+ }
+ }
+
+ // remove fields to be dropped from table object
+ foreach( $this->drop_field as $field ) {
+ unset( $this->fields[$field] );
+ }
+
+ // if table exists
+ if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
+ // drop table
+ if( $this->drop_table ) {
+ $sql[] = $xmls->dict->DropTableSQL( $this->name );
+
+ return $sql;
+ }
+
+ // drop any existing fields not in schema
+ foreach( $legacy_fields as $field_id => $field ) {
+ if( !isset( $this->fields[$field_id] ) ) {
+ $sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name );
+ }
+ }
+ // if table doesn't exist
+ } else {
+ if( $this->drop_table ) {
+ return $sql;
+ }
+
+ $legacy_fields = array();
+ }
+
+ // Loop through the field specifier array, building the associative array for the field options
+ $fldarray = array();
+
+ foreach( $this->fields as $field_id => $finfo ) {
+ // Set an empty size if it isn't supplied
+ if( !isset( $finfo['SIZE'] ) ) {
+ $finfo['SIZE'] = '';
+ }
+
+ // Initialize the field array with the type and size
+ $fldarray[$field_id] = array(
+ 'NAME' => $finfo['NAME'],
+ 'TYPE' => $finfo['TYPE'],
+ 'SIZE' => $finfo['SIZE']
+ );
+
+ // Loop through the options array and add the field options.
+ if( isset( $finfo['OPTS'] ) ) {
+ foreach( $finfo['OPTS'] as $opt ) {
+ // Option has an argument.
+ if( is_array( $opt ) ) {
+ $key = key( $opt );
+ $value = $opt[key( $opt )];
+ @$fldarray[$field_id][$key] .= $value;
+ // Option doesn't have arguments
+ } else {
+ $fldarray[$field_id][$opt] = $opt;
+ }
+ }
+ }
+ }
+
+ if( empty( $legacy_fields ) ) {
+ // Create the new table
+ $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+ logMsg( end( $sql ), 'Generated CreateTableSQL' );
+ } else {
+ // Upgrade an existing table
+ logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
+ switch( $xmls->upgrade ) {
+ // Use ChangeTableSQL
+ case 'ALTER':
+ logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
+ $sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
+ break;
+ case 'REPLACE':
+ logMsg( 'Doing upgrade REPLACE (testing)' );
+ $sql[] = $xmls->dict->DropTableSQL( $this->name );
+ $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
+ break;
+ // ignore table
+ default:
+ return array();
+ }
+ }
+
+ foreach( $this->indexes as $index ) {
+ $sql[] = $index->create( $xmls );
+ }
+
+ if( isset( $this->data ) ) {
+ $sql[] = $this->data->create( $xmls );
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Marks a field or table for destruction
+ */
+ function drop() {
+ if( isset( $this->current_field ) ) {
+ // Drop the current field
+ logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
+ // $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
+ $this->drop_field[$this->current_field] = $this->current_field;
+ } else {
+ // Drop the current table
+ logMsg( "Dropping table '{$this->name}'" );
+ // $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
+ $this->drop_table = TRUE;
+ }
+ }
+}
+
+/**
+* Creates an index object in ADOdb's datadict format
+*
+* This class stores information about a database index. As charactaristics
+* of the index are loaded from the external source, methods and properties
+* of this class are used to build up the index description in ADOdb's
+* datadict format.
+*
+* @package axmls
+* @access private
+*/
+class dbIndex extends dbObject {
+
+ /**
+ * @var string Index name
+ */
+ var $name;
+
+ /**
+ * @var array Index options: Index-level options
+ */
+ var $opts = array();
+
+ /**
+ * @var array Indexed fields: Table columns included in this index
+ */
+ var $columns = array();
+
+ /**
+ * @var boolean Mark index for destruction
+ * @access private
+ */
+ var $drop = FALSE;
+
+ /**
+ * Initializes the new dbIndex object.
+ *
+ * @param object $parent Parent object
+ * @param array $attributes Attributes
+ *
+ * @internal
+ */
+ function dbIndex( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+
+ $this->name = $this->prefix ($attributes['NAME']);
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * Processes XML opening tags.
+ * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH.
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'DROP':
+ $this->drop();
+ break;
+ case 'CLUSTERED':
+ case 'BITMAP':
+ case 'UNIQUE':
+ case 'FULLTEXT':
+ case 'HASH':
+ // Add index Option
+ $this->addIndexOpt( $this->currentElement );
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * Processes XML cdata.
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Index field name
+ case 'COL':
+ $this->addField( $cdata );
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'INDEX':
+ xml_set_object( $parser, $this->parent );
+ break;
+ }
+ }
+
+ /**
+ * Adds a field to the index
+ *
+ * @param string $name Field name
+ * @return string Field list
+ */
+ function addField( $name ) {
+ $this->columns[$this->FieldID( $name )] = $name;
+
+ // Return the field list
+ return $this->columns;
+ }
+
+ /**
+ * Adds options to the index
+ *
+ * @param string $opt Comma-separated list of index options.
+ * @return string Option list
+ */
+ function addIndexOpt( $opt ) {
+ $this->opts[] = $opt;
+
+ // Return the options list
+ return $this->opts;
+ }
+
+ /**
+ * Generates the SQL that will create the index in the database
+ *
+ * @param object $xmls adoSchema object
+ * @return array Array containing index creation SQL
+ */
+ function create( &$xmls ) {
+ if( $this->drop ) {
+ return NULL;
+ }
+
+ // eliminate any columns that aren't in the table
+ foreach( $this->columns as $id => $col ) {
+ if( !isset( $this->parent->fields[$id] ) ) {
+ unset( $this->columns[$id] );
+ }
+ }
+
+ return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
+ }
+
+ /**
+ * Marks an index for destruction
+ */
+ function drop() {
+ $this->drop = TRUE;
+ }
+}
+
+/**
+* Creates a data object in ADOdb's datadict format
+*
+* This class stores information about table data, and is called
+* when we need to load field data into a table.
+*
+* @package axmls
+* @access private
+*/
+class dbData extends dbObject {
+
+ var $data = array();
+
+ var $row;
+
+ /**
+ * Initializes the new dbData object.
+ *
+ * @param object $parent Parent object
+ * @param array $attributes Attributes
+ *
+ * @internal
+ */
+ function dbData( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * Processes XML opening tags.
+ * Elements currently processed are: ROW and F (field).
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'ROW':
+ $this->row = count( $this->data );
+ $this->data[$this->row] = array();
+ break;
+ case 'F':
+ $this->addField($attributes);
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * Processes XML cdata.
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Index field name
+ case 'F':
+ $this->addData( $cdata );
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'DATA':
+ xml_set_object( $parser, $this->parent );
+ break;
+ }
+ }
+
+ /**
+ * Adds a field to the insert
+ *
+ * @param string $name Field name
+ * @return string Field list
+ */
+ function addField( $attributes ) {
+ // check we're in a valid row
+ if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) {
+ return;
+ }
+
+ // Set the field index so we know where we are
+ if( isset( $attributes['NAME'] ) ) {
+ $this->current_field = $this->FieldID( $attributes['NAME'] );
+ } else {
+ $this->current_field = count( $this->data[$this->row] );
+ }
+
+ // initialise data
+ if( !isset( $this->data[$this->row][$this->current_field] ) ) {
+ $this->data[$this->row][$this->current_field] = '';
+ }
+ }
+
+ /**
+ * Adds options to the index
+ *
+ * @param string $opt Comma-separated list of index options.
+ * @return string Option list
+ */
+ function addData( $cdata ) {
+ // check we're in a valid field
+ if ( isset( $this->data[$this->row][$this->current_field] ) ) {
+ // add data to field
+ $this->data[$this->row][$this->current_field] .= $cdata;
+ }
+ }
+
+ /**
+ * Generates the SQL that will add/update the data in the database
+ *
+ * @param object $xmls adoSchema object
+ * @return array Array containing index creation SQL
+ */
+ function create( &$xmls ) {
+ $table = $xmls->dict->TableName($this->parent->name);
+ $table_field_count = count($this->parent->fields);
+ $tables = $xmls->db->MetaTables();
+ $sql = array();
+
+ $ukeys = $xmls->db->MetaPrimaryKeys( $table );
+ if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) {
+ foreach( $this->parent->indexes as $indexObj ) {
+ if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name;
+ }
+ }
+
+ // eliminate any columns that aren't in the table
+ foreach( $this->data as $row ) {
+ $table_fields = $this->parent->fields;
+ $fields = array();
+ $rawfields = array(); // Need to keep some of the unprocessed data on hand.
+
+ foreach( $row as $field_id => $field_data ) {
+ if( !array_key_exists( $field_id, $table_fields ) ) {
+ if( is_numeric( $field_id ) ) {
+ $field_id = reset( array_keys( $table_fields ) );
+ } else {
+ continue;
+ }
+ }
+
+ $name = $table_fields[$field_id]['NAME'];
+
+ switch( $table_fields[$field_id]['TYPE'] ) {
+ case 'I':
+ case 'I1':
+ case 'I2':
+ case 'I4':
+ case 'I8':
+ $fields[$name] = intval($field_data);
+ break;
+ case 'C':
+ case 'C2':
+ case 'X':
+ case 'X2':
+ default:
+ $fields[$name] = $xmls->db->qstr( $field_data );
+ $rawfields[$name] = $field_data;
+ }
+
+ unset($table_fields[$field_id]);
+
+ }
+
+ // check that at least 1 column is specified
+ if( empty( $fields ) ) {
+ continue;
+ }
+
+ // check that no required columns are missing
+ if( count( $fields ) < $table_field_count ) {
+ foreach( $table_fields as $field ) {
+ if( isset( $field['OPTS'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
+ continue(2);
+ }
+ }
+ }
+
+ // The rest of this method deals with updating existing data records.
+
+ if( !in_array( $table, $tables ) or ( $mode = $xmls->existingData() ) == XMLS_MODE_INSERT ) {
+ // Table doesn't yet exist, so it's safe to insert.
+ logMsg( "$table doesn't exist, inserting or mode is INSERT" );
+ $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
+ continue;
+ }
+
+ // Prepare to test for potential violations. Get primary keys and unique indexes
+ $mfields = array_merge( $fields, $rawfields );
+ $keyFields = array_intersect( $ukeys, array_keys( $mfields ) );
+
+ if( empty( $ukeys ) or count( $keyFields ) == 0 ) {
+ // No unique keys in schema, so safe to insert
+ logMsg( "Either schema or data has no unique keys, so safe to insert" );
+ $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')';
+ continue;
+ }
+
+ // Select record containing matching unique keys.
+ $where = '';
+ foreach( $ukeys as $key ) {
+ if( isset( $mfields[$key] ) and $mfields[$key] ) {
+ if( $where ) $where .= ' AND ';
+ $where .= $key . ' = ' . $xmls->db->qstr( $mfields[$key] );
+ }
+ }
+ $records = $xmls->db->Execute( 'SELECT * FROM ' . $table . ' WHERE ' . $where );
+ switch( $records->RecordCount() ) {
+ case 0:
+ // No matching record, so safe to insert.
+ logMsg( "No matching records. Inserting new row with unique data" );
+ $sql[] = $xmls->db->GetInsertSQL( $records, $mfields );
+ break;
+ case 1:
+ // Exactly one matching record, so we can update if the mode permits.
+ logMsg( "One matching record..." );
+ if( $mode == XMLS_MODE_UPDATE ) {
+ logMsg( "...Updating existing row from unique data" );
+ $sql[] = $xmls->db->GetUpdateSQL( $records, $mfields );
+ }
+ break;
+ default:
+ // More than one matching record; the result is ambiguous, so we must ignore the row.
+ logMsg( "More than one matching record. Ignoring row." );
+ }
+ }
+ return $sql;
+ }
+}
+
+/**
+* Creates the SQL to execute a list of provided SQL queries
+*
+* @package axmls
+* @access private
+*/
+class dbQuerySet extends dbObject {
+
+ /**
+ * @var array List of SQL queries
+ */
+ var $queries = array();
+
+ /**
+ * @var string String used to build of a query line by line
+ */
+ var $query;
+
+ /**
+ * @var string Query prefix key
+ */
+ var $prefixKey = '';
+
+ /**
+ * @var boolean Auto prefix enable (TRUE)
+ */
+ var $prefixMethod = 'AUTO';
+
+ /**
+ * Initializes the query set.
+ *
+ * @param object $parent Parent object
+ * @param array $attributes Attributes
+ */
+ function dbQuerySet( &$parent, $attributes = NULL ) {
+ $this->parent = $parent;
+
+ // Overrides the manual prefix key
+ if( isset( $attributes['KEY'] ) ) {
+ $this->prefixKey = $attributes['KEY'];
+ }
+
+ $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : '';
+
+ // Enables or disables automatic prefix prepending
+ switch( $prefixMethod ) {
+ case 'AUTO':
+ $this->prefixMethod = 'AUTO';
+ break;
+ case 'MANUAL':
+ $this->prefixMethod = 'MANUAL';
+ break;
+ case 'NONE':
+ $this->prefixMethod = 'NONE';
+ break;
+ }
+ }
+
+ /**
+ * XML Callback to process start elements. Elements currently
+ * processed are: QUERY.
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ $this->currentElement = strtoupper( $tag );
+
+ switch( $this->currentElement ) {
+ case 'QUERY':
+ // Create a new query in a SQL queryset.
+ // Ignore this query set if a platform is specified and it's different than the
+ // current connection platform.
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ $this->newQuery();
+ } else {
+ $this->discardQuery();
+ }
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ switch( $this->currentElement ) {
+ // Line of queryset SQL data
+ case 'QUERY':
+ $this->buildQuery( $cdata );
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ */
+ function _tag_close( &$parser, $tag ) {
+ $this->currentElement = '';
+
+ switch( strtoupper( $tag ) ) {
+ case 'QUERY':
+ // Add the finished query to the open query set.
+ $this->addQuery();
+ break;
+ case 'SQL':
+ $this->parent->addSQL( $this->create( $this->parent ) );
+ xml_set_object( $parser, $this->parent );
+ $this->destroy();
+ break;
+ default:
+
+ }
+ }
+
+ /**
+ * Re-initializes the query.
+ *
+ * @return boolean TRUE
+ */
+ function newQuery() {
+ $this->query = '';
+
+ return TRUE;
+ }
+
+ /**
+ * Discards the existing query.
+ *
+ * @return boolean TRUE
+ */
+ function discardQuery() {
+ unset( $this->query );
+
+ return TRUE;
+ }
+
+ /**
+ * Appends a line to a query that is being built line by line
+ *
+ * @param string $data Line of SQL data or NULL to initialize a new query
+ * @return string SQL query string.
+ */
+ function buildQuery( $sql = NULL ) {
+ if( !isset( $this->query ) OR empty( $sql ) ) {
+ return FALSE;
+ }
+
+ $this->query .= $sql;
+
+ return $this->query;
+ }
+
+ /**
+ * Adds a completed query to the query list
+ *
+ * @return string SQL of added query
+ */
+ function addQuery() {
+ if( !isset( $this->query ) ) {
+ return FALSE;
+ }
+
+ $this->queries[] = $return = trim($this->query);
+
+ unset( $this->query );
+
+ return $return;
+ }
+
+ /**
+ * Creates and returns the current query set
+ *
+ * @param object $xmls adoSchema object
+ * @return array Query set
+ */
+ function create( &$xmls ) {
+ foreach( $this->queries as $id => $query ) {
+ switch( $this->prefixMethod ) {
+ case 'AUTO':
+ // Enable auto prefix replacement
+
+ // Process object prefix.
+ // Evaluate SQL statements to prepend prefix to objects
+ $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+ $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+ $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix );
+
+ // SELECT statements aren't working yet
+ #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data );
+
+ case 'MANUAL':
+ // If prefixKey is set and has a value then we use it to override the default constant XMLS_PREFIX.
+ // If prefixKey is not set, we use the default constant XMLS_PREFIX
+ if( isset( $this->prefixKey ) AND( $this->prefixKey !== '' ) ) {
+ // Enable prefix override
+ $query = str_replace( $this->prefixKey, $xmls->objectPrefix, $query );
+ } else {
+ // Use default replacement
+ $query = str_replace( XMLS_PREFIX , $xmls->objectPrefix, $query );
+ }
+ }
+
+ $this->queries[$id] = trim( $query );
+ }
+
+ // Return the query set array
+ return $this->queries;
+ }
+
+ /**
+ * Rebuilds the query with the prefix attached to any objects
+ *
+ * @param string $regex Regex used to add prefix
+ * @param string $query SQL query string
+ * @param string $prefix Prefix to be appended to tables, indices, etc.
+ * @return string Prefixed SQL query string.
+ */
+ function prefixQuery( $regex, $query, $prefix = NULL ) {
+ if( !isset( $prefix ) ) {
+ return $query;
+ }
+
+ if( preg_match( $regex, $query, $match ) ) {
+ $preamble = $match[1];
+ $postamble = $match[5];
+ $objectList = explode( ',', $match[3] );
+ // $prefix = $prefix . '_';
+
+ $prefixedList = '';
+
+ foreach( $objectList as $object ) {
+ if( $prefixedList !== '' ) {
+ $prefixedList .= ', ';
+ }
+
+ $prefixedList .= $prefix . trim( $object );
+ }
+
+ $query = $preamble . ' ' . $prefixedList . ' ' . $postamble;
+ }
+
+ return $query;
+ }
+}
+
+/**
+* Loads and parses an XML file, creating an array of "ready-to-run" SQL statements
+*
+* This class is used to load and parse the XML file, to create an array of SQL statements
+* that can be used to build a database, and to build the database using the SQL array.
+*
+* @tutorial getting_started.pkg
+*
+* @author Richard Tango-Lowy & Dan Cech
+* @version $Revision$
+*
+* @package axmls
+*/
+class adoSchema {
+
+ /**
+ * @var array Array containing SQL queries to generate all objects
+ * @access private
+ */
+ var $sqlArray;
+
+ /**
+ * @var object ADOdb connection object
+ * @access private
+ */
+ var $db;
+
+ /**
+ * @var object ADOdb Data Dictionary
+ * @access private
+ */
+ var $dict;
+
+ /**
+ * @var string Current XML element
+ * @access private
+ */
+ var $currentElement = '';
+
+ /**
+ * @var string If set (to 'ALTER' or 'REPLACE'), upgrade an existing database
+ * @access private
+ */
+ var $upgrade = '';
+
+ /**
+ * @var string Optional object prefix
+ * @access private
+ */
+ var $objectPrefix = '';
+
+ /**
+ * @var long Original Magic Quotes Runtime value
+ * @access private
+ */
+ var $mgq;
+
+ /**
+ * @var long System debug
+ * @access private
+ */
+ var $debug;
+
+ /**
+ * @var string Regular expression to find schema version
+ * @access private
+ */
+ var $versionRegex = '/<schema.*?( version="([^"]*)")?.*?>/';
+
+ /**
+ * @var string Current schema version
+ * @access private
+ */
+ var $schemaVersion;
+
+ /**
+ * @var int Success of last Schema execution
+ */
+ var $success;
+
+ /**
+ * @var bool Execute SQL inline as it is generated
+ */
+ var $executeInline;
+
+ /**
+ * @var bool Continue SQL execution if errors occur
+ */
+ var $continueOnError;
+
+ /**
+ * @var int How to handle existing data rows (insert, update, or ignore)
+ */
+ var $existingData;
+
+ /**
+ * Creates an adoSchema object
+ *
+ * Creating an adoSchema object is the first step in processing an XML schema.
+ * The only parameter is an ADOdb database connection object, which must already
+ * have been created.
+ *
+ * @param object $db ADOdb database connection object.
+ */
+ function adoSchema( &$db ) {
+ // Initialize the environment
+ $this->mgq = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+
+ $this->db = $db;
+ $this->debug = $this->db->debug;
+ $this->dict = NewDataDictionary( $this->db );
+ $this->sqlArray = array();
+ $this->schemaVersion = XMLS_SCHEMA_VERSION;
+ $this->executeInline( XMLS_EXECUTE_INLINE );
+ $this->continueOnError( XMLS_CONTINUE_ON_ERROR );
+ $this->existingData( XMLS_EXISTING_DATA );
+ $this->setUpgradeMethod();
+ }
+
+ /**
+ * Sets the method to be used for upgrading an existing database
+ *
+ * Use this method to specify how existing database objects should be upgraded.
+ * The method option can be set to ALTER, REPLACE, BEST, or NONE. ALTER attempts to
+ * alter each database object directly, REPLACE attempts to rebuild each object
+ * from scratch, BEST attempts to determine the best upgrade method for each
+ * object, and NONE disables upgrading.
+ *
+ * This method is not yet used by AXMLS, but exists for backward compatibility.
+ * The ALTER method is automatically assumed when the adoSchema object is
+ * instantiated; other upgrade methods are not currently supported.
+ *
+ * @param string $method Upgrade method (ALTER|REPLACE|BEST|NONE)
+ * @returns string Upgrade method used
+ */
+ function SetUpgradeMethod( $method = '' ) {
+ if( !is_string( $method ) ) {
+ return FALSE;
+ }
+
+ $method = strtoupper( $method );
+
+ // Handle the upgrade methods
+ switch( $method ) {
+ case 'ALTER':
+ $this->upgrade = $method;
+ break;
+ case 'REPLACE':
+ $this->upgrade = $method;
+ break;
+ case 'BEST':
+ $this->upgrade = 'ALTER';
+ break;
+ case 'NONE':
+ $this->upgrade = 'NONE';
+ break;
+ default:
+ // Use default if no legitimate method is passed.
+ $this->upgrade = XMLS_DEFAULT_UPGRADE_METHOD;
+ }
+
+ return $this->upgrade;
+ }
+
+ /**
+ * Specifies how to handle existing data row when there is a unique key conflict.
+ *
+ * The existingData setting specifies how the parser should handle existing rows
+ * when a unique key violation occurs during the insert. This can happen when inserting
+ * data into an existing table with one or more primary keys or unique indexes.
+ * The existingData method takes one of three options: XMLS_MODE_INSERT attempts
+ * to always insert the data as a new row. In the event of a unique key violation,
+ * the database will generate an error. XMLS_MODE_UPDATE attempts to update the
+ * any existing rows with the new data based upon primary or unique key fields in
+ * the schema. If the data row in the schema specifies no unique fields, the row
+ * data will be inserted as a new row. XMLS_MODE_IGNORE specifies that any data rows
+ * that would result in a unique key violation be ignored; no inserts or updates will
+ * take place. For backward compatibility, the default setting is XMLS_MODE_INSERT,
+ * but XMLS_MODE_UPDATE will generally be the most appropriate setting.
+ *
+ * @param int $mode XMLS_MODE_INSERT, XMLS_MODE_UPDATE, or XMLS_MODE_IGNORE
+ * @return int current mode
+ */
+ function ExistingData( $mode = NULL ) {
+ if( is_int( $mode ) ) {
+ switch( $mode ) {
+ case XMLS_MODE_UPDATE:
+ $mode = XMLS_MODE_UPDATE;
+ break;
+ case XMLS_MODE_IGNORE:
+ $mode = XMLS_MODE_IGNORE;
+ break;
+ case XMLS_MODE_INSERT:
+ $mode = XMLS_MODE_INSERT;
+ break;
+ default:
+ $mode = XMLS_EXISTING_DATA;
+ break;
+ }
+ $this->existingData = $mode;
+ }
+
+ return $this->existingData;
+ }
+
+ /**
+ * Enables/disables inline SQL execution.
+ *
+ * Call this method to enable or disable inline execution of the schema. If the mode is set to TRUE (inline execution),
+ * AXMLS applies the SQL to the database immediately as each schema entity is parsed. If the mode
+ * is set to FALSE (post execution), AXMLS parses the entire schema and you will need to call adoSchema::ExecuteSchema()
+ * to apply the schema to the database.
+ *
+ * @param bool $mode execute
+ * @return bool current execution mode
+ *
+ * @see ParseSchema(), ExecuteSchema()
+ */
+ function ExecuteInline( $mode = NULL ) {
+ if( is_bool( $mode ) ) {
+ $this->executeInline = $mode;
+ }
+
+ return $this->executeInline;
+ }
+
+ /**
+ * Enables/disables SQL continue on error.
+ *
+ * Call this method to enable or disable continuation of SQL execution if an error occurs.
+ * If the mode is set to TRUE (continue), AXMLS will continue to apply SQL to the database, even if an error occurs.
+ * If the mode is set to FALSE (halt), AXMLS will halt execution of generated sql if an error occurs, though parsing
+ * of the schema will continue.
+ *
+ * @param bool $mode execute
+ * @return bool current continueOnError mode
+ *
+ * @see addSQL(), ExecuteSchema()
+ */
+ function ContinueOnError( $mode = NULL ) {
+ if( is_bool( $mode ) ) {
+ $this->continueOnError = $mode;
+ }
+
+ return $this->continueOnError;
+ }
+
+ /**
+ * Loads an XML schema from a file and converts it to SQL.
+ *
+ * Call this method to load the specified schema (see the DTD for the proper format) from
+ * the filesystem and generate the SQL necessary to create the database
+ * described. This method automatically converts the schema to the latest
+ * axmls schema version.
+ * @see ParseSchemaString()
+ *
+ * @param string $file Name of XML schema file.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute
+ */
+ function ParseSchema( $filename, $returnSchema = FALSE ) {
+ return $this->ParseSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+ }
+
+ /**
+ * Loads an XML schema from a file and converts it to SQL.
+ *
+ * Call this method to load the specified schema directly from a file (see
+ * the DTD for the proper format) and generate the SQL necessary to create
+ * the database described by the schema. Use this method when you are dealing
+ * with large schema files. Otherwise, ParseSchema() is faster.
+ * This method does not automatically convert the schema to the latest axmls
+ * schema version. You must convert the schema manually using either the
+ * ConvertSchemaFile() or ConvertSchemaString() method.
+ * @see ParseSchema()
+ * @see ConvertSchemaFile()
+ * @see ConvertSchemaString()
+ *
+ * @param string $file Name of XML schema file.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute.
+ *
+ * @deprecated Replaced by adoSchema::ParseSchema() and adoSchema::ParseSchemaString()
+ * @see ParseSchema(), ParseSchemaString()
+ */
+ function ParseSchemaFile( $filename, $returnSchema = FALSE ) {
+ // Open the file
+ if( !($fp = fopen( $filename, 'r' )) ) {
+ logMsg( 'Unable to open file' );
+ return FALSE;
+ }
+
+ // do version detection here
+ if( $this->SchemaFileVersion( $filename ) != $this->schemaVersion ) {
+ logMsg( 'Invalid Schema Version' );
+ return FALSE;
+ }
+
+ if( $returnSchema ) {
+ $xmlstring = '';
+ while( $data = fread( $fp, 4096 ) ) {
+ $xmlstring .= $data . "\n";
+ }
+ return $xmlstring;
+ }
+
+ $this->success = 2;
+
+ $xmlParser = $this->create_parser();
+
+ // Process the file
+ while( $data = fread( $fp, 4096 ) ) {
+ if( !xml_parse( $xmlParser, $data, feof( $fp ) ) ) {
+ die( sprintf(
+ "XML error: %s at line %d",
+ xml_error_string( xml_get_error_code( $xmlParser) ),
+ xml_get_current_line_number( $xmlParser)
+ ) );
+ }
+ }
+
+ xml_parser_free( $xmlParser );
+
+ return $this->sqlArray;
+ }
+
+ /**
+ * Converts an XML schema string to SQL.
+ *
+ * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+ * and generate the SQL necessary to create the database described by the schema.
+ * @see ParseSchema()
+ *
+ * @param string $xmlstring XML schema string.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute.
+ */
+ function ParseSchemaString( $xmlstring, $returnSchema = FALSE ) {
+ if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+ logMsg( 'Empty or Invalid Schema' );
+ return FALSE;
+ }
+
+ // do version detection here
+ if( $this->SchemaStringVersion( $xmlstring ) != $this->schemaVersion ) {
+ logMsg( 'Invalid Schema Version' );
+ return FALSE;
+ }
+
+ if( $returnSchema ) {
+ return $xmlstring;
+ }
+
+ $this->success = 2;
+
+ $xmlParser = $this->create_parser();
+
+ if( !xml_parse( $xmlParser, $xmlstring, TRUE ) ) {
+ die( sprintf(
+ "XML error: %s at line %d",
+ xml_error_string( xml_get_error_code( $xmlParser) ),
+ xml_get_current_line_number( $xmlParser)
+ ) );
+ }
+
+ xml_parser_free( $xmlParser );
+
+ return $this->sqlArray;
+ }
+
+ /**
+ * Loads an XML schema from a file and converts it to uninstallation SQL.
+ *
+ * Call this method to load the specified schema (see the DTD for the proper format) from
+ * the filesystem and generate the SQL necessary to remove the database described.
+ * @see RemoveSchemaString()
+ *
+ * @param string $file Name of XML schema file.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute
+ */
+ function RemoveSchema( $filename, $returnSchema = FALSE ) {
+ return $this->RemoveSchemaString( $this->ConvertSchemaFile( $filename ), $returnSchema );
+ }
+
+ /**
+ * Converts an XML schema string to uninstallation SQL.
+ *
+ * Call this method to parse a string containing an XML schema (see the DTD for the proper format)
+ * and generate the SQL necessary to uninstall the database described by the schema.
+ * @see RemoveSchema()
+ *
+ * @param string $schema XML schema string.
+ * @param bool $returnSchema Return schema rather than parsing.
+ * @return array Array of SQL queries, ready to execute.
+ */
+ function RemoveSchemaString( $schema, $returnSchema = FALSE ) {
+
+ // grab current version
+ if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+ return FALSE;
+ }
+
+ return $this->ParseSchemaString( $this->TransformSchema( $schema, 'remove-' . $version), $returnSchema );
+ }
+
+ /**
+ * Applies the current XML schema to the database (post execution).
+ *
+ * Call this method to apply the current schema (generally created by calling
+ * ParseSchema() or ParseSchemaString() ) to the database (creating the tables, indexes,
+ * and executing other SQL specified in the schema) after parsing.
+ * @see ParseSchema(), ParseSchemaString(), ExecuteInline()
+ *
+ * @param array $sqlArray Array of SQL statements that will be applied rather than
+ * the current schema.
+ * @param boolean $continueOnErr Continue to apply the schema even if an error occurs.
+ * @returns integer 0 if failure, 1 if errors, 2 if successful.
+ */
+ function ExecuteSchema( $sqlArray = NULL, $continueOnErr = NULL ) {
+ if( !is_bool( $continueOnErr ) ) {
+ $continueOnErr = $this->ContinueOnError();
+ }
+
+ if( !isset( $sqlArray ) ) {
+ $sqlArray = $this->sqlArray;
+ }
+
+ if( !is_array( $sqlArray ) ) {
+ $this->success = 0;
+ } else {
+ $this->success = $this->dict->ExecuteSQLArray( $sqlArray, $continueOnErr );
+ }
+
+ return $this->success;
+ }
+
+ /**
+ * Returns the current SQL array.
+ *
+ * Call this method to fetch the array of SQL queries resulting from
+ * ParseSchema() or ParseSchemaString().
+ *
+ * @param string $format Format: HTML, TEXT, or NONE (PHP array)
+ * @return array Array of SQL statements or FALSE if an error occurs
+ */
+ function PrintSQL( $format = 'NONE' ) {
+ $sqlArray = null;
+ return $this->getSQL( $format, $sqlArray );
+ }
+
+ /**
+ * Saves the current SQL array to the local filesystem as a list of SQL queries.
+ *
+ * Call this method to save the array of SQL queries (generally resulting from a
+ * parsed XML schema) to the filesystem.
+ *
+ * @param string $filename Path and name where the file should be saved.
+ * @return boolean TRUE if save is successful, else FALSE.
+ */
+ function SaveSQL( $filename = './schema.sql' ) {
+
+ if( !isset( $sqlArray ) ) {
+ $sqlArray = $this->sqlArray;
+ }
+ if( !isset( $sqlArray ) ) {
+ return FALSE;
+ }
+
+ $fp = fopen( $filename, "w" );
+
+ foreach( $sqlArray as $key => $query ) {
+ fwrite( $fp, $query . ";\n" );
+ }
+ fclose( $fp );
+ }
+
+ /**
+ * Create an xml parser
+ *
+ * @return object PHP XML parser object
+ *
+ * @access private
+ */
+ function create_parser() {
+ // Create the parser
+ $xmlParser = xml_parser_create();
+ xml_set_object( $xmlParser, $this );
+
+ // Initialize the XML callback functions
+ xml_set_element_handler( $xmlParser, '_tag_open', '_tag_close' );
+ xml_set_character_data_handler( $xmlParser, '_tag_cdata' );
+
+ return $xmlParser;
+ }
+
+ /**
+ * XML Callback to process start elements
+ *
+ * @access private
+ */
+ function _tag_open( &$parser, $tag, $attributes ) {
+ switch( strtoupper( $tag ) ) {
+ case 'TABLE':
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ $this->obj = new dbTable( $this, $attributes );
+ xml_set_object( $parser, $this->obj );
+ }
+ break;
+ case 'SQL':
+ if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
+ $this->obj = new dbQuerySet( $this, $attributes );
+ xml_set_object( $parser, $this->obj );
+ }
+ break;
+ default:
+ // print_r( array( $tag, $attributes ) );
+ }
+
+ }
+
+ /**
+ * XML Callback to process CDATA elements
+ *
+ * @access private
+ */
+ function _tag_cdata( &$parser, $cdata ) {
+ }
+
+ /**
+ * XML Callback to process end elements
+ *
+ * @access private
+ * @internal
+ */
+ function _tag_close( &$parser, $tag ) {
+
+ }
+
+ /**
+ * Converts an XML schema string to the specified DTD version.
+ *
+ * Call this method to convert a string containing an XML schema to a different AXMLS
+ * DTD version. For instance, to convert a schema created for an pre-1.0 version for
+ * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version
+ * parameter is specified, the schema will be converted to the current DTD version.
+ * If the newFile parameter is provided, the converted schema will be written to the specified
+ * file.
+ * @see ConvertSchemaFile()
+ *
+ * @param string $schema String containing XML schema that will be converted.
+ * @param string $newVersion DTD version to convert to.
+ * @param string $newFile File name of (converted) output file.
+ * @return string Converted XML schema or FALSE if an error occurs.
+ */
+ function ConvertSchemaString( $schema, $newVersion = NULL, $newFile = NULL ) {
+
+ // grab current version
+ if( !( $version = $this->SchemaStringVersion( $schema ) ) ) {
+ return FALSE;
+ }
+
+ if( !isset ($newVersion) ) {
+ $newVersion = $this->schemaVersion;
+ }
+
+ if( $version == $newVersion ) {
+ $result = $schema;
+ } else {
+ $result = $this->TransformSchema( $schema, 'convert-' . $version . '-' . $newVersion);
+ }
+
+ if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+ fwrite( $fp, $result );
+ fclose( $fp );
+ }
+
+ return $result;
+ }
+
+ /*
+ // compat for pre-4.3 - jlim
+ function _file_get_contents($path)
+ {
+ if (function_exists('file_get_contents')) return file_get_contents($path);
+ return join('',file($path));
+ }*/
+
+ /**
+ * Converts an XML schema file to the specified DTD version.
+ *
+ * Call this method to convert the specified XML schema file to a different AXMLS
+ * DTD version. For instance, to convert a schema created for an pre-1.0 version for
+ * AXMLS (DTD version 0.1) to a newer version of the DTD (e.g. 0.2). If no DTD version
+ * parameter is specified, the schema will be converted to the current DTD version.
+ * If the newFile parameter is provided, the converted schema will be written to the specified
+ * file.
+ * @see ConvertSchemaString()
+ *
+ * @param string $filename Name of XML schema file that will be converted.
+ * @param string $newVersion DTD version to convert to.
+ * @param string $newFile File name of (converted) output file.
+ * @return string Converted XML schema or FALSE if an error occurs.
+ */
+ function ConvertSchemaFile( $filename, $newVersion = NULL, $newFile = NULL ) {
+
+ // grab current version
+ if( !( $version = $this->SchemaFileVersion( $filename ) ) ) {
+ return FALSE;
+ }
+
+ if( !isset ($newVersion) ) {
+ $newVersion = $this->schemaVersion;
+ }
+
+ if( $version == $newVersion ) {
+ $result = _file_get_contents( $filename );
+
+ // remove unicode BOM if present
+ if( substr( $result, 0, 3 ) == sprintf( '%c%c%c', 239, 187, 191 ) ) {
+ $result = substr( $result, 3 );
+ }
+ } else {
+ $result = $this->TransformSchema( $filename, 'convert-' . $version . '-' . $newVersion, 'file' );
+ }
+
+ if( is_string( $result ) AND is_string( $newFile ) AND ( $fp = fopen( $newFile, 'w' ) ) ) {
+ fwrite( $fp, $result );
+ fclose( $fp );
+ }
+
+ return $result;
+ }
+
+ function TransformSchema( $schema, $xsl, $schematype='string' )
+ {
+ // Fail if XSLT extension is not available
+ if( ! function_exists( 'xslt_create' ) ) {
+ return FALSE;
+ }
+
+ $xsl_file = dirname( __FILE__ ) . '/xsl/' . $xsl . '.xsl';
+
+ // look for xsl
+ if( !is_readable( $xsl_file ) ) {
+ return FALSE;
+ }
+
+ switch( $schematype )
+ {
+ case 'file':
+ if( !is_readable( $schema ) ) {
+ return FALSE;
+ }
+
+ $schema = _file_get_contents( $schema );
+ break;
+ case 'string':
+ default:
+ if( !is_string( $schema ) ) {
+ return FALSE;
+ }
+ }
+
+ $arguments = array (
+ '/_xml' => $schema,
+ '/_xsl' => _file_get_contents( $xsl_file )
+ );
+
+ // create an XSLT processor
+ $xh = xslt_create ();
+
+ // set error handler
+ xslt_set_error_handler ($xh, array (&$this, 'xslt_error_handler'));
+
+ // process the schema
+ $result = xslt_process ($xh, 'arg:/_xml', 'arg:/_xsl', NULL, $arguments);
+
+ xslt_free ($xh);
+
+ return $result;
+ }
+
+ /**
+ * Processes XSLT transformation errors
+ *
+ * @param object $parser XML parser object
+ * @param integer $errno Error number
+ * @param integer $level Error level
+ * @param array $fields Error information fields
+ *
+ * @access private
+ */
+ function xslt_error_handler( $parser, $errno, $level, $fields ) {
+ if( is_array( $fields ) ) {
+ $msg = array(
+ 'Message Type' => ucfirst( $fields['msgtype'] ),
+ 'Message Code' => $fields['code'],
+ 'Message' => $fields['msg'],
+ 'Error Number' => $errno,
+ 'Level' => $level
+ );
+
+ switch( $fields['URI'] ) {
+ case 'arg:/_xml':
+ $msg['Input'] = 'XML';
+ break;
+ case 'arg:/_xsl':
+ $msg['Input'] = 'XSL';
+ break;
+ default:
+ $msg['Input'] = $fields['URI'];
+ }
+
+ $msg['Line'] = $fields['line'];
+ } else {
+ $msg = array(
+ 'Message Type' => 'Error',
+ 'Error Number' => $errno,
+ 'Level' => $level,
+ 'Fields' => var_export( $fields, TRUE )
+ );
+ }
+
+ $error_details = $msg['Message Type'] . ' in XSLT Transformation' . "\n"
+ . '<table>' . "\n";
+
+ foreach( $msg as $label => $details ) {
+ $error_details .= '<tr><td><b>' . $label . ': </b></td><td>' . htmlentities( $details ) . '</td></tr>' . "\n";
+ }
+
+ $error_details .= '</table>';
+
+ trigger_error( $error_details, E_USER_ERROR );
+ }
+
+ /**
+ * Returns the AXMLS Schema Version of the requested XML schema file.
+ *
+ * Call this method to obtain the AXMLS DTD version of the requested XML schema file.
+ * @see SchemaStringVersion()
+ *
+ * @param string $filename AXMLS schema file
+ * @return string Schema version number or FALSE on error
+ */
+ function SchemaFileVersion( $filename ) {
+ // Open the file
+ if( !($fp = fopen( $filename, 'r' )) ) {
+ // die( 'Unable to open file' );
+ return FALSE;
+ }
+
+ // Process the file
+ while( $data = fread( $fp, 4096 ) ) {
+ if( preg_match( $this->versionRegex, $data, $matches ) ) {
+ return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+ }
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Returns the AXMLS Schema Version of the provided XML schema string.
+ *
+ * Call this method to obtain the AXMLS DTD version of the provided XML schema string.
+ * @see SchemaFileVersion()
+ *
+ * @param string $xmlstring XML schema string
+ * @return string Schema version number or FALSE on error
+ */
+ function SchemaStringVersion( $xmlstring ) {
+ if( !is_string( $xmlstring ) OR empty( $xmlstring ) ) {
+ return FALSE;
+ }
+
+ if( preg_match( $this->versionRegex, $xmlstring, $matches ) ) {
+ return !empty( $matches[2] ) ? $matches[2] : XMLS_DEFAULT_SCHEMA_VERSION;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Extracts an XML schema from an existing database.
+ *
+ * Call this method to create an XML schema string from an existing database.
+ * If the data parameter is set to TRUE, AXMLS will include the data from the database
+ * in the schema.
+ *
+ * @param boolean $data Include data in schema dump
+ * @indent string indentation to use
+ * @prefix string extract only tables with given prefix
+ * @stripprefix strip prefix string when storing in XML schema
+ * @return string Generated XML schema
+ */
+ function ExtractSchema( $data = FALSE, $indent = ' ', $prefix = '' , $stripprefix=false) {
+ $old_mode = $this->db->SetFetchMode( ADODB_FETCH_NUM );
+
+ $schema = '<?xml version="1.0"?>' . "\n"
+ . '<schema version="' . $this->schemaVersion . '">' . "\n";
+
+ if( is_array( $tables = $this->db->MetaTables( 'TABLES' , ($prefix) ? $prefix.'%' : '') ) ) {
+ foreach( $tables as $table ) {
+ if ($stripprefix) $table = str_replace(str_replace('\\_', '_', $pfx ), '', $table);
+ $schema .= $indent . '<table name="' . htmlentities( $table ) . '">' . "\n";
+
+ // grab details from database
+ $rs = $this->db->Execute( 'SELECT * FROM ' . $table . ' WHERE -1' );
+ $fields = $this->db->MetaColumns( $table );
+ $indexes = $this->db->MetaIndexes( $table );
+
+ if( is_array( $fields ) ) {
+ foreach( $fields as $details ) {
+ $extra = '';
+ $content = array();
+
+ if( isset($details->max_length) && $details->max_length > 0 ) {
+ $extra .= ' size="' . $details->max_length . '"';
+ }
+
+ if( isset($details->primary_key) && $details->primary_key ) {
+ $content[] = '<KEY/>';
+ } elseif( isset($details->not_null) && $details->not_null ) {
+ $content[] = '<NOTNULL/>';
+ }
+
+ if( isset($details->has_default) && $details->has_default ) {
+ $content[] = '<DEFAULT value="' . htmlentities( $details->default_value ) . '"/>';
+ }
+
+ if( isset($details->auto_increment) && $details->auto_increment ) {
+ $content[] = '<AUTOINCREMENT/>';
+ }
+
+ if( isset($details->unsigned) && $details->unsigned ) {
+ $content[] = '<UNSIGNED/>';
+ }
+
+ // this stops the creation of 'R' columns,
+ // AUTOINCREMENT is used to create auto columns
+ $details->primary_key = 0;
+ $type = $rs->MetaType( $details );
+
+ $schema .= str_repeat( $indent, 2 ) . '<field name="' . htmlentities( $details->name ) . '" type="' . $type . '"' . $extra;
+
+ if( !empty( $content ) ) {
+ $schema .= ">\n" . str_repeat( $indent, 3 )
+ . implode( "\n" . str_repeat( $indent, 3 ), $content ) . "\n"
+ . str_repeat( $indent, 2 ) . '</field>' . "\n";
+ } else {
+ $schema .= "/>\n";
+ }
+ }
+ }
+
+ if( is_array( $indexes ) ) {
+ foreach( $indexes as $index => $details ) {
+ $schema .= str_repeat( $indent, 2 ) . '<index name="' . $index . '">' . "\n";
+
+ if( $details['unique'] ) {
+ $schema .= str_repeat( $indent, 3 ) . '<UNIQUE/>' . "\n";
+ }
+
+ foreach( $details['columns'] as $column ) {
+ $schema .= str_repeat( $indent, 3 ) . '<col>' . htmlentities( $column ) . '</col>' . "\n";
+ }
+
+ $schema .= str_repeat( $indent, 2 ) . '</index>' . "\n";
+ }
+ }
+
+ if( $data ) {
+ $rs = $this->db->Execute( 'SELECT * FROM ' . $table );
+
+ if( is_object( $rs ) && !$rs->EOF ) {
+ $schema .= str_repeat( $indent, 2 ) . "<data>\n";
+
+ while( $row = $rs->FetchRow() ) {
+ foreach( $row as $key => $val ) {
+ if ( $val != htmlentities( $val ) ) {
+ $row[$key] = '<![CDATA[' . $val . ']]>';
+ }
+ }
+
+ $schema .= str_repeat( $indent, 3 ) . '<row><f>' . implode( '</f><f>', $row ) . "</f></row>\n";
+ }
+
+ $schema .= str_repeat( $indent, 2 ) . "</data>\n";
+ }
+ }
+
+ $schema .= $indent . "</table>\n";
+ }
+ }
+
+ $this->db->SetFetchMode( $old_mode );
+
+ $schema .= '</schema>';
+ return $schema;
+ }
+
+ /**
+ * Sets a prefix for database objects
+ *
+ * Call this method to set a standard prefix that will be prepended to all database tables
+ * and indices when the schema is parsed. Calling setPrefix with no arguments clears the prefix.
+ *
+ * @param string $prefix Prefix that will be prepended.
+ * @param boolean $underscore If TRUE, automatically append an underscore character to the prefix.
+ * @return boolean TRUE if successful, else FALSE
+ */
+ function SetPrefix( $prefix = '', $underscore = TRUE ) {
+ switch( TRUE ) {
+ // clear prefix
+ case empty( $prefix ):
+ logMsg( 'Cleared prefix' );
+ $this->objectPrefix = '';
+ return TRUE;
+ // prefix too long
+ case strlen( $prefix ) > XMLS_PREFIX_MAXLEN:
+ // prefix contains invalid characters
+ case !preg_match( '/^[a-z][a-z0-9_]+$/i', $prefix ):
+ logMsg( 'Invalid prefix: ' . $prefix );
+ return FALSE;
+ }
+
+ if( $underscore AND substr( $prefix, -1 ) != '_' ) {
+ $prefix .= '_';
+ }
+
+ // prefix valid
+ logMsg( 'Set prefix: ' . $prefix );
+ $this->objectPrefix = $prefix;
+ return TRUE;
+ }
+
+ /**
+ * Returns an object name with the current prefix prepended.
+ *
+ * @param string $name Name
+ * @return string Prefixed name
+ *
+ * @access private
+ */
+ function prefix( $name = '' ) {
+ // if prefix is set
+ if( !empty( $this->objectPrefix ) ) {
+ // Prepend the object prefix to the table name
+ // prepend after quote if used
+ return preg_replace( '/^(`?)(.+)$/', '$1' . $this->objectPrefix . '$2', $name );
+ }
+
+ // No prefix set. Use name provided.
+ return $name;
+ }
+
+ /**
+ * Checks if element references a specific platform
+ *
+ * @param string $platform Requested platform
+ * @returns boolean TRUE if platform check succeeds
+ *
+ * @access private
+ */
+ function supportedPlatform( $platform = NULL ) {
+ if( !empty( $platform ) ) {
+ $regex = '/(^|\|)' . $this->db->databaseType . '(\||$)/i';
+
+ if( preg_match( '/^- /', $platform ) ) {
+ if (preg_match ( $regex, substr( $platform, 2 ) ) ) {
+ logMsg( 'Platform ' . $platform . ' is NOT supported' );
+ return FALSE;
+ }
+ } else {
+ if( !preg_match ( $regex, $platform ) ) {
+ logMsg( 'Platform ' . $platform . ' is NOT supported' );
+ return FALSE;
+ }
+ }
+ }
+
+ logMsg( 'Platform ' . $platform . ' is supported' );
+ return TRUE;
+ }
+
+ /**
+ * Clears the array of generated SQL.
+ *
+ * @access private
+ */
+ function clearSQL() {
+ $this->sqlArray = array();
+ }
+
+ /**
+ * Adds SQL into the SQL array.
+ *
+ * @param mixed $sql SQL to Add
+ * @return boolean TRUE if successful, else FALSE.
+ *
+ * @access private
+ */
+ function addSQL( $sql = NULL ) {
+ if( is_array( $sql ) ) {
+ foreach( $sql as $line ) {
+ $this->addSQL( $line );
+ }
+
+ return TRUE;
+ }
+
+ if( is_string( $sql ) ) {
+ $this->sqlArray[] = $sql;
+
+ // if executeInline is enabled, and either no errors have occurred or continueOnError is enabled, execute SQL.
+ if( $this->ExecuteInline() && ( $this->success == 2 || $this->ContinueOnError() ) ) {
+ $saved = $this->db->debug;
+ $this->db->debug = $this->debug;
+ $ok = $this->db->Execute( $sql );
+ $this->db->debug = $saved;
+
+ if( !$ok ) {
+ if( $this->debug ) {
+ ADOConnection::outp( $this->db->ErrorMsg() );
+ }
+
+ $this->success = 1;
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+
+ /**
+ * Gets the SQL array in the specified format.
+ *
+ * @param string $format Format
+ * @return mixed SQL
+ *
+ * @access private
+ */
+ function getSQL( $format = NULL, $sqlArray = NULL ) {
+ if( !is_array( $sqlArray ) ) {
+ $sqlArray = $this->sqlArray;
+ }
+
+ if( !is_array( $sqlArray ) ) {
+ return FALSE;
+ }
+
+ switch( strtolower( $format ) ) {
+ case 'string':
+ case 'text':
+ return !empty( $sqlArray ) ? implode( ";\n\n", $sqlArray ) . ';' : '';
+ case'html':
+ return !empty( $sqlArray ) ? nl2br( htmlentities( implode( ";\n\n", $sqlArray ) . ';' ) ) : '';
+ }
+
+ return $this->sqlArray;
+ }
+
+ /**
+ * Destroys an adoSchema object.
+ *
+ * Call this method to clean up after an adoSchema object that is no longer in use.
+ * @deprecated adoSchema now cleans up automatically.
+ */
+ function Destroy() {
+ set_magic_quotes_runtime( $this->mgq );
+ unset( $this );
+ }
+}
+
+/**
+* Message logging function
+*
+* @access private
+*/
+function logMsg( $msg, $title = NULL, $force = FALSE ) {
+ if( XMLS_DEBUG or $force ) {
+ echo '<pre>';
+
+ if( isset( $title ) ) {
+ echo '<h3>' . htmlentities( $title ) . '</h3>';
+ }
+
+ if( @is_object( $this ) ) {
+ echo '[' . get_class( $this ) . '] ';
+ }
+
+ print_r( $msg );
+
+ echo '</pre>';
+ }
+}
?>
\ No newline at end of file
*/
/**
- \mainpage
+ \mainpage
- @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ @version V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
Released under both BSD license and Lesser GPL library license. You can choose which license
you prefer.
$ADODB_vers, // database version
$ADODB_COUNTRECS, // count number of records returned - slows down query
$ADODB_CACHE_DIR, // directory to cache recordsets
+ $ADODB_CACHE,
+ $ADODB_CACHE_CLASS,
$ADODB_EXTENSION, // ADODB extension installed
$ADODB_COMPAT_FETCH, // If $ADODB_COUNTRECS and this is true, $rs->fields is available on EOF
$ADODB_FETCH_MODE, // DEFAULT, NUM, ASSOC or BOTH. Default follows native driver default...
+ $ADODB_GETONE_EOF,
$ADODB_QUOTE_FIELDNAMES; // Allows you to force quotes (backticks) around field names in queries generated by getinsertsql and getupdatesql.
//==============================================================================================
die("PHP5 or later required. You are running ".PHP_VERSION);
}
+
//if (!defined('ADODB_ASSOC_CASE')) define('ADODB_ASSOC_CASE',2);
$ADODB_COUNTRECS, // count number of records returned - slows down query
$ADODB_CACHE_DIR, // directory to cache recordsets
$ADODB_FETCH_MODE,
+ $ADODB_CACHE,
+ $ADODB_CACHE_CLASS,
$ADODB_FORCE_TYPE,
+ $ADODB_GETONE_EOF,
$ADODB_QUOTE_FIELDNAMES;
+ if (empty($ADODB_CACHE_CLASS)) $ADODB_CACHE_CLASS = 'ADODB_Cache_File' ;
$ADODB_FETCH_MODE = ADODB_FETCH_DEFAULT;
$ADODB_FORCE_TYPE = ADODB_FORCE_VALUE;
-
+ $ADODB_GETONE_EOF = null;
if (!isset($ADODB_CACHE_DIR)) {
$ADODB_CACHE_DIR = '/tmp'; //(isset($_ENV['TMP'])) ? $_ENV['TMP'] : '/tmp';
/**
* ADODB version as a string.
*/
- $ADODB_vers = 'V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved. Released BSD & LGPL.';
+ $ADODB_vers = 'V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved. Released BSD & LGPL.';
/**
* Determines whether recordset->RecordCount() is used.
*/
}
-
+ // for transaction handling
function ADODB_TransMonitor($dbms, $fn, $errno, $errmsg, $p1, $p2, &$thisConnection)
{
}
}
+ //------------------
+ // class for caching
+ class ADODB_Cache_File {
+
+ var $createdir = true; // requires creation of temp dirs
+
+ function ADODB_Cache_File()
+ {
+ global $ADODB_INCLUDED_CSV;
+ if (empty($ADODB_INCLUDED_CSV)) include_once(ADODB_DIR.'/adodb-csvlib.inc.php');
+ }
+
+ // write serialised recordset to cache item/file
+ function writecache($filename, $contents, $debug, $secs2cache)
+ {
+ return adodb_write_file($filename, $contents,$debug);
+ }
+
+ // load serialised recordset and unserialise it
+ function &readcache($filename, &$err, $secs2cache, $rsClass)
+ {
+ $rs = csv2rs($filename,$err,$secs2cache,$rsClass);
+ return $rs;
+ }
+
+ // flush all items in cache
+ function flushall($debug=false)
+ {
+ global $ADODB_CACHE_DIR;
+
+ $rez = false;
+
+ if (strlen($ADODB_CACHE_DIR) > 1) {
+ $rez = $this->_dirFlush($ADODB_CACHE_DIR);
+ if ($debug) ADOConnection::outp( "flushall: $dir<br><pre>\n". $rez."</pre>");
+ }
+ return $rez;
+ }
+
+ // flush one file in cache
+ function flushcache($f, $debug=false)
+ {
+ if (!@unlink($f)) {
+ if ($debug) ADOConnection::outp( "flushcache: failed for $f");
+ }
+ }
+
+ function getdirname($hash)
+ {
+ global $ADODB_CACHE_DIR;
+ if (!isset($this->notSafeMode)) $this->notSafeMode = !ini_get('safe_mode');
+ return ($this->notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($hash,0,2) : $ADODB_CACHE_DIR;
+ }
+
+ // create temp directories
+ function createdir($hash, $debug)
+ {
+ $dir = $this->getdirname($hash);
+ if ($this->notSafeMode && !file_exists($dir)) {
+ $oldu = umask(0);
+ if (!@mkdir($dir,0771)) if(!is_dir($dir) && $debug) ADOConnection::outp("Cannot create $dir");
+ umask($oldu);
+ }
+
+ return $dir;
+ }
+
+ /**
+ * Private function to erase all of the files and subdirectories in a directory.
+ *
+ * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
+ * Note: $kill_top_level is used internally in the function to flush subdirectories.
+ */
+ function _dirFlush($dir, $kill_top_level = false)
+ {
+ if(!$dh = @opendir($dir)) return;
+
+ while (($obj = readdir($dh))) {
+ if($obj=='.' || $obj=='..') continue;
+ $f = $dir.'/'.$obj;
+
+ if (strpos($obj,'.cache')) @unlink($f);
+ if (is_dir($f)) $this->_dirFlush($f, true);
+ }
+ if ($kill_top_level === true) @rmdir($dir);
+ return true;
+ }
+ }
+
//==============================================================================================
// CLASS ADOConnection
//==============================================================================================
if ($argDatabaseName != "") $this->database = $argDatabaseName;
$this->_isPersistentConnection = false;
+
+ global $ADODB_CACHE;
+ if (empty($ADODB_CACHE)) $this->_CreateCache();
+
if ($forceNew) {
if ($rez=$this->_nconnect($this->host, $this->user, $this->password, $this->database)) return true;
} else {
*/
function PConnect($argHostname = "", $argUsername = "", $argPassword = "", $argDatabaseName = "")
{
+
if (defined('ADODB_NEVER_PERSIST'))
return $this->Connect($argHostname,$argUsername,$argPassword,$argDatabaseName);
if ($argDatabaseName != "") $this->database = $argDatabaseName;
$this->_isPersistentConnection = true;
+
+ global $ADODB_CACHE;
+ if (empty($ADODB_CACHE)) $this->_CreateCache();
+
if ($rez = $this->_pconnect($this->host, $this->user, $this->password, $this->database)) return true;
if (isset($rez)) {
$err = $this->ErrorMsg();
ADOConnection::outp($msg);
}
+ // create cache class. Code is backward compat with old memcache implementation
+ function _CreateCache()
+ {
+ global $ADODB_CACHE, $ADODB_CACHE_CLASS;
+
+ if ($this->memCache) {
+ global $ADODB_INCLUDED_MEMCACHE;
+
+ if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
+ $ADODB_CACHE = new ADODB_Cache_MemCache($this);
+ } else
+ $ADODB_CACHE = new $ADODB_CACHE_CLASS($this);
+
+ }
+
// Format date column in sql string given an input format that understands Y M D
function SQLDate($fmt, $col=false)
{
{
if ($this->transOff > 0) {
$this->transOff += 1;
- return;
+ return true;
}
$this->_oldRaiseFn = $this->raiseErrorFn;
$this->_transOK = true;
if ($this->debug && $this->transCnt > 0) ADOConnection::outp("Bad Transaction: StartTrans called within BeginTrans");
- $this->BeginTrans();
+ $ok = $this->BeginTrans();
$this->transOff = 1;
+ return $ok;
}
if (!is_array($sql) && !$this->_bindInputArray) {
$sqlarr = explode('?',$sql);
-
+ $nparams = sizeof($sqlarr)-1;
if (!$array_2d) $inputarr = array($inputarr);
foreach($inputarr as $arr) {
$sql = ''; $i = 0;
else
$sql .= $v;
$i += 1;
- }
+
+ if ($i == $nparams) break;
+ } // while
if (isset($sqlarr[$i])) {
$sql .= $sqlarr[$i];
if ($i+1 != sizeof($sqlarr)) $this->outp_throw( "Input Array does not match ?: ".htmlspecialchars($sql),'Execute');
$savec = $ADODB_COUNTRECS;
$ADODB_COUNTRECS = false;
- if ($offset>0){
- if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
- else $rs = $this->Execute($sql,$inputarr);
- } else {
- if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
- else $rs = $this->Execute($sql,$inputarr);
- }
+
+ if ($secs2cache != 0) $rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
+ else $rs = $this->Execute($sql,$inputarr);
+
$ADODB_COUNTRECS = $savec;
if ($rs && !$rs->EOF) {
$rs = $this->_rs2rs($rs,$nrows,$offset);
*/
function GetOne($sql,$inputarr=false)
{
- global $ADODB_COUNTRECS;
+ global $ADODB_COUNTRECS,$ADODB_GETONE_EOF;
$crecs = $ADODB_COUNTRECS;
$ADODB_COUNTRECS = false;
$ret = false;
$rs = $this->Execute($sql,$inputarr);
if ($rs) {
- if ($rs->EOF) $ret = null;
+ if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
else $ret = reset($rs->fields);
$rs->Close();
return $ret;
}
+ // $where should include 'WHERE fld=value'
+ function GetMedian($table, $field,$where = '')
+ {
+ $total = $this->GetOne("select count(*) from $table $where");
+ if (!$total) return false;
+
+ $midrow = (integer) ($total/2);
+ $rs = $this->SelectLimit("select $field from $table $where order by 1",1,$midrow);
+ if ($rs && !$rs->EOF) return reset($rs->fields);
+ return false;
+ }
+
+
function CacheGetOne($secs2cache,$sql=false,$inputarr=false)
{
+ global $ADODB_GETONE_EOF;
$ret = false;
$rs = $this->CacheExecute($secs2cache,$sql,$inputarr);
if ($rs) {
- if ($rs->EOF) $ret = null;
+ if ($rs->EOF) $ret = $ADODB_GETONE_EOF;
else $ret = reset($rs->fields);
$rs->Close();
}
function GetRandRow($sql, $arr= false)
{
$rezarr = $this->GetAll($sql, $arr);
- $sz = sizeof($rez);
+ $sz = sizeof($rezarr);
return $rezarr[abs(rand()) % $sz];
}
*/
function CacheFlush($sql=false,$inputarr=false)
{
- global $ADODB_CACHE_DIR;
-
- if ($this->memCache) {
- global $ADODB_INCLUDED_MEMCACHE;
+ global $ADODB_CACHE_DIR, $ADODB_CACHE;
- $key = false;
- if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
- if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true);
- FlushMemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug);
- return;
- }
-
- if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
- /*if (strncmp(PHP_OS,'WIN',3) === 0)
- $dir = str_replace('/', '\\', $ADODB_CACHE_DIR);
- else */
- $dir = $ADODB_CACHE_DIR;
-
- if ($this->debug) {
- ADOConnection::outp( "CacheFlush: $dir<br><pre>\n". $this->_dirFlush($dir)."</pre>");
- } else {
- $this->_dirFlush($dir);
- }
- return;
- }
-
- global $ADODB_INCLUDED_CSV;
- if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
-
- $f = $this->_gencachename($sql.serialize($inputarr),false);
- adodb_write_file($f,''); // is adodb_write_file needed?
- if (!@unlink($f)) {
- if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
- }
- }
-
- /**
- * Private function to erase all of the files and subdirectories in a directory.
- *
- * Just specify the directory, and tell it if you want to delete the directory or just clear it out.
- * Note: $kill_top_level is used internally in the function to flush subdirectories.
- *
- */
- function _dirFlush($dir, $kill_top_level = false)
- {
- if(!$dh = @opendir($dir)) return;
-
- while (($obj = readdir($dh))) {
- if($obj=='.' || $obj=='..') continue;
- $f = $dir.'/'.$obj;
-
- if (strpos($obj,'.cache')) @unlink($f);
- if (is_dir($f)) $this->_dirFlush($f, true);
- }
- if ($kill_top_level === true) @rmdir($dir);
- return true;
- }
-
- // this only deletes .cache files
- function xCacheFlush($sql=false,$inputarr=false)
- {
- global $ADODB_CACHE_DIR;
-
- if ($this->memCache) {
- global $ADODB_INCLUDED_MEMCACHE;
- $key = false;
- if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
- if ($sql) $key = $this->_gencachename($sql.serialize($inputarr),false,true);
- flushmemCache($key, $this->memCacheHost, $this->memCachePort, $this->debug);
- return;
- }
-
- if (strlen($ADODB_CACHE_DIR) > 1 && !$sql) {
- if (strncmp(PHP_OS,'WIN',3) === 0) {
- $cmd = 'del /s '.str_replace('/','\\',$ADODB_CACHE_DIR).'\adodb_*.cache';
- } else {
- //$cmd = 'find "'.$ADODB_CACHE_DIR.'" -type f -maxdepth 1 -print0 | xargs -0 rm -f';
- $cmd = 'rm -rf '.$ADODB_CACHE_DIR.'/[0-9a-f][0-9a-f]/';
- // old version 'rm -f `find '.$ADODB_CACHE_DIR.' -name adodb_*.cache`';
- }
- if ($this->debug) {
- ADOConnection::outp( "CacheFlush: $cmd<br><pre>\n", system($cmd),"</pre>");
- } else {
- exec($cmd);
- }
- return;
- }
-
- global $ADODB_INCLUDED_CSV;
- if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
+ if (!$sql) {
+ $ADODB_CACHE->flushall($this->debug);
+ return;
+ }
$f = $this->_gencachename($sql.serialize($inputarr),false);
- adodb_write_file($f,''); // is adodb_write_file needed?
- if (!@unlink($f)) {
- if ($this->debug) ADOConnection::outp( "CacheFlush: failed for $f");
- }
+ return $ADODB_CACHE->flushcache($f, $this->debug);
}
+
/**
* Private function to generate filename for caching.
* Assuming that we can have 50,000 files per directory with good performance,
* then we can scale to 12.8 million unique cached recordsets. Wow!
*/
- function _gencachename($sql,$createdir,$memcache=false)
+ function _gencachename($sql,$createdir)
{
- global $ADODB_CACHE_DIR;
- static $notSafeMode;
+ global $ADODB_CACHE, $ADODB_CACHE_DIR;
if ($this->fetchMode === false) {
global $ADODB_FETCH_MODE;
$mode = $this->fetchMode;
}
$m = md5($sql.$this->databaseType.$this->database.$this->user.$mode);
- if ($memcache) return $m;
+ if (!$ADODB_CACHE->createdir) return $m;
+ if (!$createdir) $dir = $ADODB_CACHE->getdirname($m);
+ else $dir = $ADODB_CACHE->createdir($m, $this->debug);
- if (!isset($notSafeMode)) $notSafeMode = !ini_get('safe_mode');
- $dir = ($notSafeMode) ? $ADODB_CACHE_DIR.'/'.substr($m,0,2) : $ADODB_CACHE_DIR;
-
- if ($createdir && $notSafeMode && !file_exists($dir)) {
- $oldu = umask(0);
- if (!@mkdir($dir,0771))
- if(!is_dir($dir) && $this->debug) ADOConnection::outp( "Unable to mkdir $dir for $sql");
- umask($oldu);
- }
return $dir.'/adodb_'.$m.'.cache';
}
*/
function CacheExecute($secs2cache,$sql=false,$inputarr=false)
{
+ global $ADODB_CACHE;
+
if (!is_numeric($secs2cache)) {
$inputarr = $sql;
$sql = $secs2cache;
} else
$sqlparam = $sql;
- if ($this->memCache) {
- global $ADODB_INCLUDED_MEMCACHE;
- if (empty($ADODB_INCLUDED_MEMCACHE)) include(ADODB_DIR.'/adodb-memcache.lib.inc.php');
- $md5file = $this->_gencachename($sql.serialize($inputarr),false,true);
- } else {
- global $ADODB_INCLUDED_CSV;
- if (empty($ADODB_INCLUDED_CSV)) include(ADODB_DIR.'/adodb-csvlib.inc.php');
- $md5file = $this->_gencachename($sql.serialize($inputarr),true);
- }
-
+
+ $md5file = $this->_gencachename($sql.serialize($inputarr),true);
$err = '';
if ($secs2cache > 0){
- if ($this->memCache)
- $rs = getmemCache($md5file,$err,$secs2cache, $this->memCacheHost, $this->memCachePort);
- else
- $rs = csv2rs($md5file,$err,$secs2cache,$this->arrayClass);
+ $rs = $ADODB_CACHE->readcache($md5file,$err,$secs2cache,$this->arrayClass);
$this->numCacheHits += 1;
} else {
$err='Timeout 1';
$rs = false;
$this->numCacheMisses += 1;
}
-
+
if (!$rs) {
// no cached rs found
if ($this->debug) {
$rs = $this->Execute($sqlparam,$inputarr);
- if ($rs && $this->memCache) {
- $rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
- if(!putmemCache($md5file, $rs, $this->memCacheHost, $this->memCachePort, $this->memCacheCompress, $this->debug)) {
- if ($fn = $this->raiseErrorFn)
- $fn($this->databaseType,'CacheExecute',-32000,"Cache write error",$md5file,$sql,$this);
- if ($this->debug) ADOConnection::outp( " Cache write error");
- }
- } else if ($rs) {
+ if ($rs) {
+
$eof = $rs->EOF;
$rs = $this->_rs2rs($rs); // read entire recordset into memory immediately
+ $rs->timeCreated = time(); // used by caching
$txt = _rs2serialize($rs,false,$sql); // serialize
- $ok = adodb_write_file($md5file,$txt,$this->debug);
+ $ok = $ADODB_CACHE->writecache($md5file,$txt,$this->debug, $secs2cache);
if (!$ok) {
if ($ok === false) {
$em = 'Cache write error';
$rs->connection = $this; // Pablo suggestion
}
- } else
- if (!$this->memCache)
- @unlink($md5file);
+ } else if (!$this->memCache)
+ $ADODB_CACHE->flushcache($md5file);
} else {
$this->_errorMsg = '';
$this->_errorCode = 0;
}
}
- function GetActiveRecordsClass($class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false)
+ /**
+ * GetActiveRecordsClass Performs an 'ALL' query
+ *
+ * @param mixed $class This string represents the class of the current active record
+ * @param mixed $table Table used by the active record object
+ * @param mixed $whereOrderBy Where, order, by clauses
+ * @param mixed $bindarr
+ * @param mixed $primkeyArr
+ * @param array $extra Query extras: limit, offset...
+ * @param mixed $relations Associative array: table's foreign name, "hasMany", "belongsTo"
+ * @access public
+ * @return void
+ */
+ function GetActiveRecordsClass(
+ $class, $table,$whereOrderBy=false,$bindarr=false, $primkeyArr=false,
+ $extra=array(),
+ $relations=array())
{
global $_ADODB_ACTIVE_DBS;
-
- $save = $this->SetFetchMode(ADODB_FETCH_NUM);
- if (empty($whereOrderBy)) $whereOrderBy = '1=1';
- $rows = $this->GetAll("select * from ".$table.' WHERE '.$whereOrderBy,$bindarr);
- $this->SetFetchMode($save);
-
- $false = false;
-
- if ($rows === false) {
- return $false;
- }
-
-
- if (!isset($_ADODB_ACTIVE_DBS)) {
- include(ADODB_DIR.'/adodb-active-record.inc.php');
- }
- if (!class_exists($class)) {
- $this->outp_throw("Unknown class $class in GetActiveRecordsClass()",'GetActiveRecordsClass');
- return $false;
- }
- $arr = array();
- foreach($rows as $row) {
-
- $obj = new $class($table,$primkeyArr,$this);
- if ($obj->ErrorMsg()){
- $this->_errorMsg = $obj->ErrorMsg();
- return $false;
- }
- $obj->Set($row);
- $arr[] = $obj;
- }
- return $arr;
+ ## reduce overhead of adodb.inc.php -- moved to adodb-active-record.inc.php
+ ## if adodb-active-recordx is loaded -- should be no issue as they will probably use Find()
+ if (!isset($_ADODB_ACTIVE_DBS))include_once(ADODB_DIR.'/adodb-active-record.inc.php');
+ return adodb_GetActiveRecordsClass($this, $class, $table, $whereOrderBy, $bindarr, $primkeyArr, $extra, $relations);
}
function GetActiveRecords($table,$where=false,$bindarr=false,$primkeyArr=false)
*
* @return date string in database date format
*/
- function DBDate($d)
+ function DBDate($d, $isfld=false)
{
if (empty($d) && $d !== 0) return 'null';
-
+ if ($isfld) return $d;
+
if (is_string($d) && !is_numeric($d)) {
if ($d === 'null' || strncmp($d,"'",1) === 0) return $d;
if ($this->isoDates) return "'$d'";
*
* @return timestamp string in database timestamp format
*/
- function DBTimeStamp($ts)
+ function DBTimeStamp($ts,$isfld=false)
{
if (empty($ts) && $ts !== 0) return 'null';
-
+ if ($isfld) return $ts;
+
# strlen(14) allows YYYYMMDDHHMMSS format
if (!is_string($ts) || (is_numeric($ts) && strlen($ts)<14))
return adodb_date($this->fmtTimeStamp,$ts);
function key()
{
- return $false;
+ return false;
}
function current()
{
- return $false;
+ return false;
}
function next()
'CHARACTER' => 'C',
'INTERVAL' => 'C', # Postgres
'MACADDR' => 'C', # postgres
+ 'VAR_STRING' => 'C', # mysql
##
'LONGCHAR' => 'X',
'TEXT' => 'X',
##
'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
##
+ 'SMALLDATETIME' => 'T',
'TIME' => 'T',
'TIMESTAMP' => 'T',
'DATETIME' => 'T',
if (strncmp($origdsn,'pdo',3) == 0) {
$sch = explode('_',$dsna['scheme']);
if (sizeof($sch)>1) {
+
$dsna['host'] = isset($dsna['host']) ? rawurldecode($dsna['host']) : '';
- $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
+ if ($sch[1] == 'sqlite')
+ $dsna['host'] = rawurlencode($sch[1].':'.rawurldecode($dsna['host']));
+ else
+ $dsna['host'] = rawurlencode($sch[1].':host='.rawurldecode($dsna['host']));
$dsna['scheme'] = 'pdo';
}
}
if (!empty($ADODB_NEWCONNECTION)) {
$obj = $ADODB_NEWCONNECTION($db);
- } else {
+ }
+
+ if(empty($obj)) {
if (!isset($ADODB_LASTDB)) $ADODB_LASTDB = '';
if (empty($db)) $db = $ADODB_LASTDB;
@param printOrArr Pass in a boolean to indicate print, or an $exception->trace array (assumes that print is true then).
@param levels Number of levels to display
*/
- function adodb_backtrace($printOrArr=true,$levels=9999)
+ function adodb_backtrace($printOrArr=true,$levels=9999,$ishtml=null)
{
global $ADODB_INCLUDED_LIB;
if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
- return _adodb_backtrace($printOrArr,$levels);
+ return _adodb_backtrace($printOrArr,$levels,0,$ishtml);
}
}
-?>
+?>
\ No newline at end of file
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
// return string must begin with space
- function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint)
+ function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
if ($fautoinc) {
$ftype = 'COUNTER';
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
// return string must begin with space
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
if ($fautoinc) return ' GENERATED ALWAYS AS IDENTITY'; # as identity start with
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
// return string must begin with space
- function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint)
+ function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
if ($fautoinc) {
$ftype = 'SERIAL';
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
case 'TINYINT': return 'I1';
case 'SMALLINT': return 'I2';
case 'BIGINT': return 'I8';
-
+ case 'SMALLDATETIME': return 'T';
case 'REAL':
case 'FLOAT': return 'F';
default: return parent::MetaType($t,$len,$fieldobj);
}
// return string must begin with space
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
--- /dev/null
+<?php
+
+/**
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
+
+*/
+
+/*
+In ADOdb, named quotes for MS SQL Server use ". From the MSSQL Docs:
+
+ Note Delimiters are for identifiers only. Delimiters cannot be used for keywords,
+ whether or not they are marked as reserved in SQL Server.
+
+ Quoted identifiers are delimited by double quotation marks ("):
+ SELECT * FROM "Blanks in Table Name"
+
+ Bracketed identifiers are delimited by brackets ([ ]):
+ SELECT * FROM [Blanks In Table Name]
+
+ Quoted identifiers are valid only when the QUOTED_IDENTIFIER option is set to ON. By default,
+ the Microsoft OLE DB Provider for SQL Server and SQL Server ODBC driver set QUOTED_IDENTIFIER ON
+ when they connect.
+
+ In Transact-SQL, the option can be set at various levels using SET QUOTED_IDENTIFIER,
+ the quoted identifier option of sp_dboption, or the user options option of sp_configure.
+
+ When SET ANSI_DEFAULTS is ON, SET QUOTED_IDENTIFIER is enabled.
+
+ Syntax
+
+ SET QUOTED_IDENTIFIER { ON | OFF }
+
+
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+class ADODB2_mssqlnative extends ADODB_DataDict {
+ var $databaseType = 'mssqlnative';
+ var $dropIndex = 'DROP INDEX %2$s.%1$s';
+ var $renameTable = "EXEC sp_rename '%s','%s'";
+ var $renameColumn = "EXEC sp_rename '%s.%s','%s'";
+
+ var $typeX = 'TEXT'; ## Alternatively, set it to VARCHAR(4000)
+ var $typeXL = 'TEXT';
+
+ //var $alterCol = ' ALTER COLUMN ';
+
+ function MetaType($t,$len=-1,$fieldobj=false)
+ {
+ if (is_object($t)) {
+ $fieldobj = $t;
+ $t = $fieldobj->type;
+ $len = $fieldobj->max_length;
+ }
+
+ $len = -1; // mysql max_length is not accurate
+ switch (strtoupper($t)) {
+ case 'R':
+ case 'INT':
+ case 'INTEGER': return 'I';
+ case 'BIT':
+ case 'TINYINT': return 'I1';
+ case 'SMALLINT': return 'I2';
+ case 'BIGINT': return 'I8';
+
+ case 'REAL':
+ case 'FLOAT': return 'F';
+ default: return parent::MetaType($t,$len,$fieldobj);
+ }
+ }
+
+ function ActualType($meta)
+ {
+ switch(strtoupper($meta)) {
+
+ case 'C': return 'VARCHAR';
+ case 'XL': return (isset($this)) ? $this->typeXL : 'TEXT';
+ case 'X': return (isset($this)) ? $this->typeX : 'TEXT'; ## could be varchar(8000), but we want compat with oracle
+ case 'C2': return 'NVARCHAR';
+ case 'X2': return 'NTEXT';
+
+ case 'B': return 'IMAGE';
+
+ case 'D': return 'DATETIME';
+ case 'T': return 'DATETIME';
+ case 'L': return 'BIT';
+
+ case 'R':
+ case 'I': return 'INT';
+ case 'I1': return 'TINYINT';
+ case 'I2': return 'SMALLINT';
+ case 'I4': return 'INT';
+ case 'I8': return 'BIGINT';
+
+ case 'F': return 'REAL';
+ case 'N': return 'NUMERIC';
+ default:
+ return $meta;
+ }
+ }
+
+
+ function AddColumnSQL($tabname, $flds)
+ {
+ $tabname = $this->TableName ($tabname);
+ $f = array();
+ list($lines,$pkey) = $this->_GenFields($flds);
+ $s = "ALTER TABLE $tabname $this->addCol";
+ foreach($lines as $v) {
+ $f[] = "\n $v";
+ }
+ $s .= implode(', ',$f);
+ $sql[] = $s;
+ return $sql;
+ }
+
+ /*
+ function AlterColumnSQL($tabname, $flds)
+ {
+ $tabname = $this->TableName ($tabname);
+ $sql = array();
+ list($lines,$pkey) = $this->_GenFields($flds);
+ foreach($lines as $v) {
+ $sql[] = "ALTER TABLE $tabname $this->alterCol $v";
+ }
+
+ return $sql;
+ }
+ */
+
+ function DropColumnSQL($tabname, $flds)
+ {
+ $tabname = $this->TableName ($tabname);
+ if (!is_array($flds))
+ $flds = explode(',',$flds);
+ $f = array();
+ $s = 'ALTER TABLE ' . $tabname;
+ foreach($flds as $v) {
+ $f[] = "\n$this->dropCol ".$this->NameQuote($v);
+ }
+ $s .= implode(', ',$f);
+ $sql[] = $s;
+ return $sql;
+ }
+
+ // return string must begin with space
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+ {
+ $suffix = '';
+ if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
+ if ($fautoinc) $suffix .= ' IDENTITY(1,1)';
+ if ($fnotnull) $suffix .= ' NOT NULL';
+ else if ($suffix == '') $suffix .= ' NULL';
+ if ($fconstraint) $suffix .= ' '.$fconstraint;
+ return $suffix;
+ }
+
+ /*
+CREATE TABLE
+ [ database_name.[ owner ] . | owner. ] table_name
+ ( { < column_definition >
+ | column_name AS computed_column_expression
+ | < table_constraint > ::= [ CONSTRAINT constraint_name ] }
+
+ | [ { PRIMARY KEY | UNIQUE } [ ,...n ]
+ )
+
+[ ON { filegroup | DEFAULT } ]
+[ TEXTIMAGE_ON { filegroup | DEFAULT } ]
+
+< column_definition > ::= { column_name data_type }
+ [ COLLATE < collation_name > ]
+ [ [ DEFAULT constant_expression ]
+ | [ IDENTITY [ ( seed , increment ) [ NOT FOR REPLICATION ] ] ]
+ ]
+ [ ROWGUIDCOL]
+ [ < column_constraint > ] [ ...n ]
+
+< column_constraint > ::= [ CONSTRAINT constraint_name ]
+ { [ NULL | NOT NULL ]
+ | [ { PRIMARY KEY | UNIQUE }
+ [ CLUSTERED | NONCLUSTERED ]
+ [ WITH FILLFACTOR = fillfactor ]
+ [ON {filegroup | DEFAULT} ] ]
+ ]
+ | [ [ FOREIGN KEY ]
+ REFERENCES ref_table [ ( ref_column ) ]
+ [ ON DELETE { CASCADE | NO ACTION } ]
+ [ ON UPDATE { CASCADE | NO ACTION } ]
+ [ NOT FOR REPLICATION ]
+ ]
+ | CHECK [ NOT FOR REPLICATION ]
+ ( logical_expression )
+ }
+
+< table_constraint > ::= [ CONSTRAINT constraint_name ]
+ { [ { PRIMARY KEY | UNIQUE }
+ [ CLUSTERED | NONCLUSTERED ]
+ { ( column [ ASC | DESC ] [ ,...n ] ) }
+ [ WITH FILLFACTOR = fillfactor ]
+ [ ON { filegroup | DEFAULT } ]
+ ]
+ | FOREIGN KEY
+ [ ( column [ ,...n ] ) ]
+ REFERENCES ref_table [ ( ref_column [ ,...n ] ) ]
+ [ ON DELETE { CASCADE | NO ACTION } ]
+ [ ON UPDATE { CASCADE | NO ACTION } ]
+ [ NOT FOR REPLICATION ]
+ | CHECK [ NOT FOR REPLICATION ]
+ ( search_conditions )
+ }
+
+
+ */
+
+ /*
+ CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name
+ ON { table | view } ( column [ ASC | DESC ] [ ,...n ] )
+ [ WITH < index_option > [ ,...n] ]
+ [ ON filegroup ]
+ < index_option > :: =
+ { PAD_INDEX |
+ FILLFACTOR = fillfactor |
+ IGNORE_DUP_KEY |
+ DROP_EXISTING |
+ STATISTICS_NORECOMPUTE |
+ SORT_IN_TEMPDB
+ }
+*/
+ 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' : '';
+ $clustered = isset($idxoptions['CLUSTERED']) ? ' CLUSTERED' : '';
+
+ if ( is_array($flds) )
+ $flds = implode(', ',$flds);
+ $s = 'CREATE' . $unique . $clustered . ' INDEX ' . $idxname . ' ON ' . $tabname . ' (' . $flds . ')';
+
+ if ( isset($idxoptions[$this->upperName]) )
+ $s .= $idxoptions[$this->upperName];
+
+
+ $sql[] = $s;
+
+ return $sql;
+ }
+
+
+ function _GetSize($ftype, $ty, $fsize, $fprec)
+ {
+ switch ($ftype) {
+ case 'INT':
+ case 'SMALLINT':
+ case 'TINYINT':
+ case 'BIGINT':
+ return $ftype;
+ }
+ if ($ty == 'T') return $ftype;
+ return parent::_GetSize($ftype, $ty, $fsize, $fprec);
+
+ }
+}
+?>
\ No newline at end of file
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
// return string must begin with space
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
if ($funsigned) $suffix .= ' UNSIGNED';
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
case 'D':
case 'T': return 'DATE';
- case 'L': return 'DECIMAL(1)';
- case 'I1': return 'DECIMAL(3)';
- case 'I2': return 'DECIMAL(5)';
+ case 'L': return 'NUMBER(1)';
+ case 'I1': return 'NUMBER(3)';
+ case 'I2': return 'NUMBER(5)';
case 'I':
- case 'I4': return 'DECIMAL(10)';
+ case 'I4': return 'NUMBER(10)';
- case 'I8': return 'DECIMAL(20)';
- case 'F': return 'DECIMAL';
- case 'N': return 'DECIMAL';
+ case 'I8': return 'NUMBER(20)';
+ case 'F': return 'NUMBER';
+ case 'N': return 'NUMBER';
+ case 'R': return 'NUMBER(20)';
default:
return $meta;
}
}
// return string must begin with space
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
// return string must begin with space
- function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint)
+ function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
if ($fautoinc) {
$ftype = 'SERIAL';
<?php
/**
- V4.50 6 July 2004 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V4.50 6 July 2004 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
// return string must begin with space
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
if ($funsigned) $suffix .= ' UNSIGNED';
<?php
/**
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
// return string must begin with space
- function _CreateSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint)
+ function _CreateSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
{
$suffix = '';
if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
$oCmd->CommandText = $sql;
$oCmd->CommandType = 1;
- foreach($inputarr as $val) {
+ // Map by http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ado270/htm/mdmthcreateparam.asp
+ // Check issue http://bugs.php.net/bug.php?id=40664 !!!
+ while(list(, $val) = each($inputarr)) {
+ $type = gettype($val);
+ $len=strlen($val);
+ if ($type == 'boolean')
+ $this->adoParameterType = 11;
+ else if ($type == 'integer')
+ $this->adoParameterType = 3;
+ else if ($type == 'double')
+ $this->adoParameterType = 5;
+ elseif ($type == 'string')
+ $this->adoParameterType = 202;
+ else if (($val === null) || (!defined($val)))
+ $len=1;
+ else
+ $this->adoParameterType = 130;
+
// name, type, direction 1 = input, len,
- $this->adoParameterType = 130;
- $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val);
- //print $p->Type.' '.$p->value;
+ $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,$len,$val);
+
$oCmd->Parameters->Append($p);
}
$p = false;
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
} else {
$argDatabasename = '';
if ($argDBorProvider) $argProvider = $argDBorProvider;
- else $argProvider = 'MSDASQL';
+ else if (stripos($argHostname,'PROVIDER') === false) /* full conn string is not in $argHostname */
+ $argProvider = 'MSDASQL';
}
$dbc->CursorLocation = $this->_cursor_location;
return $dbc->State > 0;
} catch (exception $e) {
+ if ($this->debug);echo "<pre>",$argHostname,"\n",$e,"</pre>\n";
}
return false;
$oCmd->CommandText = $sql;
$oCmd->CommandType = 1;
- foreach($inputarr as $val) {
+ while(list(, $val) = each($inputarr)) {
+ $type = gettype($val);
+ $len=strlen($val);
+ if ($type == 'boolean')
+ $this->adoParameterType = 11;
+ else if ($type == 'integer')
+ $this->adoParameterType = 3;
+ else if ($type == 'double')
+ $this->adoParameterType = 5;
+ elseif ($type == 'string')
+ $this->adoParameterType = 202;
+ else if (($val === null) || (!defined($val)))
+ $len=1;
+ else
+ $this->adoParameterType = 130;
+
// name, type, direction 1 = input, len,
- $this->adoParameterType = 130;
- $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,strlen($val),$val);
- //print $p->Type.' '.$p->value;
+ $p = $oCmd->CreateParameter('name',$this->adoParameterType,1,$len,$val);
+
$oCmd->Parameters->Append($p);
}
+
$p = false;
$rs = $oCmd->Execute();
$e = $dbc->Errors;
$o= new ADOFieldObject();
$rs = $this->_queryID;
+ if (!$rs) return false;
+
$f = $rs->Fields($fieldOffset);
$o->name = $f->Name;
$t = $f->Type;
function _initrs()
{
$rs = $this->_queryID;
- $this->_numOfRows = $rs->RecordCount;
+ try {
+ $this->_numOfRows = $rs->RecordCount;
+ } catch (Exception $e) {
+ $this->_numOfRows = -1;
+ }
$f = $rs->Fields;
$this->_numOfFields = $f->Count;
}
function _close() {
$this->_flds = false;
+ try {
@$this->_queryID->Close();// by Pete Dishman (peterd@telephonetics.co.uk)
+ } catch (Exception $e) {
+ }
$this->_queryID = false;
}
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
- V5.04a 25 Mar 2008 (c) 2006 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.06 16 Oct 2008 (c) 2006 John Lim (jlim#natsoft.com). All rights reserved.
This is a version of the ADODB driver for DB2. It uses the 'ibm_db2' PECL extension
for PHP (http://pecl.php.net/package/ibm_db2), which in turn requires DB2 V8.2.2 or
var $sysDate = 'CURRENT DATE';
var $sysTimeStamp = 'CURRENT TIMESTAMP';
- var $fmtTimeStamp = "'Y-m-d-H:i:s'";
+ var $fmtTimeStamp = "'Y-m-d H:i:s'";
var $replaceQuote = "''"; // string to use to replace quotes
var $dataProvider = "db2";
var $hasAffectedRows = true;
// breaking backward-compat
var $_bindInputArray = false;
var $_genIDSQL = "VALUES NEXTVAL FOR %s";
- var $_genSeqSQL = "CREATE SEQUENCE %s START WITH 1 NO MAXVALUE NO CYCLE";
+ var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s NO MAXVALUE NO CYCLE";
var $_dropSeqSQL = "DROP SEQUENCE %s";
var $_autocommit = true;
var $_haserrorfunctions = true;
global $php_errormsg;
if (!function_exists('db2_connect')) {
- ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension.");
+ ADOConnection::outp("Warning: The old ODBC based DB2 driver has been renamed 'odbc_db2'. This ADOdb driver calls PHP's native db2 extension which is not installed.");
return null;
}
// This needs to be set before the connect().
function CreateSequence($seqname='adodbseq',$start=1)
{
if (empty($this->_genSeqSQL)) return false;
- $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
+ $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname,$start));
if (!$ok) return false;
return true;
}
{
// if you have to modify the parameter below, your database is overloaded,
// or you need to implement generation of id's yourself!
- $num = $this->GetOne("VALUES NEXTVAL FOR $seq");
+ $num = $this->GetOne("VALUES NEXTVAL FOR $seq");
return $num;
- }
+ }
function ErrorMsg()
<?php
/*
- @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ @version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
// returns queryID or false
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
return fbsql_query("$sql;",$this->_connectionID);
}
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/**
-* @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+* @version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim. All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim. 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.
}
*/
// returns query ID if successful, otherwise false
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
global $ADODB_COUNTRECS;
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
if ( !function_exists( 'ldap_connect' ) ) return null;
- $conn_info = array( $host,$this->port);
+ if (strpos('ldap://',$host) === 0 || strpos('ldaps://',$host) === 0) {
+ $this->_connectionID = @ldap_connect($host);
+ } else {
+ $conn_info = array( $host,$this->port);
- if ( strstr( $host, ':' ) ) {
- $conn_info = split( ':', $host );
- }
+ if ( strstr( $host, ':' ) ) {
+ $conn_info = split( ':', $host );
+ }
- $this->_connectionID = @ldap_connect( $conn_info[0], $conn_info[1] );
+ $this->_connectionID = @ldap_connect( $conn_info[0], $conn_info[1] );
+ }
if (!$this->_connectionID) {
$e = 'Could not connect to ' . $conn_info[0];
$this->_errorMsg = $e;
}
/* returns _queryID or false */
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
$rs = @ldap_search( $this->_connectionID, $this->database, $sql );
$this->_errorMsg = ($rs) ? '' : 'Search error on '.$sql.': '.ldap_error($this->_connectionID);
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
var $uniqueOrderBy = true;
var $_bindInputArray = true;
+ var $forceNewConnect = false;
function ADODB_mssql()
{
return $this->lastInsID; // InsID from sp_executesql call
} else {
return $this->GetOne($this->identitySQL);
- }
+ }
}
function _affectedrows()
if ($nrows > 0 && $offset <= 0) {
$sql = preg_replace(
'/(^\s*select\s+(distinctrow|distinct)?)/i','\\1 '.$this->hasTop." $nrows ",$sql);
- $rs = $this->Execute($sql,$inputarr);
+
+ if ($secs2cache)
+ $rs = $this->CacheExecute($secs2cache, $sql, $inputarr);
+ else
+ $rs = $this->Execute($sql,$inputarr);
} else
$rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
{
if ($this->transOff) return true;
$this->transCnt += 1;
- $this->Execute('BEGIN TRAN');
- return true;
+ $ok = $this->Execute('BEGIN TRAN');
+ return $ok;
}
function CommitTrans($ok=true)
if ($this->transOff) return true;
if (!$ok) return $this->RollbackTrans();
if ($this->transCnt) $this->transCnt -= 1;
- $this->Execute('COMMIT TRAN');
- return true;
+ $ok = $this->Execute('COMMIT TRAN');
+ return $ok;
}
function RollbackTrans()
{
if ($this->transOff) return true;
if ($this->transCnt) $this->transCnt -= 1;
- $this->Execute('ROLLBACK TRAN');
- return true;
+ $ok = $this->Execute('ROLLBACK TRAN');
+ return $ok;
}
function SetTransactionMode( $transaction_mode )
$indexes = array();
while ($row = $rs->FetchRow()) {
- if (!$primary && $row[5]) continue;
+ if ($primary && !$row[5]) continue;
$indexes[$row[0]]['unique'] = $row[6];
$indexes[$row[0]]['columns'][] = $row[1];
else return -1;
}
- // returns true or false
- function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
+ // returns true or false, newconnect supported since php 5.1.0.
+ function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$newconnect=false)
{
if (!function_exists('mssql_pconnect')) return null;
- $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword);
+ $this->_connectionID = mssql_connect($argHostname,$argUsername,$argPassword,$newconnect);
if ($this->_connectionID === false) return false;
if ($argDatabasename) return $this->SelectDB($argDatabasename);
return true;
return true;
}
+ function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+ {
+ return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
+ }
+
function Prepare($sql)
{
$sqlarr = explode('?',$sql);
}
// returns query ID if successful, otherwise false
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
$this->_errorMsg = false;
if (is_array($inputarr)) {
http://www.databasejournal.com/scripts/article.php/1440551
*/
-?>
+?>
\ No newline at end of file
// ADOdb - Database Abstraction Library for PHP //
// http://adodb.sourceforge.net/ //
// //
-// Copyright (C) 2000-2008 John Lim (jlim\@natsoft.com.my) //
+// Copyright (C) 2000-2009 John Lim (jlim\@natsoft.com.my) //
// All rights reserved. //
// Released under both BSD license and LGPL library license. //
// Whenever there is any discrepancy between the two licenses, //
ADODB_mssql::ADODB_mssql();
}
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
$sql = $this->_appendN($sql);
return ADODB_mssql::_query($sql,$inputarr);
--- /dev/null
+<?php
+/*
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
+
+ Latest version is available at http://adodb.sourceforge.net
+
+ Native mssql driver. Requires mssql client. Works on Windows.
+ http://www.microsoft.com/sql/technologies/php/default.mspx
+ To configure for Unix, see
+ http://phpbuilder.com/columns/alberto20000919.php3
+
+ $stream = sqlsrv_get_field($stmt, $index, SQLSRV_SQLTYPE_STREAM(SQLSRV_ENC_BINARY));
+ stream_filter_append($stream, "convert.iconv.ucs-2/utf-8"); // Voila, UTF-8 can be read directly from $stream
+
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+if (!function_exists('sqlsrv_configure')) {
+ die("mssqlnative extension not installed");
+}
+
+if (!function_exists('sqlsrv_set_error_handling')) {
+ function sqlsrv_set_error_handling($constant) {
+ sqlsrv_configure("WarningsReturnAsErrors", $constant);
+ }
+}
+if (!function_exists('sqlsrv_log_set_severity')) {
+ function sqlsrv_log_set_severity($constant) {
+ sqlsrv_configure("LogSeverity", $constant);
+ }
+}
+if (!function_exists('sqlsrv_log_set_subsystems')) {
+ function sqlsrv_log_set_subsystems($constant) {
+ sqlsrv_configure("LogSubsystems", $constant);
+ }
+}
+
+
+//----------------------------------------------------------------
+// MSSQL returns dates with the format Oct 13 2002 or 13 Oct 2002
+// and this causes tons of problems because localized versions of
+// MSSQL will return the dates in dmy or mdy order; and also the
+// month strings depends on what language has been configured. The
+// following two variables allow you to control the localization
+// settings - Ugh.
+//
+// MORE LOCALIZATION INFO
+// ----------------------
+// To configure datetime, look for and modify sqlcommn.loc,
+// typically found in c:\mssql\install
+// Also read :
+// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q220918
+// Alternatively use:
+// CONVERT(char(12),datecol,120)
+//
+// Also if your month is showing as month-1,
+// e.g. Jan 13, 2002 is showing as 13/0/2002, then see
+// http://phplens.com/lens/lensforum/msgs.php?id=7048&x=1
+// it's a localisation problem.
+//----------------------------------------------------------------
+
+
+// has datetime converstion to YYYY-MM-DD format, and also mssql_fetch_assoc
+if (ADODB_PHPVER >= 0x4300) {
+// docs say 4.2.0, but testing shows only since 4.3.0 does it work!
+ ini_set('mssql.datetimeconvert',0);
+} else {
+ global $ADODB_mssql_mths; // array, months must be upper-case
+ $ADODB_mssql_date_order = 'mdy';
+ $ADODB_mssql_mths = array(
+ 'JAN'=>1,'FEB'=>2,'MAR'=>3,'APR'=>4,'MAY'=>5,'JUN'=>6,
+ 'JUL'=>7,'AUG'=>8,'SEP'=>9,'OCT'=>10,'NOV'=>11,'DEC'=>12);
+}
+
+//---------------------------------------------------------------------------
+// Call this to autoset $ADODB_mssql_date_order at the beginning of your code,
+// just after you connect to the database. Supports mdy and dmy only.
+// Not required for PHP 4.2.0 and above.
+function AutoDetect_MSSQL_Date_Order($conn)
+{
+ global $ADODB_mssql_date_order;
+ $adate = $conn->GetOne('select getdate()');
+ if ($adate) {
+ $anum = (int) $adate;
+ if ($anum > 0) {
+ if ($anum > 31) {
+ //ADOConnection::outp( "MSSQL: YYYY-MM-DD date format not supported currently");
+ } else
+ $ADODB_mssql_date_order = 'dmy';
+ } else
+ $ADODB_mssql_date_order = 'mdy';
+ }
+}
+
+class ADODB_mssqlnative extends ADOConnection {
+ var $databaseType = "mssqlnative";
+ var $dataProvider = "mssqlnative";
+ var $replaceQuote = "''"; // string to use to replace quotes
+ var $fmtDate = "'Y-m-d'";
+ var $fmtTimeStamp = "'Y-m-d H:i:s'";
+ var $hasInsertID = true;
+ var $substr = "substring";
+ var $length = 'len';
+ var $hasAffectedRows = true;
+ var $poorAffectedRows = false;
+ var $metaDatabasesSQL = "select name from sys.sysdatabases where name <> 'master'";
+ var $metaTablesSQL="select name,case when type='U' then 'T' else 'V' end from sysobjects where (type='U' or type='V') and (name not in ('sysallocations','syscolumns','syscomments','sysdepends','sysfilegroups','sysfiles','sysfiles1','sysforeignkeys','sysfulltextcatalogs','sysindexes','sysindexkeys','sysmembers','sysobjects','syspermissions','sysprotects','sysreferences','systypes','sysusers','sysalternates','sysconstraints','syssegments','REFERENTIAL_CONSTRAINTS','CHECK_CONSTRAINTS','CONSTRAINT_TABLE_USAGE','CONSTRAINT_COLUMN_USAGE','VIEWS','VIEW_TABLE_USAGE','VIEW_COLUMN_USAGE','SCHEMATA','TABLES','TABLE_CONSTRAINTS','TABLE_PRIVILEGES','COLUMNS','COLUMN_DOMAIN_USAGE','COLUMN_PRIVILEGES','DOMAINS','DOMAIN_CONSTRAINTS','KEY_COLUMN_USAGE','dtproperties'))";
+ var $metaColumnsSQL = # xtype==61 is datetime
+ "select c.name,t.name,c.length,
+ (case when c.xusertype=61 then 0 else c.xprec end),
+ (case when c.xusertype=61 then 0 else c.xscale end)
+ from syscolumns c join systypes t on t.xusertype=c.xusertype join sysobjects o on o.id=c.id where o.name='%s'";
+ var $hasTop = 'top'; // support mssql SELECT TOP 10 * FROM TABLE
+ var $hasGenID = true;
+ var $sysDate = 'convert(datetime,convert(char,GetDate(),102),102)';
+ var $sysTimeStamp = 'GetDate()';
+ var $maxParameterLen = 4000;
+ var $arrayClass = 'ADORecordSet_array_mssqlnative';
+ var $uniqueSort = true;
+ var $leftOuter = '*=';
+ var $rightOuter = '=*';
+ var $ansiOuter = true; // for mssql7 or later
+ var $identitySQL = 'select SCOPE_IDENTITY()'; // 'select SCOPE_IDENTITY'; # for mssql 2000
+ var $uniqueOrderBy = true;
+ var $_bindInputArray = true;
+ var $_dropSeqSQL = "drop table %s";
+
+ function ADODB_mssqlnative()
+ {
+ if ($this->debug) {
+ error_log("<pre>");
+ sqlsrv_set_error_handling( SQLSRV_ERRORS_LOG_ALL );
+ sqlsrv_log_set_severity( SQLSRV_LOG_SEVERITY_ALL );
+ sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
+ sqlsrv_configure('warnings_return_as_errors', 0);
+ } else {
+ sqlsrv_set_error_handling(0);
+ sqlsrv_log_set_severity(0);
+ sqlsrv_log_set_subsystems(SQLSRV_LOG_SYSTEM_ALL);
+ sqlsrv_configure('warnings_return_as_errors', 0);
+ }
+ }
+
+ function ServerInfo()
+ {
+ global $ADODB_FETCH_MODE;
+ if ($this->fetchMode === false) {
+ $savem = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ } else
+ $savem = $this->SetFetchMode(ADODB_FETCH_NUM);
+ $arrServerInfo = sqlsrv_server_info($this->_connectionID);
+ $arr['description'] = $arrServerInfo['SQLServerName'].' connected to '.$arrServerInfo['CurrentDatabase'];
+ $arr['version'] = $arrServerInfo['SQLServerVersion'];//ADOConnection::_findvers($arr['description']);
+ return $arr;
+ }
+
+ function IfNull( $field, $ifNull )
+ {
+ return " ISNULL($field, $ifNull) "; // if MS SQL Server
+ }
+
+ function _insertid()
+ {
+ // SCOPE_IDENTITY()
+ // Returns the last IDENTITY value inserted into an IDENTITY column in
+ // the same scope. A scope is a module -- a stored procedure, trigger,
+ // function, or batch. Thus, two statements are in the same scope if
+ // they are in the same stored procedure, function, or batch.
+ return $this->GetOne($this->identitySQL);
+ }
+
+ function _affectedrows()
+ {
+ return sqlsrv_rows_affected($this->_queryID);
+ }
+
+ function CreateSequence($seq='adodbseq',$start=1)
+ {
+ if($this->debug) error_log("<hr>CreateSequence($seq,$start)");
+ sqlsrv_begin_transaction($this->_connectionID);
+ $start -= 1;
+ $this->Execute("create table $seq (id int)");//was float(53)
+ $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
+ if (!$ok) {
+ if($this->debug) error_log("<hr>Error: ROLLBACK");
+ sqlsrv_rollback($this->_connectionID);
+ return false;
+ }
+ sqlsrv_commit($this->_connectionID);
+ return true;
+ }
+
+ function GenID($seq='adodbseq',$start=1)
+ {
+ if($this->debug) error_log("<hr>GenID($seq,$start)");
+ sqlsrv_begin_transaction($this->_connectionID);
+ $ok = $this->Execute("update $seq with (tablock,holdlock) set id = id + 1");
+ if (!$ok) {
+ $this->Execute("create table $seq (id int)");
+ $ok = $this->Execute("insert into $seq with (tablock,holdlock) values($start)");
+ if (!$ok) {
+ if($this->debug) error_log("<hr>Error: ROLLBACK");
+ sqlsrv_rollback($this->_connectionID);
+ return false;
+ }
+ sqlsrv_commit($this->_connectionID);
+ return $start;
+ }
+ $num = $this->GetOne("select id from $seq");
+ sqlsrv_commit($this->_connectionID);
+ if($this->debug) error_log(" Returning: $num");
+ return $num;
+ }
+
+ // Format date column in sql string given an input format that understands Y M D
+ function SQLDate($fmt, $col=false)
+ {
+ if (!$col) $col = $this->sysTimeStamp;
+ $s = '';
+
+ $len = strlen($fmt);
+ for ($i=0; $i < $len; $i++) {
+ if ($s) $s .= '+';
+ $ch = $fmt[$i];
+ switch($ch) {
+ case 'Y':
+ case 'y':
+ $s .= "datename(yyyy,$col)";
+ break;
+ case 'M':
+ $s .= "convert(char(3),$col,0)";
+ break;
+ case 'm':
+ $s .= "replace(str(month($col),2),' ','0')";
+ break;
+ case 'Q':
+ case 'q':
+ $s .= "datename(quarter,$col)";
+ break;
+ case 'D':
+ case 'd':
+ $s .= "replace(str(day($col),2),' ','0')";
+ break;
+ case 'h':
+ $s .= "substring(convert(char(14),$col,0),13,2)";
+ break;
+
+ case 'H':
+ $s .= "replace(str(datepart(hh,$col),2),' ','0')";
+ break;
+
+ case 'i':
+ $s .= "replace(str(datepart(mi,$col),2),' ','0')";
+ break;
+ case 's':
+ $s .= "replace(str(datepart(ss,$col),2),' ','0')";
+ break;
+ case 'a':
+ case 'A':
+ $s .= "substring(convert(char(19),$col,0),18,2)";
+ break;
+
+ default:
+ if ($ch == '\\') {
+ $i++;
+ $ch = substr($fmt,$i,1);
+ }
+ $s .= $this->qstr($ch);
+ break;
+ }
+ }
+ return $s;
+ }
+
+
+ function BeginTrans()
+ {
+ if ($this->transOff) return true;
+ $this->transCnt += 1;
+ if ($this->debug) error_log('<hr>begin transaction');
+ sqlsrv_begin_transaction($this->_connectionID);
+ return true;
+ }
+
+ function CommitTrans($ok=true)
+ {
+ if ($this->transOff) return true;
+ if ($this->debug) error_log('<hr>commit transaction');
+ if (!$ok) return $this->RollbackTrans();
+ if ($this->transCnt) $this->transCnt -= 1;
+ sqlsrv_commit($this->_connectionID);
+ return true;
+ }
+ function RollbackTrans()
+ {
+ if ($this->transOff) return true;
+ if ($this->debug) error_log('<hr>rollback transaction');
+ if ($this->transCnt) $this->transCnt -= 1;
+ sqlsrv_rollback($this->_connectionID);
+ return true;
+ }
+
+ function SetTransactionMode( $transaction_mode )
+ {
+ $this->_transmode = $transaction_mode;
+ if (empty($transaction_mode)) {
+ $this->Execute('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
+ return;
+ }
+ if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode;
+ $this->Execute("SET TRANSACTION ".$transaction_mode);
+ }
+
+ /*
+ Usage:
+
+ $this->BeginTrans();
+ $this->RowLock('table1,table2','table1.id=33 and table2.id=table1.id'); # lock row 33 for both tables
+
+ # some operation on both tables table1 and table2
+
+ $this->CommitTrans();
+
+ See http://www.swynk.com/friends/achigrik/SQL70Locks.asp
+ */
+ function RowLock($tables,$where,$flds='top 1 null as ignore')
+ {
+ if (!$this->transCnt) $this->BeginTrans();
+ return $this->GetOne("select $flds from $tables with (ROWLOCK,HOLDLOCK) where $where");
+ }
+
+ function SelectDB($dbName)
+ {
+ $this->database = $dbName;
+ $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
+ if ($this->_connectionID) {
+ $rs = $this->Execute('USE '.$dbName);
+ if($rs) {
+ return true;
+ } else return false;
+ }
+ else return false;
+ }
+
+ function ErrorMsg()
+ {
+ $retErrors = sqlsrv_errors(SQLSRV_ERR_ALL);
+ if($retErrors != null) {
+ foreach($retErrors as $arrError) {
+ $this->_errorMsg .= "SQLState: ".$arrError[ 'SQLSTATE']."\n";
+ $this->_errorMsg .= "Error Code: ".$arrError[ 'code']."\n";
+ $this->_errorMsg .= "Message: ".$arrError[ 'message']."\n";
+ }
+ } else {
+ $this->_errorMsg = "No errors found";
+ }
+ return $this->_errorMsg;
+ }
+
+ function ErrorNo()
+ {
+ if ($this->_logsql && $this->_errorCode !== false) return $this->_errorCode;
+ $err = sqlsrv_errors(SQLSRV_ERR_ALL);
+ if($err[0]) return $err[0]['code'];
+ else return -1;
+ }
+
+ // returns true or false
+ function _connect($argHostname, $argUsername, $argPassword, $argDatabasename)
+ {
+ if (!function_exists('sqlsrv_connect')) return null;
+ $connectionInfo = array("Database"=>$argDatabasename,'UID'=>$argUsername,'PWD'=>$argPassword);
+ if ($this->debug) error_log("<hr>connecting... hostname: $argHostname params: ".var_export($connectionInfo,true));
+ //if ($this->debug) error_log("<hr>_connectionID before: ".serialize($this->_connectionID));
+ if(!($this->_connectionID = sqlsrv_connect($argHostname,$connectionInfo))) {
+ if ($this->debug) error_log( "<hr><b>errors</b>: ".print_r( sqlsrv_errors(), true));
+ return false;
+ }
+ //if ($this->debug) error_log(" _connectionID after: ".serialize($this->_connectionID));
+ //if ($this->debug) error_log("<hr>defined functions: <pre>".var_export(get_defined_functions(),true)."</pre>");
+ return true;
+ }
+
+ // returns true or false
+ function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+ {
+ //return null;//not implemented. NOTE: Persistent connections have no effect if PHP is used as a CGI program. (FastCGI!)
+ return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
+ }
+
+ function Prepare($sql)
+ {
+ $stmt = sqlsrv_prepare( $this->_connectionID, $sql);
+ if (!$stmt) return $sql;
+ return array($sql,$stmt);
+ }
+
+ // returns concatenated string
+ // MSSQL requires integers to be cast as strings
+ // automatically cast every datatype to VARCHAR(255)
+ // @author David Rogers (introspectshun)
+ function Concat()
+ {
+ $s = "";
+ $arr = func_get_args();
+
+ // Split single record on commas, if possible
+ if (sizeof($arr) == 1) {
+ foreach ($arr as $arg) {
+ $args = explode(',', $arg);
+ }
+ $arr = $args;
+ }
+
+ array_walk($arr, create_function('&$v', '$v = "CAST(" . $v . " AS VARCHAR(255))";'));
+ $s = implode('+',$arr);
+ if (sizeof($arr) > 0) return "$s";
+
+ return '';
+ }
+
+ /*
+ Unfortunately, it appears that mssql cannot handle varbinary > 255 chars
+ So all your blobs must be of type "image".
+
+ Remember to set in php.ini the following...
+
+ ; Valid range 0 - 2147483647. Default = 4096.
+ mssql.textlimit = 0 ; zero to pass through
+
+ ; Valid range 0 - 2147483647. Default = 4096.
+ mssql.textsize = 0 ; zero to pass through
+ */
+ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+ {
+
+ if (strtoupper($blobtype) == 'CLOB') {
+ $sql = "UPDATE $table SET $column='" . $val . "' WHERE $where";
+ return $this->Execute($sql) != false;
+ }
+ $sql = "UPDATE $table SET $column=0x".bin2hex($val)." WHERE $where";
+ return $this->Execute($sql) != false;
+ }
+
+ // returns query ID if successful, otherwise false
+ function _query($sql,$inputarr=false)
+ {
+ $this->_errorMsg = false;
+ if (is_array($inputarr)) {
+ $rez = sqlsrv_query($this->_connectionID,$sql,$inputarr);
+ } else if (is_array($sql)) {
+ $rez = sqlsrv_query($this->_connectionID,$sql[1],$inputarr);
+ } else {
+ $rez = sqlsrv_query($this->_connectionID,$sql);
+ }
+ if ($this->debug) error_log("<hr>running query: ".var_export($sql,true)."<hr>input array: ".var_export($inputarr,true)."<hr>result: ".var_export($rez,true));//"<hr>connection: ".serialize($this->_connectionID)
+ //fix for returning true on anything besides select statements
+ if (is_array($sql)) $sql = $sql[1];
+ $sql = ltrim($sql);
+ if(stripos($sql, 'SELECT') !== 0 && $rez !== false) {
+ if ($this->debug) error_log(" isn't a select query, returning boolean true");
+ return true;
+ }
+ //end fix
+ if(!$rez) $rez = false;
+ return $rez;
+ }
+
+ // returns true or false
+ function _close()
+ {
+ if ($this->transCnt) $this->RollbackTrans();
+ $rez = @sqlsrv_close($this->_connectionID);
+ $this->_connectionID = false;
+ return $rez;
+ }
+
+ // mssql uses a default date like Dec 30 2000 12:00AM
+ static function UnixDate($v)
+ {
+ return ADORecordSet_array_mssql::UnixDate($v);
+ }
+
+ static function UnixTimeStamp($v)
+ {
+ return ADORecordSet_array_mssql::UnixTimeStamp($v);
+ }
+
+ function &MetaIndexes($table,$primary=false)
+ {
+ $table = $this->qstr($table);
+
+ $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
+ CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK,
+ CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
+ FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
+ INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
+ INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
+ WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND O.Name LIKE $table
+ ORDER BY O.name, I.Name, K.keyno";
+
+ global $ADODB_FETCH_MODE;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($this->fetchMode !== FALSE) {
+ $savem = $this->SetFetchMode(FALSE);
+ }
+
+ $rs = $this->Execute($sql);
+ if (isset($savem)) {
+ $this->SetFetchMode($savem);
+ }
+ $ADODB_FETCH_MODE = $save;
+
+ if (!is_object($rs)) {
+ return FALSE;
+ }
+
+ $indexes = array();
+ while ($row = $rs->FetchRow()) {
+ if (!$primary && $row[5]) continue;
+
+ $indexes[$row[0]]['unique'] = $row[6];
+ $indexes[$row[0]]['columns'][] = $row[1];
+ }
+ return $indexes;
+ }
+
+ function MetaForeignKeys($table, $owner=false, $upper=false)
+ {
+ global $ADODB_FETCH_MODE;
+
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ $table = $this->qstr(strtoupper($table));
+
+ $sql =
+ "select object_name(constid) as constraint_name,
+ col_name(fkeyid, fkey) as column_name,
+ object_name(rkeyid) as referenced_table_name,
+ col_name(rkeyid, rkey) as referenced_column_name
+ from sysforeignkeys
+ where upper(object_name(fkeyid)) = $table
+ order by constraint_name, referenced_table_name, keyno";
+
+ $constraints =& $this->GetArray($sql);
+
+ $ADODB_FETCH_MODE = $save;
+
+ $arr = false;
+ foreach($constraints as $constr) {
+ //print_r($constr);
+ $arr[$constr[0]][$constr[2]][] = $constr[1].'='.$constr[3];
+ }
+ if (!$arr) return false;
+
+ $arr2 = false;
+
+ foreach($arr as $k => $v) {
+ foreach($v as $a => $b) {
+ if ($upper) $a = strtoupper($a);
+ $arr2[$a] = $b;
+ }
+ }
+ return $arr2;
+ }
+
+ //From: Fernando Moreira <FMoreira@imediata.pt>
+ function MetaDatabases()
+ {
+ $this->SelectDB("master");
+ $rs =& $this->Execute($this->metaDatabasesSQL);
+ $rows = $rs->GetRows();
+ $ret = array();
+ for($i=0;$i<count($rows);$i++) {
+ $ret[] = $rows[$i][0];
+ }
+ $this->SelectDB($this->database);
+ if($ret)
+ return $ret;
+ else
+ return false;
+ }
+
+ // "Stein-Aksel Basma" <basma@accelero.no>
+ // tested with MSSQL 2000
+ function &MetaPrimaryKeys($table)
+ {
+ global $ADODB_FETCH_MODE;
+
+ $schema = '';
+ $this->_findschema($table,$schema);
+ if (!$schema) $schema = $this->database;
+ if ($schema) $schema = "and k.table_catalog like '$schema%'";
+
+ $sql = "select distinct k.column_name,ordinal_position from information_schema.key_column_usage k,
+ information_schema.table_constraints tc
+ where tc.constraint_name = k.constraint_name and tc.constraint_type =
+ 'PRIMARY KEY' and k.table_name = '$table' $schema order by ordinal_position ";
+
+ $savem = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ $a = $this->GetCol($sql);
+ $ADODB_FETCH_MODE = $savem;
+
+ if ($a && sizeof($a)>0) return $a;
+ $false = false;
+ return $false;
+ }
+
+
+ function &MetaTables($ttype=false,$showSchema=false,$mask=false)
+ {
+ if ($mask) {
+ $save = $this->metaTablesSQL;
+ $mask = $this->qstr(($mask));
+ $this->metaTablesSQL .= " AND name like $mask";
+ }
+ $ret =& ADOConnection::MetaTables($ttype,$showSchema);
+
+ if ($mask) {
+ $this->metaTablesSQL = $save;
+ }
+ return $ret;
+ }
+}
+
+/*--------------------------------------------------------------------------------------
+ Class Name: Recordset
+--------------------------------------------------------------------------------------*/
+
+class ADORecordset_mssqlnative extends ADORecordSet {
+
+ var $databaseType = "mssqlnative";
+ var $canSeek = false;
+ var $fieldOffset = 0;
+ // _mths works only in non-localised system
+
+ function ADORecordset_mssqlnative($id,$mode=false)
+ {
+ if ($mode === false) {
+ global $ADODB_FETCH_MODE;
+ $mode = $ADODB_FETCH_MODE;
+
+ }
+ $this->fetchMode = $mode;
+ return $this->ADORecordSet($id,$mode);
+ }
+
+
+ function _initrs()
+ {
+ global $ADODB_COUNTRECS;
+ if ($this->connection->debug) error_log("(before) ADODB_COUNTRECS: {$ADODB_COUNTRECS} _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
+ /*$retRowsAff = sqlsrv_rows_affected($this->_queryID);//"If you need to determine the number of rows a query will return before retrieving the actual results, appending a SELECT COUNT ... query would let you get that information, and then a call to next_result would move you to the "real" results."
+ error_log("rowsaff: ".serialize($retRowsAff));
+ $this->_numOfRows = ($ADODB_COUNTRECS)? $retRowsAff:-1;*/
+ $this->_numOfRows = -1;//not supported
+ $fieldmeta = sqlsrv_field_metadata($this->_queryID);
+ $this->_numOfFields = ($fieldmeta)? count($fieldmeta):-1;
+ if ($this->connection->debug) error_log("(after) _numOfRows: {$this->_numOfRows} _numOfFields: {$this->_numOfFields}");
+ }
+
+
+ //Contributed by "Sven Axelsson" <sven.axelsson@bokochwebb.se>
+ // get next resultset - requires PHP 4.0.5 or later
+ function NextRecordSet()
+ {
+ if (!sqlsrv_next_result($this->_queryID)) return false;
+ $this->_inited = false;
+ $this->bind = false;
+ $this->_currentRow = -1;
+ $this->Init();
+ return true;
+ }
+
+ /* Use associative array to get fields array */
+ function Fields($colname)
+ {
+ if ($this->fetchMode != ADODB_FETCH_NUM) return $this->fields[$colname];
+ if (!$this->bind) {
+ $this->bind = array();
+ for ($i=0; $i < $this->_numOfFields; $i++) {
+ $o = $this->FetchField($i);
+ $this->bind[strtoupper($o->name)] = $i;
+ }
+ }
+
+ return $this->fields[$this->bind[strtoupper($colname)]];
+ }
+
+ /* Returns: an object containing field information.
+ Get column information in the Recordset object. fetchField() can be used in order to obtain information about
+ fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
+ fetchField() is retrieved. */
+
+ function &FetchField($fieldOffset = -1)
+ {
+ if ($this->connection->debug) error_log("<hr>fetchfield: $fieldOffset, fetch array: <pre>".print_r($this->fields,true)."</pre> backtrace: ".adodb_backtrace(false));
+ if ($fieldOffset != -1) $this->fieldOffset = $fieldOffset;
+ $arrKeys = array_keys($this->fields);
+ if(array_key_exists($this->fieldOffset,$arrKeys) && !array_key_exists($arrKeys[$this->fieldOffset],$this->fields)) {
+ $f = false;
+ } else {
+ $f = $this->fields[ $arrKeys[$this->fieldOffset] ];
+ if($fieldOffset == -1) $this->fieldOffset++;
+ }
+
+ if (empty($f)) {
+ $f = false;//PHP Notice: Only variable references should be returned by reference
+ }
+ return $f;
+ }
+
+ function _seek($row)
+ {
+ return false;//There is no support for cursors in the driver at this time. All data is returned via forward-only streams.
+ }
+
+ // speedup
+ function MoveNext()
+ {
+ if ($this->connection->debug) error_log("movenext()");
+ //if ($this->connection->debug) error_log("eof (beginning): ".$this->EOF);
+ if ($this->EOF) return false;
+
+ $this->_currentRow++;
+ if ($this->connection->debug) error_log("_currentRow: ".$this->_currentRow);
+
+ if ($this->_fetch()) return true;
+ $this->EOF = true;
+ //if ($this->connection->debug) error_log("eof (end): ".$this->EOF);
+
+ return false;
+ }
+
+
+ // INSERT UPDATE DELETE returns false even if no error occurs in 4.0.4
+ // also the date format has been changed from YYYY-mm-dd to dd MMM YYYY in 4.0.4. Idiot!
+ function _fetch($ignore_fields=false)
+ {
+ if ($this->connection->debug) error_log("_fetch()");
+ if ($this->fetchMode & ADODB_FETCH_ASSOC) {
+ if ($this->fetchMode & ADODB_FETCH_NUM) {
+ if ($this->connection->debug) error_log("fetch mode: both");
+ $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_BOTH);
+ } else {
+ if ($this->connection->debug) error_log("fetch mode: assoc");
+ $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_ASSOC);
+ }
+
+ if (ADODB_ASSOC_CASE == 0) {
+ foreach($this->fields as $k=>$v) {
+ $this->fields[strtolower($k)] = $v;
+ }
+ } else if (ADODB_ASSOC_CASE == 1) {
+ foreach($this->fields as $k=>$v) {
+ $this->fields[strtoupper($k)] = $v;
+ }
+ }
+ } else {
+ if ($this->connection->debug) error_log("fetch mode: num");
+ $this->fields = @sqlsrv_fetch_array($this->_queryID,SQLSRV_FETCH_NUMERIC);
+ }
+ if(is_array($this->fields) && array_key_exists(1,$this->fields) && !array_key_exists(0,$this->fields)) {//fix fetch numeric keys since they're not 0 based
+ $arrFixed = array();
+ foreach($this->fields as $key=>$value) {
+ if(is_numeric($key)) {
+ $arrFixed[$key-1] = $value;
+ } else {
+ $arrFixed[$key] = $value;
+ }
+ }
+ //if($this->connection->debug) error_log("<hr>fixing non 0 based return array, old: ".print_r($this->fields,true)." new: ".print_r($arrFixed,true));
+ $this->fields = $arrFixed;
+ }
+ if(is_array($this->fields)) {
+ foreach($this->fields as $key=>$value) {
+ if (is_object($value) && method_exists($value, 'format')) {//is DateTime object
+ $this->fields[$key] = $value->format("Y-m-d\TH:i:s\Z");
+ }
+ }
+ }
+ if($this->fields === null) $this->fields = false;
+ if ($this->connection->debug) error_log("<hr>after _fetch, fields: <pre>".print_r($this->fields,true)." backtrace: ".adodb_backtrace(false));
+ return $this->fields;
+ }
+
+ /* close() only needs to be called if you are worried about using too much memory while your script
+ is running. All associated result memory for the specified result identifier will automatically be freed. */
+ function _close()
+ {
+ $rez = sqlsrv_free_stmt($this->_queryID);
+ $this->_queryID = false;
+ return $rez;
+ }
+
+ // mssql uses a default date like Dec 30 2000 12:00AM
+ static function UnixDate($v)
+ {
+ return ADORecordSet_array_mssqlnative::UnixDate($v);
+ }
+
+ static function UnixTimeStamp($v)
+ {
+ return ADORecordSet_array_mssqlnative::UnixTimeStamp($v);
+ }
+}
+
+
+class ADORecordSet_array_mssqlnative extends ADORecordSet_array {
+ function ADORecordSet_array_mssqlnative($id=-1,$mode=false)
+ {
+ $this->ADORecordSet_array($id,$mode);
+ }
+
+ // mssql uses a default date like Dec 30 2000 12:00AM
+ static function UnixDate($v)
+ {
+
+ if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixDate($v);
+
+ global $ADODB_mssql_mths,$ADODB_mssql_date_order;
+
+ //Dec 30 2000 12:00AM
+ if ($ADODB_mssql_date_order == 'dmy') {
+ if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
+ return parent::UnixDate($v);
+ }
+ if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+
+ $theday = $rr[1];
+ $themth = substr(strtoupper($rr[2]),0,3);
+ } else {
+ if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})|" ,$v, $rr)) {
+ return parent::UnixDate($v);
+ }
+ if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+
+ $theday = $rr[2];
+ $themth = substr(strtoupper($rr[1]),0,3);
+ }
+ $themth = $ADODB_mssql_mths[$themth];
+ if ($themth <= 0) return false;
+ // h-m-s-MM-DD-YY
+ return mktime(0,0,0,$themth,$theday,$rr[3]);
+ }
+
+ static function UnixTimeStamp($v)
+ {
+
+ if (is_numeric(substr($v,0,1)) && ADODB_PHPVER >= 0x4200) return parent::UnixTimeStamp($v);
+
+ global $ADODB_mssql_mths,$ADODB_mssql_date_order;
+
+ //Dec 30 2000 12:00AM
+ if ($ADODB_mssql_date_order == 'dmy') {
+ if (!preg_match( "|^([0-9]{1,2})[-/\. ]+([A-Za-z]{3})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
+ ,$v, $rr)) return parent::UnixTimeStamp($v);
+ if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+
+ $theday = $rr[1];
+ $themth = substr(strtoupper($rr[2]),0,3);
+ } else {
+ if (!preg_match( "|^([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})|"
+ ,$v, $rr)) return parent::UnixTimeStamp($v);
+ if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
+
+ $theday = $rr[2];
+ $themth = substr(strtoupper($rr[1]),0,3);
+ }
+
+ $themth = $ADODB_mssql_mths[$themth];
+ if ($themth <= 0) return false;
+
+ switch (strtoupper($rr[6])) {
+ case 'P':
+ if ($rr[4]<12) $rr[4] += 12;
+ break;
+ case 'A':
+ if ($rr[4]==12) $rr[4] = 0;
+ break;
+ default:
+ break;
+ }
+ // h-m-s-MM-DD-YY
+ return mktime($rr[4],$rr[5],0,$themth,$theday,$rr[3]);
+ }
+}
+
+/*
+Code Example 1:
+
+select object_name(constid) as constraint_name,
+ object_name(fkeyid) as table_name,
+ col_name(fkeyid, fkey) as column_name,
+ object_name(rkeyid) as referenced_table_name,
+ col_name(rkeyid, rkey) as referenced_column_name
+from sysforeignkeys
+where object_name(fkeyid) = x
+order by constraint_name, table_name, referenced_table_name, keyno
+
+Code Example 2:
+select constraint_name,
+ column_name,
+ ordinal_position
+from information_schema.key_column_usage
+where constraint_catalog = db_name()
+and table_name = x
+order by constraint_name, ordinal_position
+
+http://www.databasejournal.com/scripts/article.php/1440551
+*/
+
+?>
\ No newline at end of file
<?php
/**
-* @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+* @version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
return array($sql,$stmt);
}
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
if (is_string($sql)) $sql = str_replace('||','+',$sql);
return ADODB_mssql::_query($sql,$inputarr);
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
$fld->not_null = ($rs->fields[2] != 'YES');
$fld->primary_key = ($rs->fields[3] == 'PRI');
$fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
- $fld->binary = (strpos($type,'blob') !== false);
+ $fld->binary = (strpos($type,'blob') !== false || strpos($type,'binary') !== false);
$fld->unsigned = (strpos($type,'unsigned') !== false);
$fld->zerofill = (strpos($type,'zerofill') !== false);
}
// returns queryID or false
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
//global $ADODB_COUNTRECS;
//if($ADODB_COUNTRECS)
$table = "$owner.$table";
}
$a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
- if ($associative) $create_sql = $a_create_table["Create Table"];
- else $create_sql = $a_create_table[1];
+ if ($associative) {
+ $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"];
+ } else $create_sql = $a_create_table[1];
$matches = array();
$ref_table = strtoupper($ref_table);
}
- $foreign_keys[$ref_table] = array();
- $num_fields = count($my_field);
- for ( $j = 0; $j < $num_fields; $j ++ ) {
+ // see https://sourceforge.net/tracker/index.php?func=detail&aid=2287278&group_id=42718&atid=433976
+ if (!isset($foreign_keys[$ref_table])) {
+ $foreign_keys[$ref_table] = array();
+ }
+ $num_fields = count($my_field);
+ for ( $j = 0; $j < $num_fields; $j ++ ) {
if ( $associative ) {
$foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
} else {
case 'LONGBLOB':
case 'BLOB':
case 'MEDIUMBLOB':
+ case 'BINARY':
return !empty($fieldobj->binary) ? 'B' : 'X';
case 'YEAR':
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
$table = "$owner.$table";
}
$a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
- if ($associative) $create_sql = $a_create_table["Create Table"];
- else $create_sql = $a_create_table[1];
+ if ($associative) {
+ $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"];
+ } else $create_sql = $a_create_table[1];
$matches = array();
$ref_table = strtoupper($ref_table);
}
- $foreign_keys[$ref_table] = array();
- $num_fields = count($my_field);
+ // see https://sourceforge.net/tracker/index.php?func=detail&aid=2287278&group_id=42718&atid=433976
+ if (!isset($foreign_keys[$ref_table])) {
+ $foreign_keys[$ref_table] = array();
+ }
+ $num_fields = count($my_field);
for ( $j = 0; $j < $num_fields; $j ++ ) {
if ( $associative ) {
$foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
$fld->type = $query_array[1];
$arr = explode(",",$query_array[2]);
$fld->enums = $arr;
- $fld->max_length = max(array_map("strlen",explode(",",$query_array[2]))) - 2; // PHP >= 4.0.6
- $fld->max_length = ($fld->max_length == 0 ? 1 : $fld->max_length);
+ $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
+ $fld->max_length = ($zlen > 0) ? $zlen : 1;
} else {
$fld->type = $type;
$fld->max_length = -1;
function Prepare($sql)
{
return $sql;
-
$stmt = $this->_connectionID->prepare($sql);
if (!$stmt) {
echo $this->ErrorMsg();
function _query($sql, $inputarr)
{
global $ADODB_COUNTRECS;
-
+ // Move to the next recordset, or return false if there is none. In a stored proc
+ // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
+ // returns false. I think this is because the last "recordset" is actually just the
+ // return value of the stored proc (ie the number of rows affected).
+ // Commented out for reasons of performance. You should retrieve every recordset yourself.
+ // if (!mysqli_next_result($this->connection->_connectionID)) return false;
+
if (is_array($sql)) {
+
+ // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but
+ // returns as bound variables.
+
$stmt = $sql[1];
$a = '';
foreach($inputarr as $k => $v) {
$fnarr = array_merge( array($stmt,$a) , $inputarr);
$ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr);
-
$ret = mysqli_stmt_execute($stmt);
return $ret;
}
+
+ /*
if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
return false;
}
return $mysql_res;
+ */
+
+ if( $rs = mysqli_multi_query($this->_connectionID, $sql.';') )//Contributed by "Geisel Sierote" <geisel#4up.com.br>
+ {
+ $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID );
+ return $rs ? $rs : true; // mysqli_more_results( $this->_connectionID )
+ } else {
+ if($this->debug)
+ ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
+ return false;
+ }
}
/* Returns: the last error message from previous database operation */
{
$fieldnr = $fieldOffset;
if ($fieldOffset != -1) {
- $fieldOffset = mysqli_field_seek($this->_queryID, $fieldnr);
+ $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr);
}
- $o = mysqli_fetch_field($this->_queryID);
+ $o = @mysqli_fetch_field($this->_queryID);
+ if (!$o) return false;
/* Properties of an ADOFieldObject as set by MetaColumns */
$o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG;
$o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG;
return true;
}
+
+ function NextRecordSet()
+ {
+ global $ADODB_COUNTRECS;
+
+ mysqli_free_result($this->_queryID);
+ $this->_queryID = -1;
+ // Move to the next recordset, or return false if there is none. In a stored proc
+ // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
+ // returns false. I think this is because the last "recordset" is actually just the
+ // return value of the stored proc (ie the number of rows affected).
+ if(!mysqli_next_result($this->connection->_connectionID)) {
+ return false;
+ }
+ // CD: There is no $this->_connectionID variable, at least in the ADO version I'm using
+ $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->connection->_connectionID )
+ : @mysqli_use_result( $this->connection->_connectionID );
+ if(!$this->_queryID) {
+ return false;
+ }
+ $this->_inited = false;
+ $this->bind = false;
+ $this->_currentRow = -1;
+ $this->Init();
+ return true;
+ }
+
// 10% speedup to move MoveNext to child class
// This is the only implementation that works now (23-10-2003).
// Other functions return no or the wrong results.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
if (!$ok) return $this->RollbackTrans();
if ($this->transCnt) $this->transCnt -= 1;
- $this->Execute('COMMIT');
+ $ok = $this->Execute('COMMIT');
$this->Execute('SET AUTOCOMMIT=1');
- return true;
+ return $ok ? true : false;
}
function RollbackTrans()
{
if ($this->transOff) return true;
if ($this->transCnt) $this->transCnt -= 1;
- $this->Execute('ROLLBACK');
+ $ok = $this->Execute('ROLLBACK');
$this->Execute('SET AUTOCOMMIT=1');
- return true;
+ return $ok ? true : false;
}
function RowLock($tables,$where='',$flds='1 as adodb_ignore')
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved.
First cut at the Netezza Driver by Josh Eldridge joshuae74#hotmail.com
Based on the previous postgres drivers.
<?php
/*
- version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim. All rights reserved.
+ version V5.06 16 Oct 2008 (c) 2000-2009 John Lim. All rights reserved.
Released under both BSD license and Lesser GPL library license.
Whenever there is any discrepancy between the two licenses,
var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
var $session_sharing_force_blob = false; // alter session on updateblob if set to true
var $firstrows = true; // enable first rows optimization on SelectLimit()
- var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit.
+ var $selectOffsetAlg1 = 1000; // when to use 1st algorithm of selectlimit.
var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS'
var $dateformat = 'YYYY-MM-DD'; // DBDate format
var $useDBDateFormatForTextInput=false;
function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
{
if (!function_exists('OCIPLogon')) return null;
-
+ #adodb_backtrace();
$this->_errorMsg = false;
$this->_errorCode = false;
$argHostport = empty($this->port)? "1521" : $this->port;
}
+ if (strncasecmp($argDatabasename,'SID=',4) == 0) {
+ $argDatabasename = substr($argDatabasename,4);
+ $this->connectSID = true;
+ }
+
if ($this->connectSID) {
$argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
.")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
}
// format and return date string in database date format
- function DBDate($d)
+ function DBDate($d,$isfld=false)
{
if (empty($d) && $d !== 0) return 'null';
-
+ if ($isfld) return 'TO_DATE('.$d.",'".$this->dateformat."')";
if (is_string($d)) $d = ADORecordSet::UnixDate($d);
return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->dateformat."')";
}
}
// format and return date string in database timestamp format
- function DBTimeStamp($ts)
+ function DBTimeStamp($ts,$isfld=false)
{
if (empty($ts) && $ts !== 0) return 'null';
+ if ($isfld) return 'TO_DATE(substr('.$ts.",1,19),'RRRR-MM-DD, HH24:MI:SS')";
if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
return 'TO_DATE('.adodb_date("'Y-m-d H:i:s'",$ts).",'RRRR-MM-DD, HH24:MI:SS')";
}
$this->autoCommit = false;
$this->_commit = OCI_DEFAULT;
- if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode);
- return true;
+ if ($this->_transmode) $ok = $this->Execute("SET TRANSACTION ".$this->_transmode);
+ else $ok = true;
+
+ return $ok ? true : false;
}
function CommitTrans($ok=true)
$sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
}
- if ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000) {
+ if ($offset == -1 || ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000)) {
if ($nrows > 0) {
if ($offset > 0) $nrows += $offset;
//$inputarr['adodb_rownum'] = $nrows;
// Let Oracle return the name of the columns
$q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
-
+
$false = false;
if (! $stmt_arr = $this->Prepare($q_fields)) {
return $false;
}
$stmt = $stmt_arr[1];
- if (is_array($inputarr)) {
+ if (is_array($inputarr)) {
foreach($inputarr as $k => $v) {
if (is_array($v)) {
if (sizeof($v) == 2) // suggested by g.giunta@libero.
$bindarr[$k] = $v;
} else { // dynamic sql, so rebind every time
OCIBindByName($stmt,":$k",$inputarr[$k],$len);
+
}
}
}
OCIFreeStatement($stmt);
$fields = implode(',', $cols);
- $nrows += $offset;
+ if ($nrows <= 0) $nrows = 999999999999;
+ else $nrows += $offset;
$offset += 1; // in Oracle rownum starts at 1
if ($this->databaseType == 'oci8po') {
- $sql = "SELECT $fields FROM".
+ $sql = "SELECT /*+ FIRST_ROWS */ $fields FROM".
"(SELECT rownum as adodb_rownum, $fields FROM".
" ($sql) WHERE rownum <= ?".
") WHERE adodb_rownum >= ?";
} else {
- $sql = "SELECT $fields FROM".
+ $sql = "SELECT /*+ FIRST_ROWS */ $fields FROM".
"(SELECT rownum as adodb_rownum, $fields FROM".
" ($sql) WHERE rownum <= :adodb_nrows".
") WHERE adodb_rownum >= :adodb_offset";
}
/**
- * Usage: store file pointed to by $var in a blob
+ * Usage: store file pointed to by $val in a blob
*/
function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
{
if ($isOutput == false) {
$var = $this->BlobEncode($var);
$tmp->WriteTemporary($var);
- $this->_refLOBs[$numlob]['VAR'] = $var;
+ $this->_refLOBs[$numlob]['VAR'] = &$var;
if ($this->debug) {
ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
}
$db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
$db->execute($stmt);
*/
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
if (is_array($sql)) { // is prepared sql
$stmt = $sql[1];
return false;
}
+ // From Oracle Whitepaper: PHP Scalability and High Availability
+ function IsConnectionError($err)
+ {
+ switch($err) {
+ case 378: /* buffer pool param incorrect */
+ case 602: /* core dump */
+ case 603: /* fatal error */
+ case 609: /* attach failed */
+ case 1012: /* not logged in */
+ case 1033: /* init or shutdown in progress */
+ case 1043: /* Oracle not available */
+ case 1089: /* immediate shutdown in progress */
+ case 1090: /* shutdown in progress */
+ case 1092: /* instance terminated */
+ case 3113: /* disconnect */
+ case 3114: /* not connected */
+ case 3122: /* closing window */
+ case 3135: /* lost contact */
+ case 12153: /* TNS: not connected */
+ case 27146: /* fatal or instance terminated */
+ case 28511: /* Lost RPC */
+ return true;
+ }
+ return false;
+ }
+
// returns true or false
function _close()
{
--- /dev/null
+<?php
+/*
+
+ version V5.04 13 Feb 2008 (c) 2000-2008 John Lim. 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.
+
+ Latest version is available at http://adodb.sourceforge.net
+
+ Code contributed by George Fourlanos <fou@infomap.gr>
+
+ 13 Nov 2000 jlim - removed all ora_* references.
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+/*
+NLS_Date_Format
+Allows you to use a date format other than the Oracle Lite default. When a literal
+character string appears where a date value is expected, the Oracle Lite database
+tests the string to see if it matches the formats of Oracle, SQL-92, or the value
+specified for this parameter in the POLITE.INI file. Setting this parameter also
+defines the default format used in the TO_CHAR or TO_DATE functions when no
+other format string is supplied.
+
+For Oracle the default is dd-mon-yy or dd-mon-yyyy, and for SQL-92 the default is
+yy-mm-dd or yyyy-mm-dd.
+
+Using 'RR' in the format forces two-digit years less than or equal to 49 to be
+interpreted as years in the 21st century (2000\962049), and years over 50 as years in
+the 20th century (1950\961999). Setting the RR format as the default for all two-digit
+year entries allows you to become year-2000 compliant. For example:
+NLS_DATE_FORMAT='RR-MM-DD'
+
+You can also modify the date format using the ALTER SESSION command.
+*/
+
+# define the LOB descriptor type for the given type
+# returns false if no LOB descriptor
+function oci_lob_desc($type) {
+ switch ($type) {
+ case OCI_B_BFILE: $result = OCI_D_FILE; break;
+ case OCI_B_CFILEE: $result = OCI_D_FILE; break;
+ case OCI_B_CLOB: $result = OCI_D_LOB; break;
+ case OCI_B_BLOB: $result = OCI_D_LOB; break;
+ case OCI_B_ROWID: $result = OCI_D_ROWID; break;
+ default: $result = false; break;
+ }
+ return $result;
+}
+
+class ADODB_oci8 extends ADOConnection {
+ var $databaseType = 'oci8';
+ var $dataProvider = 'oci8';
+ var $replaceQuote = "''"; // string to use to replace quotes
+ var $concat_operator='||';
+ var $sysDate = "TRUNC(SYSDATE)";
+ var $sysTimeStamp = 'SYSDATE';
+ var $metaDatabasesSQL = "SELECT USERNAME FROM ALL_USERS WHERE USERNAME NOT IN ('SYS','SYSTEM','DBSNMP','OUTLN') ORDER BY 1";
+ var $_stmt;
+ var $_commit = OCI_COMMIT_ON_SUCCESS;
+ var $_initdate = true; // init date to YYYY-MM-DD
+ var $metaTablesSQL = "select table_name,table_type from cat where table_type in ('TABLE','VIEW') and table_name not like 'BIN\$%'"; // bin$ tables are recycle bin tables
+ var $metaColumnsSQL = "select cname,coltype,width, SCALE, PRECISION, NULLS, DEFAULTVAL from col where tname='%s' order by colno"; //changed by smondino@users.sourceforge. net
+ var $_bindInputArray = true;
+ var $hasGenID = true;
+ var $_genIDSQL = "SELECT (%s.nextval) FROM DUAL";
+ var $_genSeqSQL = "CREATE SEQUENCE %s START WITH %s";
+ var $_dropSeqSQL = "DROP SEQUENCE %s";
+ var $hasAffectedRows = true;
+ var $random = "abs(mod(DBMS_RANDOM.RANDOM,10000001)/10000000)";
+ var $noNullStrings = false;
+ var $connectSID = false;
+ var $_bind = false;
+ var $_nestedSQL = true;
+ var $_hasOCIFetchStatement = false;
+ var $_getarray = false; // currently not working
+ var $leftOuter = ''; // oracle wierdness, $col = $value (+) for LEFT OUTER, $col (+)= $value for RIGHT OUTER
+ var $session_sharing_force_blob = false; // alter session on updateblob if set to true
+ var $firstrows = true; // enable first rows optimization on SelectLimit()
+ var $selectOffsetAlg1 = 100; // when to use 1st algorithm of selectlimit.
+ var $NLS_DATE_FORMAT = 'YYYY-MM-DD'; // To include time, use 'RRRR-MM-DD HH24:MI:SS'
+ var $dateformat = 'YYYY-MM-DD'; // DBDate format
+ var $useDBDateFormatForTextInput=false;
+ var $datetime = false; // MetaType('DATE') returns 'D' (datetime==false) or 'T' (datetime == true)
+ var $_refLOBs = array();
+
+ // var $ansiOuter = true; // if oracle9
+
+ function ADODB_oci8()
+ {
+ $this->_hasOCIFetchStatement = ADODB_PHPVER >= 0x4200;
+ if (defined('ADODB_EXTENSION')) $this->rsPrefix .= 'ext_';
+ }
+
+ /* function MetaColumns($table) added by smondino@users.sourceforge.net*/
+ function MetaColumns($table)
+ {
+ global $ADODB_FETCH_MODE;
+
+ $false = false;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($this->fetchMode !== false) $savem = $this->SetFetchMode(false);
+
+ $rs = $this->Execute(sprintf($this->metaColumnsSQL,strtoupper($table)));
+
+ if (isset($savem)) $this->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+ if (!$rs) {
+ return $false;
+ }
+ $retarr = array();
+ while (!$rs->EOF) { //print_r($rs->fields);
+ $fld = new ADOFieldObject();
+ $fld->name = $rs->fields[0];
+ $fld->type = $rs->fields[1];
+ $fld->max_length = $rs->fields[2];
+ $fld->scale = $rs->fields[3];
+ if ($rs->fields[1] == 'NUMBER') {
+ if ($rs->fields[3] == 0) $fld->type = 'INT';
+ $fld->max_length = $rs->fields[4];
+ }
+ $fld->not_null = (strncmp($rs->fields[5], 'NOT',3) === 0);
+ $fld->binary = (strpos($fld->type,'BLOB') !== false);
+ $fld->default_value = $rs->fields[6];
+
+ if ($ADODB_FETCH_MODE == ADODB_FETCH_NUM) $retarr[] = $fld;
+ else $retarr[strtoupper($fld->name)] = $fld;
+ $rs->MoveNext();
+ }
+ $rs->Close();
+ if (empty($retarr))
+ return $false;
+ else
+ return $retarr;
+ }
+
+ function Time()
+ {
+ $rs = $this->Execute("select TO_CHAR($this->sysTimeStamp,'YYYY-MM-DD HH24:MI:SS') from dual");
+ if ($rs && !$rs->EOF) return $this->UnixTimeStamp(reset($rs->fields));
+
+ return false;
+ }
+
+/*
+
+ Multiple modes of connection are supported:
+
+ a. Local Database
+ $conn->Connect(false,'scott','tiger');
+
+ b. From tnsnames.ora
+ $conn->Connect(false,'scott','tiger',$tnsname);
+ $conn->Connect($tnsname,'scott','tiger');
+
+ c. Server + service name
+ $conn->Connect($serveraddress,'scott,'tiger',$service_name);
+
+ d. Server + SID
+ $conn->connectSID = true;
+ $conn->Connect($serveraddress,'scott,'tiger',$SID);
+
+
+Example TNSName:
+---------------
+NATSOFT.DOMAIN =
+ (DESCRIPTION =
+ (ADDRESS_LIST =
+ (ADDRESS = (PROTOCOL = TCP)(HOST = kermit)(PORT = 1523))
+ )
+ (CONNECT_DATA =
+ (SERVICE_NAME = natsoft.domain)
+ )
+ )
+
+ There are 3 connection modes, 0 = non-persistent, 1 = persistent, 2 = force new connection
+
+*/
+ function _connect($argHostname, $argUsername, $argPassword, $argDatabasename,$mode=0)
+ {
+ if (!function_exists('OCIPLogon')) return null;
+
+
+ $this->_errorMsg = false;
+ $this->_errorCode = false;
+
+ if($argHostname) { // added by Jorma Tuomainen <jorma.tuomainen@ppoy.fi>
+ if (empty($argDatabasename)) $argDatabasename = $argHostname;
+ else {
+ if(strpos($argHostname,":")) {
+ $argHostinfo=explode(":",$argHostname);
+ $argHostname=$argHostinfo[0];
+ $argHostport=$argHostinfo[1];
+ } else {
+ $argHostport = empty($this->port)? "1521" : $this->port;
+ }
+
+ if ($this->connectSID) {
+ $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
+ .")(PORT=$argHostport))(CONNECT_DATA=(SID=$argDatabasename)))";
+ } else
+ $argDatabasename="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=".$argHostname
+ .")(PORT=$argHostport))(CONNECT_DATA=(SERVICE_NAME=$argDatabasename)))";
+ }
+ }
+
+ //if ($argHostname) print "<p>Connect: 1st argument should be left blank for $this->databaseType</p>";
+ if ($mode==1) {
+ $this->_connectionID = ($this->charSet) ?
+ OCIPLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
+ :
+ OCIPLogon($argUsername,$argPassword, $argDatabasename)
+ ;
+ if ($this->_connectionID && $this->autoRollback) OCIrollback($this->_connectionID);
+ } else if ($mode==2) {
+ $this->_connectionID = ($this->charSet) ?
+ OCINLogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
+ :
+ OCINLogon($argUsername,$argPassword, $argDatabasename);
+
+ } else {
+ $this->_connectionID = ($this->charSet) ?
+ OCILogon($argUsername,$argPassword, $argDatabasename,$this->charSet)
+ :
+ OCILogon($argUsername,$argPassword, $argDatabasename);
+ }
+ if (!$this->_connectionID) return false;
+ if ($this->_initdate) {
+ $this->Execute("ALTER SESSION SET NLS_DATE_FORMAT='".$this->NLS_DATE_FORMAT."'");
+ }
+
+ // looks like:
+ // Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production With the Partitioning option JServer Release 8.1.7.0.0 - Production
+ // $vers = OCIServerVersion($this->_connectionID);
+ // if (strpos($vers,'8i') !== false) $this->ansiOuter = true;
+ return true;
+ }
+
+ function ServerInfo()
+ {
+ $arr['compat'] = $this->GetOne('select value from sys.database_compatible_level');
+ $arr['description'] = @OCIServerVersion($this->_connectionID);
+ $arr['version'] = ADOConnection::_findvers($arr['description']);
+ return $arr;
+ }
+ // returns true or false
+ function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+ {
+ return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,1);
+ }
+
+ // returns true or false
+ function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
+ {
+ return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename,2);
+ }
+
+ function _affectedrows()
+ {
+ if (is_resource($this->_stmt)) return @OCIRowCount($this->_stmt);
+ return 0;
+ }
+
+ function IfNull( $field, $ifNull )
+ {
+ return " NVL($field, $ifNull) "; // if Oracle
+ }
+
+ // format and return date string in database date format
+ function DBDate($d)
+ {
+ if (empty($d) && $d !== 0) return 'null';
+
+ if (is_string($d)) $d = ADORecordSet::UnixDate($d);
+ return "TO_DATE(".adodb_date($this->fmtDate,$d).",'".$this->dateformat."')";
+ }
+
+ function BindDate($d)
+ {
+ $d = ADOConnection::DBDate($d);
+ if (strncmp($d,"'",1)) return $d;
+
+ return substr($d,1,strlen($d)-2);
+ }
+
+ function BindTimeStamp($d)
+ {
+ $d = ADOConnection::DBTimeStamp($d);
+ if (strncmp($d,"'",1)) return $d;
+
+ return substr($d,1,strlen($d)-2);
+ }
+
+ // format and return date string in database timestamp format
+ function DBTimeStamp($ts)
+ {
+ if (empty($ts) && $ts !== 0) return 'null';
+ if (is_string($ts)) $ts = ADORecordSet::UnixTimeStamp($ts);
+ return 'TO_DATE('.adodb_date("'Y-m-d H:i:s'",$ts).",'RRRR-MM-DD, HH24:MI:SS')";
+ }
+
+ function RowLock($tables,$where,$flds='1 as ignore')
+ {
+ if ($this->autoCommit) $this->BeginTrans();
+ return $this->GetOne("select $flds from $tables where $where for update");
+ }
+
+ function MetaTables($ttype=false,$showSchema=false,$mask=false)
+ {
+ if ($mask) {
+ $save = $this->metaTablesSQL;
+ $mask = $this->qstr(strtoupper($mask));
+ $this->metaTablesSQL .= " AND upper(table_name) like $mask";
+ }
+ $ret = ADOConnection::MetaTables($ttype,$showSchema);
+
+ if ($mask) {
+ $this->metaTablesSQL = $save;
+ }
+ return $ret;
+ }
+
+ // Mark Newnham
+ function MetaIndexes ($table, $primary = FALSE, $owner=false)
+ {
+ // save old fetch mode
+ global $ADODB_FETCH_MODE;
+
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+
+ if ($this->fetchMode !== FALSE) {
+ $savem = $this->SetFetchMode(FALSE);
+ }
+
+ // get index details
+ $table = strtoupper($table);
+
+ // get Primary index
+ $primary_key = '';
+
+ $false = false;
+ $rs = $this->Execute(sprintf("SELECT * FROM ALL_CONSTRAINTS WHERE UPPER(TABLE_NAME)='%s' AND CONSTRAINT_TYPE='P'",$table));
+ if ($row = $rs->FetchRow())
+ $primary_key = $row[1]; //constraint_name
+
+ if ($primary==TRUE && $primary_key=='') {
+ if (isset($savem))
+ $this->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+ return $false; //There is no primary key
+ }
+
+ $rs = $this->Execute(sprintf("SELECT ALL_INDEXES.INDEX_NAME, ALL_INDEXES.UNIQUENESS, ALL_IND_COLUMNS.COLUMN_POSITION, ALL_IND_COLUMNS.COLUMN_NAME FROM ALL_INDEXES,ALL_IND_COLUMNS WHERE UPPER(ALL_INDEXES.TABLE_NAME)='%s' AND ALL_IND_COLUMNS.INDEX_NAME=ALL_INDEXES.INDEX_NAME",$table));
+
+
+ if (!is_object($rs)) {
+ if (isset($savem))
+ $this->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+ return $false;
+ }
+
+ $indexes = array ();
+ // parse index data into array
+
+ while ($row = $rs->FetchRow()) {
+ if ($primary && $row[0] != $primary_key) continue;
+ if (!isset($indexes[$row[0]])) {
+ $indexes[$row[0]] = array(
+ 'unique' => ($row[1] == 'UNIQUE'),
+ 'columns' => array()
+ );
+ }
+ $indexes[$row[0]]['columns'][$row[2] - 1] = $row[3];
+ }
+
+ // sort columns by order in the index
+ foreach ( array_keys ($indexes) as $index ) {
+ ksort ($indexes[$index]['columns']);
+ }
+
+ if (isset($savem)) {
+ $this->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+ }
+ return $indexes;
+ }
+
+ function BeginTrans()
+ {
+ if ($this->transOff) return true;
+ $this->transCnt += 1;
+ $this->autoCommit = false;
+ $this->_commit = OCI_DEFAULT;
+
+ if ($this->_transmode) $this->Execute("SET TRANSACTION ".$this->_transmode);
+ return true;
+ }
+
+ function CommitTrans($ok=true)
+ {
+ if ($this->transOff) return true;
+ if (!$ok) return $this->RollbackTrans();
+
+ if ($this->transCnt) $this->transCnt -= 1;
+ $ret = OCIcommit($this->_connectionID);
+ $this->_commit = OCI_COMMIT_ON_SUCCESS;
+ $this->autoCommit = true;
+ return $ret;
+ }
+
+ function RollbackTrans()
+ {
+ if ($this->transOff) return true;
+ if ($this->transCnt) $this->transCnt -= 1;
+ $ret = OCIrollback($this->_connectionID);
+ $this->_commit = OCI_COMMIT_ON_SUCCESS;
+ $this->autoCommit = true;
+ return $ret;
+ }
+
+
+ function SelectDB($dbName)
+ {
+ return false;
+ }
+
+ function ErrorMsg()
+ {
+ if ($this->_errorMsg !== false) return $this->_errorMsg;
+
+ if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
+ if (empty($arr)) {
+ if (is_resource($this->_connectionID)) $arr = @OCIError($this->_connectionID);
+ else $arr = @OCIError();
+ if ($arr === false) return '';
+ }
+ $this->_errorMsg = $arr['message'];
+ $this->_errorCode = $arr['code'];
+ return $this->_errorMsg;
+ }
+
+ function ErrorNo()
+ {
+ if ($this->_errorCode !== false) return $this->_errorCode;
+
+ if (is_resource($this->_stmt)) $arr = @OCIError($this->_stmt);
+ if (empty($arr)) {
+ $arr = @OCIError($this->_connectionID);
+ if ($arr == false) $arr = @OCIError();
+ if ($arr == false) return '';
+ }
+
+ $this->_errorMsg = $arr['message'];
+ $this->_errorCode = $arr['code'];
+
+ return $arr['code'];
+ }
+
+ // Format date column in sql string given an input format that understands Y M D
+ function SQLDate($fmt, $col=false)
+ {
+ if (!$col) $col = $this->sysTimeStamp;
+ $s = 'TO_CHAR('.$col.",'";
+
+ $len = strlen($fmt);
+ for ($i=0; $i < $len; $i++) {
+ $ch = $fmt[$i];
+ switch($ch) {
+ case 'Y':
+ case 'y':
+ $s .= 'YYYY';
+ break;
+ case 'Q':
+ case 'q':
+ $s .= 'Q';
+ break;
+
+ case 'M':
+ $s .= 'Mon';
+ break;
+
+ case 'm':
+ $s .= 'MM';
+ break;
+ case 'D':
+ case 'd':
+ $s .= 'DD';
+ break;
+
+ case 'H':
+ $s.= 'HH24';
+ break;
+
+ case 'h':
+ $s .= 'HH';
+ break;
+
+ case 'i':
+ $s .= 'MI';
+ break;
+
+ case 's':
+ $s .= 'SS';
+ break;
+
+ case 'a':
+ case 'A':
+ $s .= 'AM';
+ break;
+
+ case 'w':
+ $s .= 'D';
+ break;
+
+ case 'l':
+ $s .= 'DAY';
+ break;
+
+ case 'W':
+ $s .= 'WW';
+ break;
+
+ default:
+ // handle escape characters...
+ if ($ch == '\\') {
+ $i++;
+ $ch = substr($fmt,$i,1);
+ }
+ if (strpos('-/.:;, ',$ch) !== false) $s .= $ch;
+ else $s .= '"'.$ch.'"';
+
+ }
+ }
+ return $s. "')";
+ }
+
+ function GetRandRow($sql, $arr = false)
+ {
+ $sql = "SELECT * FROM ($sql ORDER BY dbms_random.value) WHERE rownum = 1";
+
+ return $this->GetRow($sql,$arr);
+ }
+
+ /*
+ This algorithm makes use of
+
+ a. FIRST_ROWS hint
+ The FIRST_ROWS hint explicitly chooses the approach to optimize response time,
+ that is, minimum resource usage to return the first row. Results will be returned
+ as soon as they are identified.
+
+ b. Uses rownum tricks to obtain only the required rows from a given offset.
+ As this uses complicated sql statements, we only use this if the $offset >= 100.
+ This idea by Tomas V V Cox.
+
+ This implementation does not appear to work with oracle 8.0.5 or earlier. Comment
+ out this function then, and the slower SelectLimit() in the base class will be used.
+ */
+ function SelectLimit($sql,$nrows=-1,$offset=-1, $inputarr=false,$secs2cache=0)
+ {
+ // seems that oracle only supports 1 hint comment in 8i
+ if ($this->firstrows) {
+ if (strpos($sql,'/*+') !== false)
+ $sql = str_replace('/*+ ','/*+FIRST_ROWS ',$sql);
+ else
+ $sql = preg_replace('/^[ \t\n]*select/i','SELECT /*+FIRST_ROWS*/',$sql);
+ }
+
+ if ($offset < $this->selectOffsetAlg1 && 0 < $nrows && $nrows < 1000) {
+ if ($nrows > 0) {
+ if ($offset > 0) $nrows += $offset;
+ //$inputarr['adodb_rownum'] = $nrows;
+ if ($this->databaseType == 'oci8po') {
+ $sql = "select * from (".$sql.") where rownum <= ?";
+ } else {
+ $sql = "select * from (".$sql.") where rownum <= :adodb_offset";
+ }
+ $inputarr['adodb_offset'] = $nrows;
+ $nrows = -1;
+ }
+ // note that $nrows = 0 still has to work ==> no rows returned
+
+ $rs = ADOConnection::SelectLimit($sql,$nrows,$offset,$inputarr,$secs2cache);
+ return $rs;
+
+ } else {
+ // Algorithm by Tomas V V Cox, from PEAR DB oci8.php
+
+ // Let Oracle return the name of the columns
+ $q_fields = "SELECT * FROM (".$sql.") WHERE NULL = NULL";
+
+ $false = false;
+ if (! $stmt_arr = $this->Prepare($q_fields)) {
+ return $false;
+ }
+ $stmt = $stmt_arr[1];
+
+ if (is_array($inputarr)) {
+ foreach($inputarr as $k => $v) {
+ if (is_array($v)) {
+ if (sizeof($v) == 2) // suggested by g.giunta@libero.
+ OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
+ else
+ OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
+ } else {
+ $len = -1;
+ if ($v === ' ') $len = 1;
+ if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
+ $bindarr[$k] = $v;
+ } else { // dynamic sql, so rebind every time
+ OCIBindByName($stmt,":$k",$inputarr[$k],$len);
+ }
+ }
+ }
+ }
+
+ if (!OCIExecute($stmt, OCI_DEFAULT)) {
+ OCIFreeStatement($stmt);
+ return $false;
+ }
+
+ $ncols = OCINumCols($stmt);
+ for ( $i = 1; $i <= $ncols; $i++ ) {
+ $cols[] = '"'.OCIColumnName($stmt, $i).'"';
+ }
+ $result = false;
+
+ OCIFreeStatement($stmt);
+ $fields = implode(',', $cols);
+ $nrows += $offset;
+ $offset += 1; // in Oracle rownum starts at 1
+
+ if ($this->databaseType == 'oci8po') {
+ $sql = "SELECT $fields FROM".
+ "(SELECT rownum as adodb_rownum, $fields FROM".
+ " ($sql) WHERE rownum <= ?".
+ ") WHERE adodb_rownum >= ?";
+ } else {
+ $sql = "SELECT $fields FROM".
+ "(SELECT rownum as adodb_rownum, $fields FROM".
+ " ($sql) WHERE rownum <= :adodb_nrows".
+ ") WHERE adodb_rownum >= :adodb_offset";
+ }
+ $inputarr['adodb_nrows'] = $nrows;
+ $inputarr['adodb_offset'] = $offset;
+
+ if ($secs2cache>0) $rs = $this->CacheExecute($secs2cache, $sql,$inputarr);
+ else $rs = $this->Execute($sql,$inputarr);
+ return $rs;
+ }
+
+ }
+
+ /**
+ * Usage:
+ * Store BLOBs and CLOBs
+ *
+ * Example: to store $var in a blob
+ *
+ * $conn->Execute('insert into TABLE (id,ablob) values(12,empty_blob())');
+ * $conn->UpdateBlob('TABLE', 'ablob', $varHoldingBlob, 'ID=12', 'BLOB');
+ *
+ * $blobtype supports 'BLOB' and 'CLOB', but you need to change to 'empty_clob()'.
+ *
+ * to get length of LOB:
+ * select DBMS_LOB.GETLENGTH(ablob) from TABLE
+ *
+ * If you are using CURSOR_SHARING = force, it appears this will case a segfault
+ * under oracle 8.1.7.0. Run:
+ * $db->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
+ * before UpdateBlob() then...
+ */
+
+ function UpdateBlob($table,$column,$val,$where,$blobtype='BLOB')
+ {
+
+ //if (strlen($val) < 4000) return $this->Execute("UPDATE $table SET $column=:blob WHERE $where",array('blob'=>$val)) != false;
+
+ switch(strtoupper($blobtype)) {
+ default: ADOConnection::outp("<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
+ case 'BLOB': $type = OCI_B_BLOB; break;
+ case 'CLOB': $type = OCI_B_CLOB; break;
+ }
+
+ if ($this->databaseType == 'oci8po')
+ $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
+ else
+ $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
+
+ $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
+ $arr['blob'] = array($desc,-1,$type);
+ if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=EXACT');
+ $commit = $this->autoCommit;
+ if ($commit) $this->BeginTrans();
+ $rs = $this->_Execute($sql,$arr);
+ if ($rez = !empty($rs)) $desc->save($val);
+ $desc->free();
+ if ($commit) $this->CommitTrans();
+ if ($this->session_sharing_force_blob) $this->Execute('ALTER SESSION SET CURSOR_SHARING=FORCE');
+
+ if ($rez) $rs->Close();
+ return $rez;
+ }
+
+ /**
+ * Usage: store file pointed to by $var in a blob
+ */
+ function UpdateBlobFile($table,$column,$val,$where,$blobtype='BLOB')
+ {
+ switch(strtoupper($blobtype)) {
+ default: ADOConnection::outp( "<b>UpdateBlob</b>: Unknown blobtype=$blobtype"); return false;
+ case 'BLOB': $type = OCI_B_BLOB; break;
+ case 'CLOB': $type = OCI_B_CLOB; break;
+ }
+
+ if ($this->databaseType == 'oci8po')
+ $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO ?";
+ else
+ $sql = "UPDATE $table set $column=EMPTY_{$blobtype}() WHERE $where RETURNING $column INTO :blob";
+
+ $desc = OCINewDescriptor($this->_connectionID, OCI_D_LOB);
+ $arr['blob'] = array($desc,-1,$type);
+
+ $this->BeginTrans();
+ $rs = ADODB_oci8::Execute($sql,$arr);
+ if ($rez = !empty($rs)) $desc->savefile($val);
+ $desc->free();
+ $this->CommitTrans();
+
+ if ($rez) $rs->Close();
+ return $rez;
+ }
+
+ /**
+ * Execute SQL
+ *
+ * @param sql SQL statement to execute, or possibly an array holding prepared statement ($sql[0] will hold sql text)
+ * @param [inputarr] holds the input data to bind to. Null elements will be set to null.
+ * @return RecordSet or false
+ */
+ function Execute($sql,$inputarr=false)
+ {
+ if ($this->fnExecute) {
+ $fn = $this->fnExecute;
+ $ret = $fn($this,$sql,$inputarr);
+ if (isset($ret)) return $ret;
+ }
+ if ($inputarr) {
+ #if (!is_array($inputarr)) $inputarr = array($inputarr);
+
+ $element0 = reset($inputarr);
+
+ # is_object check because oci8 descriptors can be passed in
+ if (is_array($element0) && !is_object(reset($element0))) {
+ if (is_string($sql))
+ $stmt = $this->Prepare($sql);
+ else
+ $stmt = $sql;
+
+ foreach($inputarr as $arr) {
+ $ret = $this->_Execute($stmt,$arr);
+ if (!$ret) return $ret;
+ }
+ } else {
+ $ret = $this->_Execute($sql,$inputarr);
+ }
+
+ } else {
+ $ret = $this->_Execute($sql,false);
+ }
+
+ return $ret;
+ }
+
+ /*
+ Example of usage:
+
+ $stmt = $this->Prepare('insert into emp (empno, ename) values (:empno, :ename)');
+ */
+ function Prepare($sql,$cursor=false)
+ {
+ static $BINDNUM = 0;
+
+ $stmt = OCIParse($this->_connectionID,$sql);
+
+ if (!$stmt) {
+ $this->_errorMsg = false;
+ $this->_errorCode = false;
+ $arr = @OCIError($this->_connectionID);
+ if ($arr === false) return false;
+
+ $this->_errorMsg = $arr['message'];
+ $this->_errorCode = $arr['code'];
+ return false;
+ }
+
+ $BINDNUM += 1;
+
+ $sttype = @OCIStatementType($stmt);
+ if ($sttype == 'BEGIN' || $sttype == 'DECLARE') {
+ return array($sql,$stmt,0,$BINDNUM, ($cursor) ? OCINewCursor($this->_connectionID) : false);
+ }
+ return array($sql,$stmt,0,$BINDNUM);
+ }
+
+ /*
+ Call an oracle stored procedure and returns a cursor variable as a recordset.
+ Concept by Robert Tuttle robert@ud.com
+
+ Example:
+ Note: we return a cursor variable in :RS2
+ $rs = $db->ExecuteCursor("BEGIN adodb.open_tab(:RS2); END;",'RS2');
+
+ $rs = $db->ExecuteCursor(
+ "BEGIN :RS2 = adodb.getdata(:VAR1); END;",
+ 'RS2',
+ array('VAR1' => 'Mr Bean'));
+
+ */
+ function ExecuteCursor($sql,$cursorName='rs',$params=false)
+ {
+ if (is_array($sql)) $stmt = $sql;
+ else $stmt = ADODB_oci8::Prepare($sql,true); # true to allocate OCINewCursor
+
+ if (is_array($stmt) && sizeof($stmt) >= 5) {
+ $hasref = true;
+ $ignoreCur = false;
+ $this->Parameter($stmt, $ignoreCur, $cursorName, false, -1, OCI_B_CURSOR);
+ if ($params) {
+ foreach($params as $k => $v) {
+ $this->Parameter($stmt,$params[$k], $k);
+ }
+ }
+ } else
+ $hasref = false;
+
+ $rs = $this->Execute($stmt);
+ if ($rs) {
+ if ($rs->databaseType == 'array') OCIFreeCursor($stmt[4]);
+ else if ($hasref) $rs->_refcursor = $stmt[4];
+ }
+ return $rs;
+ }
+
+ /*
+ Bind a variable -- very, very fast for executing repeated statements in oracle.
+ Better than using
+ for ($i = 0; $i < $max; $i++) {
+ $p1 = ?; $p2 = ?; $p3 = ?;
+ $this->Execute("insert into table (col0, col1, col2) values (:0, :1, :2)",
+ array($p1,$p2,$p3));
+ }
+
+ Usage:
+ $stmt = $DB->Prepare("insert into table (col0, col1, col2) values (:0, :1, :2)");
+ $DB->Bind($stmt, $p1);
+ $DB->Bind($stmt, $p2);
+ $DB->Bind($stmt, $p3);
+ for ($i = 0; $i < $max; $i++) {
+ $p1 = ?; $p2 = ?; $p3 = ?;
+ $DB->Execute($stmt);
+ }
+
+ Some timings:
+ ** Test table has 3 cols, and 1 index. Test to insert 1000 records
+ Time 0.6081s (1644.60 inserts/sec) with direct OCIParse/OCIExecute
+ Time 0.6341s (1577.16 inserts/sec) with ADOdb Prepare/Bind/Execute
+ Time 1.5533s ( 643.77 inserts/sec) with pure SQL using Execute
+
+ Now if PHP only had batch/bulk updating like Java or PL/SQL...
+
+ Note that the order of parameters differs from OCIBindByName,
+ because we default the names to :0, :1, :2
+ */
+ function Bind(&$stmt,&$var,$size=4000,$type=false,$name=false,$isOutput=false)
+ {
+
+ if (!is_array($stmt)) return false;
+
+ if (($type == OCI_B_CURSOR) && sizeof($stmt) >= 5) {
+ return OCIBindByName($stmt[1],":".$name,$stmt[4],$size,$type);
+ }
+
+ if ($name == false) {
+ if ($type !== false) $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size,$type);
+ else $rez = OCIBindByName($stmt[1],":".$stmt[2],$var,$size); // +1 byte for null terminator
+ $stmt[2] += 1;
+ } else if (oci_lob_desc($type)) {
+ if ($this->debug) {
+ ADOConnection::outp("<b>Bind</b>: name = $name");
+ }
+ //we have to create a new Descriptor here
+ $numlob = count($this->_refLOBs);
+ $this->_refLOBs[$numlob]['LOB'] = OCINewDescriptor($this->_connectionID, oci_lob_desc($type));
+ $this->_refLOBs[$numlob]['TYPE'] = $isOutput;
+
+ $tmp = $this->_refLOBs[$numlob]['LOB'];
+ $rez = OCIBindByName($stmt[1], ":".$name, $tmp, -1, $type);
+ if ($this->debug) {
+ ADOConnection::outp("<b>Bind</b>: descriptor has been allocated, var (".$name.") binded");
+ }
+
+ // if type is input then write data to lob now
+ if ($isOutput == false) {
+ $var = $this->BlobEncode($var);
+ $tmp->WriteTemporary($var);
+ $this->_refLOBs[$numlob]['VAR'] = $var;
+ if ($this->debug) {
+ ADOConnection::outp("<b>Bind</b>: LOB has been written to temp");
+ }
+ } else {
+ $this->_refLOBs[$numlob]['VAR'] = $var;
+ }
+ $rez = $tmp;
+ } else {
+ if ($this->debug)
+ ADOConnection::outp("<b>Bind</b>: name = $name");
+
+ if ($type !== false) $rez = OCIBindByName($stmt[1],":".$name,$var,$size,$type);
+ else $rez = OCIBindByName($stmt[1],":".$name,$var,$size); // +1 byte for null terminator
+ }
+
+ return $rez;
+ }
+
+ function Param($name,$type=false)
+ {
+ return ':'.$name;
+ }
+
+ /*
+ Usage:
+ $stmt = $db->Prepare('select * from table where id =:myid and group=:group');
+ $db->Parameter($stmt,$id,'myid');
+ $db->Parameter($stmt,$group,'group');
+ $db->Execute($stmt);
+
+ @param $stmt Statement returned by Prepare() or PrepareSP().
+ @param $var PHP variable to bind to
+ @param $name Name of stored procedure variable name to bind to.
+ @param [$isOutput] Indicates direction of parameter 0/false=IN 1=OUT 2= IN/OUT. This is ignored in oci8.
+ @param [$maxLen] Holds an maximum length of the variable.
+ @param [$type] The data type of $var. Legal values depend on driver.
+
+ See OCIBindByName documentation at php.net.
+ */
+ function Parameter(&$stmt,&$var,$name,$isOutput=false,$maxLen=4000,$type=false)
+ {
+ if ($this->debug) {
+ $prefix = ($isOutput) ? 'Out' : 'In';
+ $ztype = (empty($type)) ? 'false' : $type;
+ ADOConnection::outp( "{$prefix}Parameter(\$stmt, \$php_var='$var', \$name='$name', \$maxLen=$maxLen, \$type=$ztype);");
+ }
+ return $this->Bind($stmt,$var,$maxLen,$type,$name,$isOutput);
+ }
+
+ /*
+ returns query ID if successful, otherwise false
+ this version supports:
+
+ 1. $db->execute('select * from table');
+
+ 2. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
+ $db->execute($prepared_statement, array(1,2,3));
+
+ 3. $db->execute('insert into table (a,b,c) values (:a,:b,:c)',array('a'=>1,'b'=>2,'c'=>3));
+
+ 4. $db->prepare('insert into table (a,b,c) values (:0,:1,:2)');
+ $db->bind($stmt,1); $db->bind($stmt,2); $db->bind($stmt,3);
+ $db->execute($stmt);
+ */
+ function _query($sql,$inputarr)
+ {
+ if (is_array($sql)) { // is prepared sql
+ $stmt = $sql[1];
+
+ // we try to bind to permanent array, so that OCIBindByName is persistent
+ // and carried out once only - note that max array element size is 4000 chars
+ if (is_array($inputarr)) {
+ $bindpos = $sql[3];
+ if (isset($this->_bind[$bindpos])) {
+ // all tied up already
+ $bindarr = $this->_bind[$bindpos];
+ } else {
+ // one statement to bind them all
+ $bindarr = array();
+ foreach($inputarr as $k => $v) {
+ $bindarr[$k] = $v;
+ OCIBindByName($stmt,":$k",$bindarr[$k],is_string($v) && strlen($v)>4000 ? -1 : 4000);
+ }
+ $this->_bind[$bindpos] = $bindarr;
+ }
+ }
+ } else {
+ $stmt=OCIParse($this->_connectionID,$sql);
+ }
+
+ $this->_stmt = $stmt;
+ if (!$stmt) return false;
+
+ if (defined('ADODB_PREFETCH_ROWS')) @OCISetPrefetch($stmt,ADODB_PREFETCH_ROWS);
+
+ if (is_array($inputarr)) {
+ foreach($inputarr as $k => $v) {
+ if (is_array($v)) {
+ if (sizeof($v) == 2) // suggested by g.giunta@libero.
+ OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1]);
+ else
+ OCIBindByName($stmt,":$k",$inputarr[$k][0],$v[1],$v[2]);
+
+ if ($this->debug==99) {
+ if (is_object($v[0]))
+ echo "name=:$k",' len='.$v[1],' type='.$v[2],'<br>';
+ else
+ echo "name=:$k",' var='.$inputarr[$k][0],' len='.$v[1],' type='.$v[2],'<br>';
+
+ }
+ } else {
+ $len = -1;
+ if ($v === ' ') $len = 1;
+ if (isset($bindarr)) { // is prepared sql, so no need to ocibindbyname again
+ $bindarr[$k] = $v;
+ } else { // dynamic sql, so rebind every time
+ OCIBindByName($stmt,":$k",$inputarr[$k],$len);
+ }
+ }
+ }
+ }
+
+ $this->_errorMsg = false;
+ $this->_errorCode = false;
+ if (OCIExecute($stmt,$this->_commit)) {
+//OCIInternalDebug(1);
+ if (count($this -> _refLOBs) > 0) {
+
+ foreach ($this -> _refLOBs as $key => $value) {
+ if ($this -> _refLOBs[$key]['TYPE'] == true) {
+ $tmp = $this -> _refLOBs[$key]['LOB'] -> load();
+ if ($this -> debug) {
+ ADOConnection::outp("<b>OUT LOB</b>: LOB has been loaded. <br>");
+ }
+ //$_GLOBALS[$this -> _refLOBs[$key]['VAR']] = $tmp;
+ $this -> _refLOBs[$key]['VAR'] = $tmp;
+ } else {
+ $this->_refLOBs[$key]['LOB']->save($this->_refLOBs[$key]['VAR']);
+ $this -> _refLOBs[$key]['LOB']->free();
+ unset($this -> _refLOBs[$key]);
+ if ($this->debug) {
+ ADOConnection::outp("<b>IN LOB</b>: LOB has been saved. <br>");
+ }
+ }
+ }
+ }
+
+ switch (@OCIStatementType($stmt)) {
+ case "SELECT":
+ return $stmt;
+
+ case 'DECLARE':
+ case "BEGIN":
+ if (is_array($sql) && !empty($sql[4])) {
+ $cursor = $sql[4];
+ if (is_resource($cursor)) {
+ $ok = OCIExecute($cursor);
+ return $cursor;
+ }
+ return $stmt;
+ } else {
+ if (is_resource($stmt)) {
+ OCIFreeStatement($stmt);
+ return true;
+ }
+ return $stmt;
+ }
+ break;
+ default :
+ // ociclose -- no because it could be used in a LOB?
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // returns true or false
+ function _close()
+ {
+ if (!$this->_connectionID) return;
+
+ if (!$this->autoCommit) OCIRollback($this->_connectionID);
+ if (count($this->_refLOBs) > 0) {
+ foreach ($this ->_refLOBs as $key => $value) {
+ $this->_refLOBs[$key]['LOB']->free();
+ unset($this->_refLOBs[$key]);
+ }
+ }
+ OCILogoff($this->_connectionID);
+
+ $this->_stmt = false;
+ $this->_connectionID = false;
+ }
+
+ function MetaPrimaryKeys($table, $owner=false,$internalKey=false)
+ {
+ if ($internalKey) return array('ROWID');
+
+ // tested with oracle 8.1.7
+ $table = strtoupper($table);
+ if ($owner) {
+ $owner_clause = "AND ((a.OWNER = b.OWNER) AND (a.OWNER = UPPER('$owner')))";
+ $ptab = 'ALL_';
+ } else {
+ $owner_clause = '';
+ $ptab = 'USER_';
+ }
+ $sql = "
+SELECT /*+ RULE */ distinct b.column_name
+ FROM {$ptab}CONSTRAINTS a
+ , {$ptab}CONS_COLUMNS b
+ WHERE ( UPPER(b.table_name) = ('$table'))
+ AND (UPPER(a.table_name) = ('$table') and a.constraint_type = 'P')
+ $owner_clause
+ AND (a.constraint_name = b.constraint_name)";
+
+ $rs = $this->Execute($sql);
+ if ($rs && !$rs->EOF) {
+ $arr = $rs->GetArray();
+ $a = array();
+ foreach($arr as $v) {
+ $a[] = reset($v);
+ }
+ return $a;
+ }
+ else return false;
+ }
+
+ // http://gis.mit.edu/classes/11.521/sqlnotes/referential_integrity.html
+ function MetaForeignKeys($table, $owner=false)
+ {
+ global $ADODB_FETCH_MODE;
+
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ $table = $this->qstr(strtoupper($table));
+ if (!$owner) {
+ $owner = $this->user;
+ $tabp = 'user_';
+ } else
+ $tabp = 'all_';
+
+ $owner = ' and owner='.$this->qstr(strtoupper($owner));
+
+ $sql =
+"select constraint_name,r_owner,r_constraint_name
+ from {$tabp}constraints
+ where constraint_type = 'R' and table_name = $table $owner";
+
+ $constraints = $this->GetArray($sql);
+ $arr = false;
+ foreach($constraints as $constr) {
+ $cons = $this->qstr($constr[0]);
+ $rowner = $this->qstr($constr[1]);
+ $rcons = $this->qstr($constr[2]);
+ $cols = $this->GetArray("select column_name from {$tabp}cons_columns where constraint_name=$cons $owner order by position");
+ $tabcol = $this->GetArray("select table_name,column_name from {$tabp}cons_columns where owner=$rowner and constraint_name=$rcons order by position");
+
+ if ($cols && $tabcol)
+ for ($i=0, $max=sizeof($cols); $i < $max; $i++) {
+ $arr[$tabcol[$i][0]] = $cols[$i][0].'='.$tabcol[$i][1];
+ }
+ }
+ $ADODB_FETCH_MODE = $save;
+
+ return $arr;
+ }
+
+
+ function CharMax()
+ {
+ return 4000;
+ }
+
+ function TextMax()
+ {
+ return 4000;
+ }
+
+ /**
+ * Quotes a string.
+ * An example is $db->qstr("Don't bother",magic_quotes_runtime());
+ *
+ * @param s the string to quote
+ * @param [magic_quotes] if $s is GET/POST var, set to get_magic_quotes_gpc().
+ * This undoes the stupidity of magic quotes for GPC.
+ *
+ * @return quoted string to be sent back to database
+ */
+ function qstr($s,$magic_quotes=false)
+ {
+ //$nofixquotes=false;
+
+ if ($this->noNullStrings && strlen($s)==0)$s = ' ';
+ if (!$magic_quotes) {
+ if ($this->replaceQuote[0] == '\\'){
+ $s = str_replace('\\','\\\\',$s);
+ }
+ return "'".str_replace("'",$this->replaceQuote,$s)."'";
+ }
+
+ // undo magic quotes for "
+ $s = str_replace('\\"','"',$s);
+
+ $s = str_replace('\\\\','\\',$s);
+ return "'".str_replace("\\'",$this->replaceQuote,$s)."'";
+
+ }
+
+}
+
+/*--------------------------------------------------------------------------------------
+ Class Name: Recordset
+--------------------------------------------------------------------------------------*/
+
+class ADORecordset_oci8 extends ADORecordSet {
+
+ var $databaseType = 'oci8';
+ var $bind=false;
+ var $_fieldobjs;
+
+ //var $_arr = false;
+
+ function ADORecordset_oci8($queryID,$mode=false)
+ {
+ if ($mode === false) {
+ global $ADODB_FETCH_MODE;
+ $mode = $ADODB_FETCH_MODE;
+ }
+ switch ($mode)
+ {
+ case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+ case ADODB_FETCH_DEFAULT:
+ case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+ case ADODB_FETCH_NUM:
+ default:
+ $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+ }
+
+ $this->adodbFetchMode = $mode;
+ $this->_queryID = $queryID;
+ }
+
+
+ function Init()
+ {
+ if ($this->_inited) return;
+
+ $this->_inited = true;
+ if ($this->_queryID) {
+
+ $this->_currentRow = 0;
+ @$this->_initrs();
+ $this->EOF = !$this->_fetch();
+
+ /*
+ // based on idea by Gaetano Giunta to detect unusual oracle errors
+ // see http://phplens.com/lens/lensforum/msgs.php?id=6771
+ $err = OCIError($this->_queryID);
+ if ($err && $this->connection->debug) ADOConnection::outp($err);
+ */
+
+ if (!is_array($this->fields)) {
+ $this->_numOfRows = 0;
+ $this->fields = array();
+ }
+ } else {
+ $this->fields = array();
+ $this->_numOfRows = 0;
+ $this->_numOfFields = 0;
+ $this->EOF = true;
+ }
+ }
+
+ function _initrs()
+ {
+ $this->_numOfRows = -1;
+ $this->_numOfFields = OCInumcols($this->_queryID);
+ if ($this->_numOfFields>0) {
+ $this->_fieldobjs = array();
+ $max = $this->_numOfFields;
+ for ($i=0;$i<$max; $i++) $this->_fieldobjs[] = $this->_FetchField($i);
+ }
+ }
+
+ /* Returns: an object containing field information.
+ Get column information in the Recordset object. fetchField() can be used in order to obtain information about
+ fields in a certain query result. If the field offset isn't specified, the next field that wasn't yet retrieved by
+ fetchField() is retrieved. */
+
+ function _FetchField($fieldOffset = -1)
+ {
+ $fld = new ADOFieldObject;
+ $fieldOffset += 1;
+ $fld->name =OCIcolumnname($this->_queryID, $fieldOffset);
+ $fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
+ $fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
+ switch($fld->type) {
+ case 'NUMBER':
+ $p = OCIColumnPrecision($this->_queryID, $fieldOffset);
+ $sc = OCIColumnScale($this->_queryID, $fieldOffset);
+ if ($p != 0 && $sc == 0) $fld->type = 'INT';
+ break;
+
+ case 'CLOB':
+ case 'NCLOB':
+ case 'BLOB':
+ $fld->max_length = -1;
+ break;
+ }
+ return $fld;
+ }
+
+ /* For some reason, OCIcolumnname fails when called after _initrs() so we cache it */
+ function FetchField($fieldOffset = -1)
+ {
+ return $this->_fieldobjs[$fieldOffset];
+ }
+
+
+ /*
+ // 10% speedup to move MoveNext to child class
+ function _MoveNext()
+ {
+ //global $ADODB_EXTENSION;if ($ADODB_EXTENSION) return @adodb_movenext($this);
+
+ if ($this->EOF) return false;
+
+ $this->_currentRow++;
+ if(@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode))
+ return true;
+ $this->EOF = true;
+
+ return false;
+ } */
+
+
+ function MoveNext()
+ {
+ if (@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) {
+ $this->_currentRow += 1;
+ return true;
+ }
+ if (!$this->EOF) {
+ $this->_currentRow += 1;
+ $this->EOF = true;
+ }
+ return false;
+ }
+
+ /*
+ # does not work as first record is retrieved in _initrs(), so is not included in GetArray()
+ function GetArray($nRows = -1)
+ {
+ global $ADODB_OCI8_GETARRAY;
+
+ if (true || !empty($ADODB_OCI8_GETARRAY)) {
+ # does not support $ADODB_ANSI_PADDING_OFF
+
+ //OCI_RETURN_NULLS and OCI_RETURN_LOBS is set by OCIfetchstatement
+ switch($this->adodbFetchMode) {
+ case ADODB_FETCH_NUM:
+
+ $ncols = @OCIfetchstatement($this->_queryID, $results, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW+OCI_NUM);
+ $results = array_merge(array($this->fields),$results);
+ return $results;
+
+ case ADODB_FETCH_ASSOC:
+ if (ADODB_ASSOC_CASE != 2 || $this->databaseType != 'oci8') break;
+
+ $ncols = @OCIfetchstatement($this->_queryID, $assoc, 0, $nRows, OCI_FETCHSTATEMENT_BY_ROW);
+ $results = array_merge(array($this->fields),$assoc);
+ return $results;
+
+ default:
+ break;
+ }
+ }
+
+ $results = ADORecordSet::GetArray($nRows);
+ return $results;
+
+ } */
+
+ /* Optimize SelectLimit() by using OCIFetch() instead of OCIFetchInto() */
+ function GetArrayLimit($nrows,$offset=-1)
+ {
+ if ($offset <= 0) {
+ $arr = $this->GetArray($nrows);
+ return $arr;
+ }
+ $arr = array();
+ for ($i=1; $i < $offset; $i++)
+ if (!@OCIFetch($this->_queryID)) return $arr;
+
+ if (!@OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode)) return $arr;;
+ $results = array();
+ $cnt = 0;
+ while (!$this->EOF && $nrows != $cnt) {
+ $results[$cnt++] = $this->fields;
+ $this->MoveNext();
+ }
+
+ return $results;
+ }
+
+
+ /* Use associative array to get fields array */
+ function Fields($colname)
+ {
+ if (!$this->bind) {
+ $this->bind = array();
+ for ($i=0; $i < $this->_numOfFields; $i++) {
+ $o = $this->FetchField($i);
+ $this->bind[strtoupper($o->name)] = $i;
+ }
+ }
+
+ return $this->fields[$this->bind[strtoupper($colname)]];
+ }
+
+
+
+ function _seek($row)
+ {
+ return false;
+ }
+
+ function _fetch()
+ {
+ return @OCIfetchinto($this->_queryID,$this->fields,$this->fetchMode);
+ }
+
+ /* close() only needs to be called if you are worried about using too much memory while your script
+ is running. All associated result memory for the specified result identifier will automatically be freed. */
+
+ function _close()
+ {
+ if ($this->connection->_stmt === $this->_queryID) $this->connection->_stmt = false;
+ if (!empty($this->_refcursor)) {
+ OCIFreeCursor($this->_refcursor);
+ $this->_refcursor = false;
+ }
+ @OCIFreeStatement($this->_queryID);
+ $this->_queryID = false;
+
+ }
+
+ function MetaType($t,$len=-1)
+ {
+ if (is_object($t)) {
+ $fieldobj = $t;
+ $t = $fieldobj->type;
+ $len = $fieldobj->max_length;
+ }
+ switch (strtoupper($t)) {
+ case 'VARCHAR':
+ case 'VARCHAR2':
+ case 'CHAR':
+ case 'VARBINARY':
+ case 'BINARY':
+ case 'NCHAR':
+ case 'NVARCHAR':
+ case 'NVARCHAR2':
+ if (isset($this) && $len <= $this->blobSize) return 'C';
+
+ case 'NCLOB':
+ case 'LONG':
+ case 'LONG VARCHAR':
+ case 'CLOB':
+ return 'X';
+
+ case 'LONG RAW':
+ case 'LONG VARBINARY':
+ case 'BLOB':
+ return 'B';
+
+ case 'DATE':
+ return ($this->connection->datetime) ? 'T' : 'D';
+
+
+ case 'TIMESTAMP': return 'T';
+
+ case 'INT':
+ case 'SMALLINT':
+ case 'INTEGER':
+ return 'I';
+
+ default: return 'N';
+ }
+ }
+}
+
+class ADORecordSet_ext_oci8 extends ADORecordSet_oci8 {
+ function ADORecordSet_ext_oci8($queryID,$mode=false)
+ {
+ if ($mode === false) {
+ global $ADODB_FETCH_MODE;
+ $mode = $ADODB_FETCH_MODE;
+ }
+ switch ($mode)
+ {
+ case ADODB_FETCH_ASSOC:$this->fetchMode = OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+ case ADODB_FETCH_DEFAULT:
+ case ADODB_FETCH_BOTH:$this->fetchMode = OCI_NUM+OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+ case ADODB_FETCH_NUM:
+ default: $this->fetchMode = OCI_NUM+OCI_RETURN_NULLS+OCI_RETURN_LOBS; break;
+ }
+ $this->adodbFetchMode = $mode;
+ $this->_queryID = $queryID;
+ }
+
+ function MoveNext()
+ {
+ return adodb_movenext($this);
+ }
+}
+?>
<?php
/**
- * @version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim. All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim. 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.
}
// emulate handling of parameters ? ?, replacing with :bind0 :bind1
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
if (is_array($inputarr)) {
$i = 0;
{
$fld = new ADOFieldObject;
$fieldOffset += 1;
- $fld->name = strtolower(OCIcolumnname($this->_queryID, $fieldOffset));
+ $fld->name = OCIcolumnname($this->_queryID, $fieldOffset);
+ if (ADODB_ASSOC_CASE == 0) $fld->name = strtolower($fld->name);
$fld->type = OCIcolumntype($this->_queryID, $fieldOffset);
$fld->max_length = OCIcolumnsize($this->_queryID, $fieldOffset);
if ($fld->type == 'NUMBER') {
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
return $indexes;
}
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
if (is_string($sql)) $sql = str_replace('||','+',$sql);
return ADODB_odbc::_query($sql,$inputarr);
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
function ErrorMsg()
{
+ if ($this->_errorMsg !== false) return $this->_errorMsg;
if (empty($this->_connectionID)) return @odbtp_last_error();
return @odbtp_last_error($this->_connectionID);
}
function ErrorNo()
{
+ if ($this->_errorCode !== false) return $this->_errorCode;
if (empty($this->_connectionID)) return @odbtp_last_error_state();
return @odbtp_last_error_state($this->_connectionID);
}
-
+/*
+ function DBDate($d,$isfld=false)
+ {
+ if (empty($d) && $d !== 0) return 'null';
+ if ($isfld) return "convert(date, $d, 120)";
+
+ if (is_string($d)) $d = ADORecordSet::UnixDate($d);
+ $d = adodb_date($this->fmtDate,$d);
+ return "convert(date, $d, 120)";
+ }
+
+ function DBTimeStamp($d,$isfld=false)
+ {
+ if (empty($d) && $d !== 0) return 'null';
+ if ($isfld) return "convert(datetime, $d, 120)";
+
+ if (is_string($d)) $d = ADORecordSet::UnixDate($d);
+ $d = adodb_date($this->fmtDate,$d);
+ return "convert(datetime, $d, 120)";
+ }
+*/
+
function _insertid()
{
// SCOPE_IDENTITY()
function Prepare($sql)
{
if (! $this->_bindInputArray) return $sql; // no binding
+
+ $this->_errorMsg = false;
+ $this->_errorCode = false;
+
$stmt = @odbtp_prepare($sql,$this->_connectionID);
if (!$stmt) {
// print "Prepare Error for ($sql) ".$this->ErrorMsg()."<br>";
{
if (!$this->_canPrepareSP) return $sql; // Can't prepare procedures
+ $this->_errorMsg = false;
+ $this->_errorCode = false;
+
$stmt = @odbtp_prepare_proc($sql,$this->_connectionID);
if (!$stmt) return false;
return array($sql,$stmt);
return @odbtp_execute( $stmt ) != false;
}
+ function MetaIndexes($table,$primary=false)
+ {
+ switch ( $this->odbc_driver) {
+ case ODB_DRIVER_MSSQL:
+ return $this->MetaIndexes_mssql($table, $primary);
+ default:
+ return array();
+ }
+ }
+
+ function MetaIndexes_mssql($table,$primary=false)
+ {
+ $table = strtolower($this->qstr($table));
+
+ $sql = "SELECT i.name AS ind_name, C.name AS col_name, USER_NAME(O.uid) AS Owner, c.colid, k.Keyno,
+ CASE WHEN I.indid BETWEEN 1 AND 254 AND (I.status & 2048 = 2048 OR I.Status = 16402 AND O.XType = 'V') THEN 1 ELSE 0 END AS IsPK,
+ CASE WHEN I.status & 2 = 2 THEN 1 ELSE 0 END AS IsUnique
+ FROM dbo.sysobjects o INNER JOIN dbo.sysindexes I ON o.id = i.id
+ INNER JOIN dbo.sysindexkeys K ON I.id = K.id AND I.Indid = K.Indid
+ INNER JOIN dbo.syscolumns c ON K.id = C.id AND K.colid = C.Colid
+ WHERE LEFT(i.name, 8) <> '_WA_Sys_' AND o.status >= 0 AND lower(O.Name) = $table
+ ORDER BY O.name, I.Name, K.keyno";
+
+ global $ADODB_FETCH_MODE;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($this->fetchMode !== FALSE) {
+ $savem = $this->SetFetchMode(FALSE);
+ }
+
+ $rs = $this->Execute($sql);
+ if (isset($savem)) {
+ $this->SetFetchMode($savem);
+ }
+ $ADODB_FETCH_MODE = $save;
+
+ if (!is_object($rs)) {
+ return FALSE;
+ }
+
+ $indexes = array();
+ while ($row = $rs->FetchRow()) {
+ if ($primary && !$row[5]) continue;
+
+ $indexes[$row[0]]['unique'] = $row[6];
+ $indexes[$row[0]]['columns'][] = $row[1];
+ }
+ return $indexes;
+ }
+
function IfNull( $field, $ifNull )
{
switch( $this->odbc_driver ) {
{
global $php_errormsg;
+ $this->_errorMsg = false;
+ $this->_errorCode = false;
+
if ($inputarr) {
if (is_array($sql)) {
$stmtid = $sql[1];
}
}
$num_params = @odbtp_num_params( $stmtid );
+ /*
for( $param = 1; $param <= $num_params; $param++ ) {
@odbtp_input( $stmtid, $param );
@odbtp_set( $stmtid, $param, $inputarr[$param-1] );
+ }*/
+
+ $param = 1;
+ foreach($inputarr as $v) {
+ @odbtp_input( $stmtid, $param );
+ @odbtp_set( $stmtid, $param, $v );
+ $param += 1;
+ if ($param > $num_params) break;
}
+
if (!@odbtp_execute($stmtid) ) {
return false;
}
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
case 'mysql':
case 'pgsql':
case 'mssql':
+ case 'sqlite':
include_once(ADODB_DIR.'/drivers/adodb-pdo_'.$this->dsnType.'.inc.php');
break;
}
return false;
}
+ function Concat()
+ {
+ $args = func_get_args();
+ if(method_exists($this->_driver, 'Concat'))
+ return call_user_func_array(array($this->_driver, 'Concat'), $args);
+
+ return call_user_func_array(array($this,'parent::Concat'), $args);
+ }
+
// returns true or false
function _pconnect($argDSN, $argUsername, $argPassword, $argDatabasename)
{
else $obj->bindParam($name, $var);
}
+ function OffsetDate($dayFraction,$date=false)
+ {
+ return $this->_driver->OffsetDate($dayFraction,$date);
+ }
function ErrorMsg()
{
return $err;
}
+ function SetTransactionMode($transaction_mode)
+ {
+ if(method_exists($this->_driver, 'SetTransactionMode'))
+ return $this->_driver->SetTransactionMode($transaction_mode);
+
+ return parent::SetTransactionMode($seqname);
+ }
+
function BeginTrans()
{
+ if(method_exists($this->_driver, 'BeginTrans'))
+ return $this->_driver->BeginTrans();
+
if (!$this->hasTransactions) return false;
if ($this->transOff) return true;
$this->transCnt += 1;
function CommitTrans($ok=true)
{
+ if(method_exists($this->_driver, 'CommitTrans'))
+ return $this->_driver->CommitTrans($ok);
+
if (!$this->hasTransactions) return false;
if ($this->transOff) return true;
if (!$ok) return $this->RollbackTrans();
function RollbackTrans()
{
+ if(method_exists($this->_driver, 'RollbackTrans'))
+ return $this->_driver->RollbackTrans();
+
if (!$this->hasTransactions) return false;
if ($this->transOff) return true;
if ($this->transCnt) $this->transCnt -= 1;
return $obj;
}
+ function CreateSequence($seqname='adodbseq',$startID=1)
+ {
+ if(method_exists($this->_driver, 'CreateSequence'))
+ return $this->_driver->CreateSequence($seqname, $startID);
+
+ return parent::CreateSequence($seqname, $startID);
+ }
+
+ function DropSequence($seqname='adodbseq')
+ {
+ if(method_exists($this->_driver, 'DropSequence'))
+ return $this->_driver->DropSequence($seqname);
+
+ return parent::DropSequence($seqname);
+ }
+
+ function GenID($seqname='adodbseq',$startID=1)
+ {
+ if(method_exists($this->_driver, 'GenID'))
+ return $this->_driver->GenID($seqname, $startID);
+
+ return parent::GenID($seqname, $startID);
+ }
+
/* returns queryID or false */
function _query($sql,$inputarr=false)
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
$this->Execute("SET TRANSACTION ".$transaction_mode);
}
- function MetaTables()
+ function MetaTables($ttype=false,$showSchema=false,$mask=false)
{
return false;
}
- function MetaColumns()
+ function MetaColumns($table,$normalize=true)
{
return false;
}
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
var $hasGenID = true;
var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
var $_dropSeqSQL = "drop table %s";
-
+ var $fmtTimeStamp = "'Y-m-d, H:i:s'";
var $nameQuote = '`';
function _init($parentDriver)
{
$parentDriver->hasTransactions = false;
- $parentDriver->_bindInputArray = false;
+ #$parentDriver->_bindInputArray = false;
$parentDriver->hasInsertID = true;
$parentDriver->_connectionID->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY,true);
}
// return "from_unixtime(unix_timestamp($date)+$fraction)";
}
+ function Concat()
+ {
+ $s = "";
+ $arr = func_get_args();
+
+ // suggestion by andrew005#mnogo.ru
+ $s = implode(',',$arr);
+ if (strlen($s) > 0) return "CONCAT($s)"; return '';
+ }
+
function ServerInfo()
{
$arr['description'] = ADOConnection::GetOne("select version()");
$this->Execute("SET SESSION TRANSACTION ".$transaction_mode);
}
- function MetaColumns($table)
+ function MetaColumns($table,$normalize=true)
{
$this->_findschema($table,$schema);
if ($schema) {
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
return $ret;
}
- function MetaColumns($table)
+ function MetaColumns($table,$normalize=true)
{
global $ADODB_FETCH_MODE;
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
--- /dev/null
+<?php
+
+/*
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
+ Set tabs to 4 for best viewing.
+
+ Latest version is available at http://adodb.sourceforge.net
+
+ Thanks Diogo Toscano (diogo#scriptcase.net) for the code.
+ And also Sid Dunayer [sdunayer#interserv.com] for extensive fixes.
+*/
+
+class ADODB_pdo_sqlite extends ADODB_pdo {
+ var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table'";
+ var $sysDate = 'current_date';
+ var $sysTimeStamp = 'current_timestamp';
+ var $nameQuote = '`';
+ var $replaceQuote = "''";
+ var $hasGenID = true;
+ var $_genIDSQL = "UPDATE %s SET id=id+1 WHERE id=%s";
+ var $_genSeqSQL = "CREATE TABLE %s (id integer)";
+ var $_genSeqCountSQL = 'SELECT COUNT(*) FROM %s';
+ var $_genSeq2SQL = 'INSERT INTO %s VALUES(%s)';
+ var $_dropSeqSQL = 'DROP TABLE %s';
+ var $concat_operator = '||';
+ var $pdoDriver = false;
+
+ function _init($parentDriver)
+ {
+ $this->pdoDriver = $parentDriver;
+ $parentDriver->_bindInputArray = true;
+ $parentDriver->hasTransactions = true;
+ $parentDriver->hasInsertID = true;
+ }
+
+ function ServerInfo()
+ {
+ $parent = $this->pdoDriver;
+ @($ver = array_pop($parent->GetCol("SELECT sqlite_version()")));
+ @($end = array_pop($parent->GetCol("PRAGMA encoding")));
+
+ $arr['version'] = $ver;
+ $arr['description'] = 'SQLite ';
+ $arr['encoding'] = $enc;
+
+ return $arr;
+ }
+
+ function SelectLimit($sql,$nrows=-1,$offset=-1,$inputarr=false,$secs2cache=0)
+ {
+ $parent = $this->pdoDriver;
+ $offsetStr = ($offset >= 0) ? " OFFSET $offset" : '';
+ $limitStr = ($nrows >= 0) ? " LIMIT $nrows" : ($offset >= 0 ? ' LIMIT 999999999' : '');
+ if ($secs2cache)
+ $rs = $parent->CacheExecute($secs2cache,$sql."$limitStr$offsetStr",$inputarr);
+ else
+ $rs = $parent->Execute($sql."$limitStr$offsetStr",$inputarr);
+
+ return $rs;
+ }
+
+ function GenID($seq='adodbseq',$start=1)
+ {
+ $parent = $this->pdoDriver;
+ // if you have to modify the parameter below, your database is overloaded,
+ // or you need to implement generation of id's yourself!
+ $MAXLOOPS = 100;
+ while (--$MAXLOOPS>=0) {
+ @($num = array_pop($parent->GetCol("SELECT id FROM {$seq}")));
+ if ($num === false || !is_numeric($num)) {
+ @$parent->Execute(sprintf($this->_genSeqSQL ,$seq));
+ $start -= 1;
+ $num = '0';
+ $cnt = $parent->GetOne(sprintf($this->_genSeqCountSQL,$seq));
+ if (!$cnt) {
+ $ok = $parent->Execute(sprintf($this->_genSeq2SQL,$seq,$start));
+ }
+ if (!$ok) return false;
+ }
+ $parent->Execute(sprintf($this->_genIDSQL,$seq,$num));
+
+ if ($parent->affected_rows() > 0) {
+ $num += 1;
+ $parent->genID = intval($num);
+ return intval($num);
+ }
+ }
+ if ($fn = $parent->raiseErrorFn) {
+ $fn($parent->databaseType,'GENID',-32000,"Unable to generate unique id after $MAXLOOPS attempts",$seq,$num);
+ }
+ return false;
+ }
+
+ function CreateSequence($seqname='adodbseq',$start=1)
+ {
+ $parent = $this->pdoDriver;
+ $ok = $parent->Execute(sprintf($this->_genSeqSQL,$seqname));
+ if (!$ok) return false;
+ $start -= 1;
+ return $parent->Execute("insert into $seqname values($start)");
+ }
+
+ function SetTransactionMode($transaction_mode)
+ {
+ $parent = $this->pdoDriver;
+ $parent->_transmode = strtoupper($transaction_mode);
+ }
+
+ function BeginTrans()
+ {
+ $parent = $this->pdoDriver;
+ if ($parent->transOff) return true;
+ $parent->transCnt += 1;
+ $parent->_autocommit = false;
+ return $parent->Execute("BEGIN {$parent->_transmode}");
+ }
+
+ function CommitTrans($ok=true)
+ {
+ $parent = $this->pdoDriver;
+ if ($parent->transOff) return true;
+ if (!$ok) return $parent->RollbackTrans();
+ if ($parent->transCnt) $parent->transCnt -= 1;
+ $parent->_autocommit = true;
+
+ $ret = $parent->Execute('COMMIT');
+ return $ret;
+ }
+
+ function RollbackTrans()
+ {
+ $parent = $this->pdoDriver;
+ if ($parent->transOff) return true;
+ if ($parent->transCnt) $parent->transCnt -= 1;
+ $parent->_autocommit = true;
+
+ $ret = $parent->Execute('ROLLBACK');
+ return $ret;
+ }
+
+
+ // mark newnham
+ function MetaColumns($tab,$normalize=true))
+ {
+ global $ADODB_FETCH_MODE;
+
+ $parent = $this->pdoDriver;
+ $false = false;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
+ if ($parent->fetchMode !== false) $savem = $parent->SetFetchMode(false);
+ $rs = $parent->Execute("PRAGMA table_info('$tab')");
+ if (isset($savem)) $parent->SetFetchMode($savem);
+ if (!$rs) {
+ $ADODB_FETCH_MODE = $save;
+ return $false;
+ }
+ $arr = array();
+ while ($r = $rs->FetchRow()) {
+ $type = explode('(',$r['type']);
+ $size = '';
+ if (sizeof($type)==2)
+ $size = trim($type[1],')');
+ $fn = strtoupper($r['name']);
+ $fld = new ADOFieldObject;
+ $fld->name = $r['name'];
+ $fld->type = $type[0];
+ $fld->max_length = $size;
+ $fld->not_null = $r['notnull'];
+ $fld->primary_key = $r['pk'];
+ $fld->default_value = $r['dflt_value'];
+ $fld->scale = 0;
+ if ($save == ADODB_FETCH_NUM) $arr[] = $fld;
+ else $arr[strtoupper($fld->name)] = $fld;
+ }
+ $rs->Close();
+ $ADODB_FETCH_MODE = $save;
+ return $arr;
+ }
+
+ function MetaTables($ttype=false,$showSchema=false,$mask=false)
+ {
+ $parent = $this->pdoDriver;
+ return $parent->GetCol($this->metaTablesSQL);
+ }
+}
+?>
\ No newline at end of file
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
// if magic quotes disabled, use pg_escape_string()
function qstr($s,$magic_quotes=false)
{
+ if (is_bool($s)) return $s ? 'true' : 'false';
+
if (!$magic_quotes) {
if (ADODB_PHPVER >= 0x5200) {
return "'".pg_escape_string($this->_connectionID,$s)."'";
// returns queryID or false
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
$this->_errorMsg = false;
if ($inputarr) {
function _decode($blob)
{
+ if ($blob === NULL) return NULL;
eval('$realblob="'.adodb_str_replace(array('"','$'),array('\"','\$'),$blob).'";');
return $realblob;
}
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
}
*/
-
- // from Edward Jaramilla, improved version - works on pg 7.4
+ /*
+ I discovered that the MetaForeignKeys method no longer worked for Postgres 8.3.
+ I went ahead and modified it to work for both 8.2 and 8.3.
+ Please feel free to include this change in your next release of adodb.
+ William Kolodny [William.Kolodny#gt-t.net]
+ */
function MetaForeignKeys($table, $owner=false, $upper=false)
+ {
+ $sql="
+ SELECT fum.ftblname AS lookup_table, split_part(fum.rf, ')'::text, 1) AS lookup_field,
+ fum.ltable AS dep_table, split_part(fum.lf, ')'::text, 1) AS dep_field
+ FROM (
+ SELECT fee.ltable, fee.ftblname, fee.consrc, split_part(fee.consrc,'('::text, 2) AS lf,
+ split_part(fee.consrc, '('::text, 3) AS rf
+ FROM (
+ SELECT foo.relname AS ltable, foo.ftblname,
+ pg_get_constraintdef(foo.oid) AS consrc
+ FROM (
+ SELECT c.oid, c.conname AS name, t.relname, ft.relname AS ftblname
+ FROM pg_constraint c
+ JOIN pg_class t ON (t.oid = c.conrelid)
+ JOIN pg_class ft ON (ft.oid = c.confrelid)
+ JOIN pg_namespace nft ON (nft.oid = ft.relnamespace)
+ LEFT JOIN pg_description ds ON (ds.objoid = c.oid)
+ JOIN pg_namespace n ON (n.oid = t.relnamespace)
+ WHERE c.contype = 'f'::\"char\"
+ ORDER BY t.relname, n.nspname, c.conname, c.oid
+ ) foo
+ ) fee) fum
+ WHERE fum.ltable='".strtolower($table)."'
+ ORDER BY fum.ftblname, fum.ltable, split_part(fum.lf, ')'::text, 1)
+ ";
+ $rs = $this->Execute($sql);
+
+ if (!$rs || $rs->EOF) return false;
+
+ $a = array();
+ while (!$rs->EOF) {
+ if ($upper) {
+ $a[strtoupper($rs->Fields('lookup_table'))][] = strtoupper(str_replace('"','',$rs->Fields('dep_field').'='.$rs->Fields('lookup_field')));
+ } else {
+ $a[$rs->Fields('lookup_table')][] = str_replace('"','',$rs->Fields('dep_field').'='.$rs->Fields('lookup_field'));
+ }
+ $rs->MoveNext();
+ }
+
+ return $a;
+
+ }
+
+ // from Edward Jaramilla, improved version - works on pg 7.4
+ function _old_MetaForeignKeys($table, $owner=false, $upper=false)
{
$sql = 'SELECT t.tgargs as args
FROM
return $a;
}
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
if (! $this->_bindInputArray) {
// We don't have native support for parameterized queries, so let's emulate it at the parent
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-version V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights
+version V5.06 16 Oct 2008 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights
reserved.
Released under both BSD license and Lesser GPL library license.
Whenever there is any discrepancy between the two licenses,
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim. All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim. 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.
{
if (!function_exists('sybase_connect')) return null;
+ if ($this->charSet) {
+ $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword, $this->charSet);
+ } else {
+ $this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword);
+ }
+
$this->_connectionID = sybase_connect($argHostname,$argUsername,$argPassword);
if ($this->_connectionID === false) return false;
if ($argDatabasename) return $this->SelectDB($argDatabasename);
{
if (!function_exists('sybase_connect')) return null;
- $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword);
+ if ($this->charSet) {
+ $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword, $this->charSet);
+ } else {
+ $this->_connectionID = sybase_pconnect($argHostname,$argUsername,$argPassword);
+ }
if ($this->_connectionID === false) return false;
if ($argDatabasename) return $this->SelectDB($argDatabasename);
return true;
}
// returns query ID if successful, otherwise false
- function _query($sql,$inputarr)
+ function _query($sql,$inputarr=false)
{
global $ADODB_COUNTRECS;
return @sybase_close($this->_connectionID);
}
- function UnixDate($v)
+ static function UnixDate($v)
{
return ADORecordSet_array_sybase::UnixDate($v);
}
- function UnixTimeStamp($v)
+ static function UnixTimeStamp($v)
{
return ADORecordSet_array_sybase::UnixTimeStamp($v);
}
$s .= "convert(char(3),$col,0)";
break;
case 'm':
- $s .= "replace(str(month($col),2),' ','0')";
+ $s .= "str_replace(str(month($col),2),' ','0')";
break;
case 'Q':
case 'q':
break;
case 'D':
case 'd':
- $s .= "replace(str(datepart(dd,$col),2),' ','0')";
+ $s .= "str_replace(str(datepart(dd,$col),2),' ','0')";
break;
case 'h':
$s .= "substring(convert(char(14),$col,0),13,2)";
break;
case 'H':
- $s .= "replace(str(datepart(hh,$col),2),' ','0')";
+ $s .= "str_replace(str(datepart(hh,$col),2),' ','0')";
break;
case 'i':
- $s .= "replace(str(datepart(mi,$col),2),' ','0')";
+ $s .= "str_replace(str(datepart(mi,$col),2),' ','0')";
break;
case 's':
- $s .= "replace(str(datepart(ss,$col),2),' ','0')";
+ $s .= "str_replace(str(datepart(ss,$col),2),' ','0')";
break;
case 'a':
case 'A':
}
// sybase/mssql uses a default date like Dec 30 2000 12:00AM
- function UnixDate($v)
+ static function UnixDate($v)
{
return ADORecordSet_array_sybase::UnixDate($v);
}
- function UnixTimeStamp($v)
+ static function UnixTimeStamp($v)
{
return ADORecordSet_array_sybase::UnixTimeStamp($v);
}
}
// sybase/mssql uses a default date like Dec 30 2000 12:00AM
- function UnixDate($v)
+ static function UnixDate($v)
{
global $ADODB_sybase_mths;
//Dec 30 2000 12:00AM
- if (!ereg( "([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})"
+ if (!preg_match( "/([A-Za-z]{3})[-/\. ]+([0-9]{1,2})[-/\. ]+([0-9]{4})/"
,$v, $rr)) return parent::UnixDate($v);
if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
return mktime(0,0,0,$themth,$rr[2],$rr[3]);
}
- function UnixTimeStamp($v)
+ static function UnixTimeStamp($v)
{
global $ADODB_sybase_mths;
//11.02.2001 Toni Tunkkari toni.tunkkari@finebyte.com
//Changed [0-9] to [0-9 ] in day conversion
- if (!ereg( "([A-Za-z]{3})[-/\. ]([0-9 ]{1,2})[-/\. ]([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})"
+ if (!preg_match( "/([A-Za-z]{3})[-/\. ]([0-9 ]{1,2})[-/\. ]([0-9]{4}) +([0-9]{1,2}):([0-9]{1,2}) *([apAP]{0,1})/"
,$v, $rr)) return parent::UnixTimeStamp($v);
if ($rr[3] <= TIMESTAMP_FIRST_YEAR) return 0;
<?php
/*
- V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
--- /dev/null
+<?php\r
+\r
+/* Farsi - by "Peyman Hooshmandi Raad" <phooshmand#gmail.com> */\r
+\r
+$ADODB_LANG_ARRAY = array (\r
+ 'LANG' => 'fa',\r
+ DB_ERROR => 'خطای ناشناخته',\r
+ DB_ERROR_ALREADY_EXISTS => 'وجود دارد',\r
+ DB_ERROR_CANNOT_CREATE => 'امکان create وجود ندارد',\r
+ DB_ERROR_CANNOT_DELETE => 'امکان حذف وجود ندارد',\r
+ DB_ERROR_CANNOT_DROP => 'امکان drop وجود ندارد',\r
+ DB_ERROR_CONSTRAINT => 'نقض شرط',\r
+ DB_ERROR_DIVZERO => 'تقسیم بر صفر',\r
+ DB_ERROR_INVALID => 'نامعتبر',\r
+ DB_ERROR_INVALID_DATE => 'زمان یا تاریخ نامعتبر',\r
+ DB_ERROR_INVALID_NUMBER => 'عدد نامعتبر',\r
+ DB_ERROR_MISMATCH => 'عدم مطابقت',\r
+ DB_ERROR_NODBSELECTED => 'بانک اطلاعاتی انتخاب نشده است',\r
+ DB_ERROR_NOSUCHFIELD => 'چنین ستونی وجود ندارد',\r
+ DB_ERROR_NOSUCHTABLE => 'چنین جدولی وجود ندارد',\r
+ DB_ERROR_NOT_CAPABLE => 'backend بانک اطلاعاتی قادر نیست',\r
+ DB_ERROR_NOT_FOUND => 'پیدا نشد',\r
+ DB_ERROR_NOT_LOCKED => 'قفل نشده',\r
+ DB_ERROR_SYNTAX => 'خطای دستوری',\r
+ DB_ERROR_UNSUPPORTED => 'پشتیبانی نمی شود',\r
+ DB_ERROR_VALUE_COUNT_ON_ROW => 'شمارش مقادیر روی ردیف',\r
+ DB_ERROR_INVALID_DSN => 'DSN نامعتبر',\r
+ DB_ERROR_CONNECT_FAILED => 'ارتباط برقرار نشد',\r
+ 0 => 'بدون خطا', // DB_OK\r
+ DB_ERROR_NEED_MORE_DATA => 'داده ناکافی است',\r
+ DB_ERROR_EXTENSION_NOT_FOUND=> 'extension پیدا نشد',\r
+ DB_ERROR_NOSUCHDB => 'چنین بانک اطلاعاتی وجود ندارد',\r
+ DB_ERROR_ACCESS_VIOLATION => 'حق دسترسی ناکافی'\r
+);\r
+?>
\ No newline at end of file
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
--- /dev/null
+<?php
+
+/*
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
+ Set tabs to 4 for best viewing.
+
+ Latest version is available at http://adodb.sourceforge.net
+
+ Library for basic performance monitoring and tuning
+
+*/
+
+// security - hide paths
+if (!defined('ADODB_DIR')) die();
+
+/*
+ MSSQL has moved most performance info to Performance Monitor
+*/
+class perf_mssqlnative extends adodb_perf{
+ var $sql1 = 'cast(sql1 as text)';
+ var $createTableSQL = "CREATE TABLE adodb_logsql (
+ created datetime NOT NULL,
+ sql0 varchar(250) NOT NULL,
+ sql1 varchar(4000) NOT NULL,
+ params varchar(3000) NOT NULL,
+ tracer varchar(500) NOT NULL,
+ timer decimal(16,6) NOT NULL
+ )";
+
+ var $settings = array(
+ 'Ratios',
+ 'data cache hit ratio' => array('RATIO',
+ "select round((a.cntr_value*100.0)/b.cntr_value,2) from master.dbo.sysperfinfo a, master.dbo.sysperfinfo b where a.counter_name = 'Buffer cache hit ratio' and b.counter_name='Buffer cache hit ratio base'",
+ '=WarnCacheRatio'),
+ 'prepared sql hit ratio' => array('RATIO',
+ array('dbcc cachestats','Prepared',1,100),
+ ''),
+ 'adhoc sql hit ratio' => array('RATIO',
+ array('dbcc cachestats','Adhoc',1,100),
+ ''),
+ 'IO',
+ 'data reads' => array('IO',
+ "select cntr_value from master.dbo.sysperfinfo where counter_name = 'Page reads/sec'"),
+ 'data writes' => array('IO',
+ "select cntr_value from master.dbo.sysperfinfo where counter_name = 'Page writes/sec'"),
+
+ 'Data Cache',
+ 'data cache size' => array('DATAC',
+ "select cntr_value*8192 from master.dbo.sysperfinfo where counter_name = 'Total Pages' and object_name='SQLServer:Buffer Manager'",
+ '' ),
+ 'data cache blocksize' => array('DATAC',
+ "select 8192",'page size'),
+ 'Connections',
+ 'current connections' => array('SESS',
+ '=sp_who',
+ ''),
+ 'max connections' => array('SESS',
+ "SELECT @@MAX_CONNECTIONS",
+ ''),
+
+ false
+ );
+
+
+ function perf_mssqlnative(&$conn)
+ {
+ if ($conn->dataProvider == 'odbc') {
+ $this->sql1 = 'sql1';
+ //$this->explain = false;
+ }
+ $this->conn =& $conn;
+ }
+
+ function Explain($sql,$partial=false)
+ {
+
+ $save = $this->conn->LogSQL(false);
+ if ($partial) {
+ $sqlq = $this->conn->qstr($sql.'%');
+ $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
+ if ($arr) {
+ foreach($arr as $row) {
+ $sql = reset($row);
+ if (crc32($sql) == $partial) break;
+ }
+ }
+ }
+
+ $s = '<p><b>Explain</b>: '.htmlspecialchars($sql).'</p>';
+ $this->conn->Execute("SET SHOWPLAN_ALL ON;");
+ $sql = str_replace('?',"''",$sql);
+ global $ADODB_FETCH_MODE;
+
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ $rs =& $this->conn->Execute($sql);
+ //adodb_printr($rs);
+ $ADODB_FETCH_MODE = $save;
+ if ($rs) {
+ $rs->MoveNext();
+ $s .= '<table bgcolor=white border=0 cellpadding="1" callspacing=0><tr><td nowrap align=center> Rows<td nowrap align=center> IO<td nowrap align=center> CPU<td align=left> Plan</tr>';
+ while (!$rs->EOF) {
+ $s .= '<tr><td>'.round($rs->fields[8],1).'<td>'.round($rs->fields[9],3).'<td align=right>'.round($rs->fields[10],3).'<td nowrap><pre>'.htmlspecialchars($rs->fields[0])."</td></pre></tr>\n"; ## NOTE CORRUPT </td></pre> tag is intentional!!!!
+ $rs->MoveNext();
+ }
+ $s .= '</table>';
+
+ $rs->NextRecordSet();
+ }
+
+ $this->conn->Execute("SET SHOWPLAN_ALL OFF;");
+ $this->conn->LogSQL($save);
+ $s .= $this->Tracer($sql);
+ return $s;
+ }
+
+ function Tables()
+ {
+ global $ADODB_FETCH_MODE;
+
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ //$this->conn->debug=1;
+ $s = '<table border=1 bgcolor=white><tr><td><b>tablename</b></td><td><b>size_in_k</b></td><td><b>index size</b></td><td><b>reserved size</b></td></tr>';
+ $rs1 = $this->conn->Execute("select distinct name from sysobjects where xtype='U'");
+ if ($rs1) {
+ while (!$rs1->EOF) {
+ $tab = $rs1->fields[0];
+ $tabq = $this->conn->qstr($tab);
+ $rs2 = $this->conn->Execute("sp_spaceused $tabq");
+ if ($rs2) {
+ $s .= '<tr><td>'.$tab.'</td><td align=right>'.$rs2->fields[3].'</td><td align=right>'.$rs2->fields[4].'</td><td align=right>'.$rs2->fields[2].'</td></tr>';
+ $rs2->Close();
+ }
+ $rs1->MoveNext();
+ }
+ $rs1->Close();
+ }
+ $ADODB_FETCH_MODE = $save;
+ return $s.'</table>';
+ }
+
+ function sp_who()
+ {
+ $arr = $this->conn->GetArray('sp_who');
+ return sizeof($arr);
+ }
+
+ function HealthCheck($cli=false)
+ {
+
+ $this->conn->Execute('dbcc traceon(3604)');
+ $html = adodb_perf::HealthCheck($cli);
+ $this->conn->Execute('dbcc traceoff(3604)');
+ return $html;
+ }
+
+
+}
+
+?>
\ No newline at end of file
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
if (!defined('ADODB_DIR')) die();
class perf_mysql extends adodb_perf{
-
- var $tablesSQL = 'show table status';
-
- var $createTableSQL = "CREATE TABLE adodb_logsql (
- created datetime NOT NULL,
- sql0 varchar(250) NOT NULL,
- sql1 text NOT NULL,
- params text NOT NULL,
- tracer text NOT NULL,
- timer decimal(16,6) NOT NULL
- )";
-
- var $settings = array(
- 'Ratios',
- 'MyISAM cache hit ratio' => array('RATIO',
- '=GetKeyHitRatio',
- '=WarnCacheRatio'),
- 'InnoDB cache hit ratio' => array('RATIO',
- '=GetInnoDBHitRatio',
- '=WarnCacheRatio'),
- 'data cache hit ratio' => array('HIDE', # only if called
- '=FindDBHitRatio',
- '=WarnCacheRatio'),
- 'sql cache hit ratio' => array('RATIO',
- '=GetQHitRatio',
- ''),
- 'IO',
- 'data reads' => array('IO',
- '=GetReads',
- 'Number of selects (Key_reads is not accurate)'),
- 'data writes' => array('IO',
- '=GetWrites',
- 'Number of inserts/updates/deletes * coef (Key_writes is not accurate)'),
-
- 'Data Cache',
- 'MyISAM data cache size' => array('DATAC',
- array("show variables", 'key_buffer_size'),
- '' ),
- 'BDB data cache size' => array('DATAC',
- array("show variables", 'bdb_cache_size'),
- '' ),
- 'InnoDB data cache size' => array('DATAC',
- array("show variables", 'innodb_buffer_pool_size'),
- '' ),
- 'Memory Usage',
- 'read buffer size' => array('CACHE',
- array("show variables", 'read_buffer_size'),
- '(per session)'),
- 'sort buffer size' => array('CACHE',
- array("show variables", 'sort_buffer_size'),
- 'Size of sort buffer (per session)' ),
- 'table cache' => array('CACHE',
- array("show variables", 'table_cache'),
- 'Number of tables to keep open'),
- 'Connections',
- 'current connections' => array('SESS',
- array('show status','Threads_connected'),
- ''),
- 'max connections' => array( 'SESS',
- array("show variables",'max_connections'),
- ''),
-
- false
- );
-
- function perf_mysql(&$conn)
- {
- $this->conn = $conn;
- }
-
- function Explain($sql,$partial=false)
- {
-
- if (strtoupper(substr(trim($sql),0,6)) !== 'SELECT') return '<p>Unable to EXPLAIN non-select statement</p>';
- $save = $this->conn->LogSQL(false);
- if ($partial) {
- $sqlq = $this->conn->qstr($sql.'%');
- $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
- if ($arr) {
- foreach($arr as $row) {
- $sql = reset($row);
- if (crc32($sql) == $partial) break;
- }
- }
- }
- $sql = str_replace('?',"''",$sql);
-
- if ($partial) {
- $sqlq = $this->conn->qstr($sql.'%');
- $sql = $this->conn->GetOne("select sql1 from adodb_logsql where sql1 like $sqlq");
- }
-
- $s = '<p><b>Explain</b>: '.htmlspecialchars($sql).'</p>';
- $rs = $this->conn->Execute('EXPLAIN '.$sql);
- $s .= rs2html($rs,false,false,false,false);
- $this->conn->LogSQL($save);
- $s .= $this->Tracer($sql);
- return $s;
- }
-
- function Tables()
- {
- if (!$this->tablesSQL) return false;
-
- $rs = $this->conn->Execute($this->tablesSQL);
- if (!$rs) return false;
-
- $html = rs2html($rs,false,false,false,false);
- return $html;
- }
-
- function GetReads()
- {
- global $ADODB_FETCH_MODE;
- $save = $ADODB_FETCH_MODE;
- $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
- if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-
- $rs = $this->conn->Execute('show status');
-
- if (isset($savem)) $this->conn->SetFetchMode($savem);
- $ADODB_FETCH_MODE = $save;
-
- if (!$rs) return 0;
- $val = 0;
- while (!$rs->EOF) {
- switch($rs->fields[0]) {
- case 'Com_select':
- $val = $rs->fields[1];
- $rs->Close();
- return $val;
- }
- $rs->MoveNext();
- }
-
- $rs->Close();
-
- return $val;
- }
-
- function GetWrites()
- {
- global $ADODB_FETCH_MODE;
- $save = $ADODB_FETCH_MODE;
- $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
- if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-
- $rs = $this->conn->Execute('show status');
-
- if (isset($savem)) $this->conn->SetFetchMode($savem);
- $ADODB_FETCH_MODE = $save;
-
- if (!$rs) return 0;
- $val = 0.0;
- while (!$rs->EOF) {
- switch($rs->fields[0]) {
- case 'Com_insert':
- $val += $rs->fields[1]; break;
- case 'Com_delete':
- $val += $rs->fields[1]; break;
- case 'Com_update':
- $val += $rs->fields[1]/2;
- $rs->Close();
- return $val;
- }
- $rs->MoveNext();
- }
-
- $rs->Close();
-
- return $val;
- }
-
- function FindDBHitRatio()
- {
- // first find out type of table
- //$this->conn->debug=1;
-
- global $ADODB_FETCH_MODE;
- $save = $ADODB_FETCH_MODE;
- $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
- if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-
- $rs = $this->conn->Execute('show table status');
-
- if (isset($savem)) $this->conn->SetFetchMode($savem);
- $ADODB_FETCH_MODE = $save;
-
- if (!$rs) return '';
- $type = strtoupper($rs->fields[1]);
- $rs->Close();
- switch($type){
- case 'MYISAM':
- case 'ISAM':
- return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)';
- case 'INNODB':
- return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)';
- default:
- return $type.' not supported';
- }
-
- }
-
- function GetQHitRatio()
- {
- //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached
- $hits = $this->_DBParameter(array("show status","Qcache_hits"));
- $total = $this->_DBParameter(array("show status","Qcache_inserts"));
- $total += $this->_DBParameter(array("show status","Qcache_not_cached"));
-
- $total += $hits;
- if ($total) return round(($hits*100)/$total,2);
- return 0;
- }
-
- /*
- Use session variable to store Hit percentage, because MySQL
- does not remember last value of SHOW INNODB STATUS hit ratio
-
- # 1st query to SHOW INNODB STATUS
- 0.00 reads/s, 0.00 creates/s, 0.00 writes/s
- Buffer pool hit rate 1000 / 1000
-
- # 2nd query to SHOW INNODB STATUS
- 0.00 reads/s, 0.00 creates/s, 0.00 writes/s
- No buffer pool activity since the last printout
- */
- function GetInnoDBHitRatio()
- {
- global $ADODB_FETCH_MODE;
-
- $save = $ADODB_FETCH_MODE;
- $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
- if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
-
- $rs = $this->conn->Execute('show innodb status');
-
- if (isset($savem)) $this->conn->SetFetchMode($savem);
- $ADODB_FETCH_MODE = $save;
-
- if (!$rs || $rs->EOF) return 0;
- $stat = $rs->fields[0];
- $rs->Close();
- $at = strpos($stat,'Buffer pool hit rate');
- $stat = substr($stat,$at,200);
- if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) {
- $val = 100*$arr[1]/$arr[2];
- $_SESSION['INNODB_HIT_PCT'] = $val;
- return round($val,2);
- } else {
- if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT'];
- return 0;
- }
- }
-
- function GetKeyHitRatio()
- {
- $hits = $this->_DBParameter(array("show status","Key_read_requests"));
- $reqs = $this->_DBParameter(array("show status","Key_reads"));
- if ($reqs == 0) return 0;
-
- return round(($hits/($reqs+$hits))*100,2);
- }
-
+
+ var $tablesSQL = 'show table status';
+
+ var $createTableSQL = "CREATE TABLE adodb_logsql (
+ created datetime NOT NULL,
+ sql0 varchar(250) NOT NULL,
+ sql1 text NOT NULL,
+ params text NOT NULL,
+ tracer text NOT NULL,
+ timer decimal(16,6) NOT NULL
+ )";
+
+ var $settings = array(
+ 'Ratios',
+ 'MyISAM cache hit ratio' => array('RATIO',
+ '=GetKeyHitRatio',
+ '=WarnCacheRatio'),
+ 'InnoDB cache hit ratio' => array('RATIO',
+ '=GetInnoDBHitRatio',
+ '=WarnCacheRatio'),
+ 'data cache hit ratio' => array('HIDE', # only if called
+ '=FindDBHitRatio',
+ '=WarnCacheRatio'),
+ 'sql cache hit ratio' => array('RATIO',
+ '=GetQHitRatio',
+ ''),
+ 'IO',
+ 'data reads' => array('IO',
+ '=GetReads',
+ 'Number of selects (Key_reads is not accurate)'),
+ 'data writes' => array('IO',
+ '=GetWrites',
+ 'Number of inserts/updates/deletes * coef (Key_writes is not accurate)'),
+
+ 'Data Cache',
+ 'MyISAM data cache size' => array('DATAC',
+ array("show variables", 'key_buffer_size'),
+ '' ),
+ 'BDB data cache size' => array('DATAC',
+ array("show variables", 'bdb_cache_size'),
+ '' ),
+ 'InnoDB data cache size' => array('DATAC',
+ array("show variables", 'innodb_buffer_pool_size'),
+ '' ),
+ 'Memory Usage',
+ 'read buffer size' => array('CACHE',
+ array("show variables", 'read_buffer_size'),
+ '(per session)'),
+ 'sort buffer size' => array('CACHE',
+ array("show variables", 'sort_buffer_size'),
+ 'Size of sort buffer (per session)' ),
+ 'table cache' => array('CACHE',
+ array("show variables", 'table_cache'),
+ 'Number of tables to keep open'),
+ 'Connections',
+ 'current connections' => array('SESS',
+ array('show status','Threads_connected'),
+ ''),
+ 'max connections' => array( 'SESS',
+ array("show variables",'max_connections'),
+ ''),
+
+ false
+ );
+
+ function perf_mysql(&$conn)
+ {
+ $this->conn = $conn;
+ }
+
+ function Explain($sql,$partial=false)
+ {
+
+ if (strtoupper(substr(trim($sql),0,6)) !== 'SELECT') return '<p>Unable to EXPLAIN non-select statement</p>';
+ $save = $this->conn->LogSQL(false);
+ if ($partial) {
+ $sqlq = $this->conn->qstr($sql.'%');
+ $arr = $this->conn->GetArray("select distinct sql1 from adodb_logsql where sql1 like $sqlq");
+ if ($arr) {
+ foreach($arr as $row) {
+ $sql = reset($row);
+ if (crc32($sql) == $partial) break;
+ }
+ }
+ }
+ $sql = str_replace('?',"''",$sql);
+
+ if ($partial) {
+ $sqlq = $this->conn->qstr($sql.'%');
+ $sql = $this->conn->GetOne("select sql1 from adodb_logsql where sql1 like $sqlq");
+ }
+
+ $s = '<p><b>Explain</b>: '.htmlspecialchars($sql).'</p>';
+ $rs = $this->conn->Execute('EXPLAIN '.$sql);
+ $s .= rs2html($rs,false,false,false,false);
+ $this->conn->LogSQL($save);
+ $s .= $this->Tracer($sql);
+ return $s;
+ }
+
+ function Tables()
+ {
+ if (!$this->tablesSQL) return false;
+
+ $rs = $this->conn->Execute($this->tablesSQL);
+ if (!$rs) return false;
+
+ $html = rs2html($rs,false,false,false,false);
+ return $html;
+ }
+
+ function GetReads()
+ {
+ global $ADODB_FETCH_MODE;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+
+ $rs = $this->conn->Execute('show status');
+
+ if (isset($savem)) $this->conn->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+
+ if (!$rs) return 0;
+ $val = 0;
+ while (!$rs->EOF) {
+ switch($rs->fields[0]) {
+ case 'Com_select':
+ $val = $rs->fields[1];
+ $rs->Close();
+ return $val;
+ }
+ $rs->MoveNext();
+ }
+
+ $rs->Close();
+
+ return $val;
+ }
+
+ function GetWrites()
+ {
+ global $ADODB_FETCH_MODE;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+
+ $rs = $this->conn->Execute('show status');
+
+ if (isset($savem)) $this->conn->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+
+ if (!$rs) return 0;
+ $val = 0.0;
+ while (!$rs->EOF) {
+ switch($rs->fields[0]) {
+ case 'Com_insert':
+ $val += $rs->fields[1]; break;
+ case 'Com_delete':
+ $val += $rs->fields[1]; break;
+ case 'Com_update':
+ $val += $rs->fields[1]/2;
+ $rs->Close();
+ return $val;
+ }
+ $rs->MoveNext();
+ }
+
+ $rs->Close();
+
+ return $val;
+ }
+
+ function FindDBHitRatio()
+ {
+ // first find out type of table
+ //$this->conn->debug=1;
+
+ global $ADODB_FETCH_MODE;
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+
+ $rs = $this->conn->Execute('show table status');
+
+ if (isset($savem)) $this->conn->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+
+ if (!$rs) return '';
+ $type = strtoupper($rs->fields[1]);
+ $rs->Close();
+ switch($type){
+ case 'MYISAM':
+ case 'ISAM':
+ return $this->DBParameter('MyISAM cache hit ratio').' (MyISAM)';
+ case 'INNODB':
+ return $this->DBParameter('InnoDB cache hit ratio').' (InnoDB)';
+ default:
+ return $type.' not supported';
+ }
+
+ }
+
+ function GetQHitRatio()
+ {
+ //Total number of queries = Qcache_inserts + Qcache_hits + Qcache_not_cached
+ $hits = $this->_DBParameter(array("show status","Qcache_hits"));
+ $total = $this->_DBParameter(array("show status","Qcache_inserts"));
+ $total += $this->_DBParameter(array("show status","Qcache_not_cached"));
+
+ $total += $hits;
+ if ($total) return round(($hits*100)/$total,2);
+ return 0;
+ }
+
+ /*
+ Use session variable to store Hit percentage, because MySQL
+ does not remember last value of SHOW INNODB STATUS hit ratio
+
+ # 1st query to SHOW INNODB STATUS
+ 0.00 reads/s, 0.00 creates/s, 0.00 writes/s
+ Buffer pool hit rate 1000 / 1000
+
+ # 2nd query to SHOW INNODB STATUS
+ 0.00 reads/s, 0.00 creates/s, 0.00 writes/s
+ No buffer pool activity since the last printout
+ */
+ function GetInnoDBHitRatio()
+ {
+ global $ADODB_FETCH_MODE;
+
+ $save = $ADODB_FETCH_MODE;
+ $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
+ if ($this->conn->fetchMode !== false) $savem = $this->conn->SetFetchMode(false);
+
+ $rs = $this->conn->Execute('show innodb status');
+
+ if (isset($savem)) $this->conn->SetFetchMode($savem);
+ $ADODB_FETCH_MODE = $save;
+
+ if (!$rs || $rs->EOF) return 0;
+ $stat = $rs->fields[0];
+ $rs->Close();
+ $at = strpos($stat,'Buffer pool hit rate');
+ $stat = substr($stat,$at,200);
+ if (preg_match('!Buffer pool hit rate\s*([0-9]*) / ([0-9]*)!',$stat,$arr)) {
+ $val = 100*$arr[1]/$arr[2];
+ $_SESSION['INNODB_HIT_PCT'] = $val;
+ return round($val,2);
+ } else {
+ if (isset($_SESSION['INNODB_HIT_PCT'])) return $_SESSION['INNODB_HIT_PCT'];
+ return 0;
+ }
+ return 0;
+ }
+
+ function GetKeyHitRatio()
+ {
+ $hits = $this->_DBParameter(array("show status","Key_read_requests"));
+ $reqs = $this->_DBParameter(array("show status","Key_reads"));
+ if ($reqs == 0) return 0;
+
+ return round(($hits/($reqs+$hits))*100,2);
+ }
+
// start hack
var $optimizeTableLow = 'CHECK TABLE %s FAST QUICK';
var $optimizeTableHigh = 'OPTIMIZE TABLE %s';
}
// end hack
}
-?>
+?>
\ No newline at end of file
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
if (!defined('ADODB_DIR')) die();
class perf_oci8 extends ADODB_perf{
+
+ var $noShowIxora = 15; // if the sql for suspicious sql is taking too long, then disable ixora
var $tablesSQL = "select segment_name as \"tablename\", sum(bytes)/1024 as \"size_in_k\",tablespace_name as \"tablespace\",count(*) \"extents\" from sys.user_extents
group by segment_name,tablespace_name";
var $version;
+
var $createTableSQL = "CREATE TABLE adodb_logsql (
created date NOT NULL,
sql0 varchar(250) NOT NULL,
"select value from v\$sysstat where name='physical writes'"),
'Data Cache',
+
'data cache buffers' => array( 'DATAC',
"select a.value/b.value from v\$parameter a, v\$parameter b
where a.name = 'db_cache_size' and b.name= 'db_block_size'",
'data cache blocksize' => array('DATAC',
"select value from v\$parameter where name='db_block_size'",
'' ),
+
'Memory Pools',
- 'data cache size' => array('DATAC',
+ 'SGA Max Size' => array( 'DATAC',
+ "select value from v\$parameter where name = 'sga_max_size'",
+ 'The sga_max_size is the maximum value to which sga_target can be set.' ),
+ 'SGA target' => array( 'DATAC',
+ "select value from v\$parameter where name = 'sga_target'",
+ 'If sga_target is defined then data cache, shared, java and large pool size can be 0. This is because all these pools are consolidated into one sga_target.' ),
+ 'data cache size' => array('DATAC',
"select value from v\$parameter where name = 'db_cache_size'",
'db_cache_size' ),
'shared pool size' => array('DATAC',
if (isset($_GET['sql'])) return $this->_SuspiciousSQL($numsql);
$s = '';
+ $timer = time();
$s .= $this->_SuspiciousSQL($numsql);
+ $timer = time() - $timer;
+
+ if ($timer > $this->noShowIxora) return $s;
$s .= '<p>';
$save = $ADODB_CACHE_MODE;
}
$s = '';
+ $timer = time();
$s .= $this->_ExpensiveSQL($numsql);
+ $timer = time() - $timer;
+ if ($timer > $this->noShowIxora) return $s;
+
$s .= '<p>';
$save = $ADODB_CACHE_MODE;
$ADODB_CACHE_MODE = ADODB_FETCH_NUM;
LOOP
cnt := cnt + 1;
DELETE FROM $perf_table WHERE ROWID=rec.rr;
- IF cnt = 10000 THEN
+ IF cnt = 1000 THEN
COMMIT;
cnt := 0;
END IF;
END LOOP;
+ commit;
END;";
$ok = $this->conn->Execute($sql);
<?php
/*
-V5.04a 25 Mar 2008 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+V5.08 6 Apr 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). 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. See License.txt.
<?php
/**
- * @version V4.93 10 Oct 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V4.93 10 Oct 2006 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
-Description of ADODB V5.04a library import into Moodle
+Description of ADODB V5.08 library import into Moodle
Removed:
* contrib/
* index.html - prevent directory browsing on misconfigured servers
* readme_moodle.txt - this file ;-)
-Our changes:
+Our changes: /// Look for "moodle" in adodb code
* adodb-lib.inc.php - added support for "F" and "L" types in _adodb_column_sql()
* adodb-lib.inc.php - modify some debug output to be correct XHTML. MDL-12378.
Reported to ADOdb at: http://phplens.com/lens/lensforum/msgs.php?id=17133
Once fixed by adodb guys, we'll return to their official distro.
- * drivers/adodb-mysqli.inc.php - fixed problem with driver not detecting enums
- in the MetaColumns() function. MDL-14215.
- Reported to ADOdb at: http://phplens.com/lens/lensforum/msgs.php?id=17383
- Once fixed by adodb guys, we'll return to their official distro.
- * drivers/adodb-mssql.ic.php - fixed problem with insert statements using placeholders
- not able to return the Insert_ID() at all. MDL-14886.
- Reported to ADOdb at: http://phplens.com/lens/lensforum/msgs.php?id=17068
- Once fixed by adodb guys, we'll return to their official distro.
-
+
skodak, iarenaza, moodler, stronk7
$Id$
<?php
/**
- * @version V4.93 10 Oct 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V4.93 10 Oct 2006 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
<?php
/**
- * @version V4.93 10 Oct 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ * @version V4.93 10 Oct 2006 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
if ($addtitles) {
$fieldTypes = $rs->FieldTypesArray();
reset($fieldTypes);
+ $i = 0;
while(list(,$o) = each($fieldTypes)) {
- if (!$o) $v = '';
- else $v = $o->name;
+
+ $v = ($o) ? $o->name : 'Field'.($i++);
if ($escquote) $v = str_replace($quote,$escquotequote,$v);
$v = strip_tags(str_replace("\n", $replaceNewLine, str_replace("\r\n",$replaceNewLine,str_replace($sep,$sepreplace,$v))));
$elements[] = $v;
<?php
/*
- V4.93 10 Oct 2006 (c) 2000-2008 John Lim (jlim#natsoft.com.my). All rights reserved.
+ V4.93 10 Oct 2006 (c) 2000-2009 John Lim (jlim#natsoft.com). 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.
Some pretty-printing by Chris Oxenreider <oxenreid@state.net>
*/
-
+
// specific code for tohtml
GLOBAL $gSQLMaxRows,$gSQLBlockRows,$ADODB_ROUND;
$type = $typearr[$i];
switch($type) {
case 'D':
- if (empty($v)) $s .= "<TD> </TD>\n";
- else if (!strpos($v,':')) {
- $s .= " <TD>".$rs->UserDate($v,"D d, M Y") ." </TD>\n";
+ if (strpos($v,':') !== false);
+ else {
+ if (empty($v)) {
+ $s .= "<TD> </TD>\n";
+ } else {
+ $s .= " <TD>".$rs->UserDate($v,"D d, M Y") ."</TD>\n";
+ }
+ break;
}
- break;
case 'T':
if (empty($v)) $s .= "<TD> </TD>\n";
- else $s .= " <TD>".$rs->UserTimeStamp($v,"D d, M Y, h:i:s") ." </TD>\n";
+ else $s .= " <TD>".$rs->UserTimeStamp($v,"D d, M Y, H:i:s") ."</TD>\n";
break;
case 'N':
else
$v = round($v,$ADODB_ROUND);
case 'I':
- $s .= " <TD align=right>".stripslashes((trim($v))) ." </TD>\n";
+ $vv = stripslashes((trim($v)));
+ if (strlen($vv) == 0) $vv .= ' ';
+ $s .= " <TD align=right>".$vv ."</TD>\n";
break;
/*